diff --git a/.bazelrc b/.bazelrc index 0a66fb7107..45776ef7ef 100644 --- a/.bazelrc +++ b/.bazelrc @@ -10,9 +10,16 @@ common:build --define absl=1 common:test --test_env=GTEST_INSTALL_FAILURE_SIGNAL_HANDLER=1 ## -## Custom toolchain. +## Custom toolchain (bazel-contrib/toolchains_llvm). ## -build --crosstool_top=@llvm_toolchain_12_0_0//:toolchain --copt=-D_LIBCPP_ENABLE_NODISCARD +# Not needed after https://github.com/bazelbuild/bazel/issues/7260 is closed +build --incompatible_enable_cc_toolchain_resolution +# For macOS only, needed for Bazel versions before 7. +# Without this, one can use `--linkopt='-undefined dynamic_lookup'`. +# This feature is intentionally not supported on macOS. +build --features=-supports_dynamic_linker +# Not needed after https://github.com/grailbio/bazel-toolchain/pull/229. +build --features=-libtool ## ## Common build options across all build configurations @@ -25,11 +32,6 @@ build --crosstool_top=@llvm_toolchain_12_0_0//:toolchain --copt=-D_LIBCPP_ENABLE # this flag here once flipped in Bazel again. build --incompatible_strict_action_env -# Enforce that we don't rely on `@bazel_tools//platforms` and instead use the -# `@platforms` repository for constraint resolution. See -# https://github.com/bazelbuild/bazel/issues/8622 for more information. -build --incompatible_use_platforms_repo_for_constraints - # C / C++ Options # Don't depend on system compiler build --cxxopt=-std=c++17 --host_cxxopt=-std=c++17 @@ -40,6 +42,8 @@ build --copt=-fstack-protector build --copt=-Werror --copt=-Wimplicit-fallthrough --copt=-Wmissing-field-initializers +build --copt=-D_LIBCPP_ENABLE_NODISCARD + build --host_copt=-O1 build --host_copt=-DFORCE_DEBUG build --host_cxxopt=-Wno-unused-variable --host_cxxopt=-Wno-overloaded-virtual --host_cxxopt=-Wno-unused-const-variable # ragel violates those and we want a silent build @@ -49,8 +53,6 @@ build --host_cxxopt=-Wno-pass-failed # host build doesn't have enough optimizati # :1:9: and :355:9: so sadly we turn them all off build --copt=-Wno-macro-redefined -# Clang 12.0.0 has warnings when compiling LLVM 12.0.0 on C++20, sadly. -# TODO(jez) When we upgrade to LLVM 13.0.0+ can we delete this? # TODO(jez) We will need these to build C++20, but emscripten doesn't even know about these options yet. # Leaving them commented out so we can uncomment them after upgrading emscripten. # build --host_cxxopt=-Wno-deprecated-anon-enum-enum-conversion @@ -59,13 +61,16 @@ build --copt=-Wno-macro-redefined # build --cxxopt=-Wno-deprecated-enum-enum-conversion # build --cxxopt=-Wno-ambiguous-reversed-operator +# TODO(jez) Something between Clang 12 and Clang 15 seems to have introduced a +# warning saying _never_ use unqualified `std` calls... +build --cxxopt=-Wno-unqualified-std-cast-call --host_cxxopt=-Wno-unqualified-std-cast-call + ## ## debug configuration ## build:dbg --copt=-O0 build:dbg --compilation_mode=dbg build:dbg --config=debugsymbols -build:dbg --config=rubydbg build:rubydbg --copt=-DRUBY_DEBUG --copt=-DVM_CHECK_MODE=1 --copt=-DTRANSIENT_HEAP_CHECK_MODE --copt=-DRGENGC_CHECK_MODE --copt=-DENC_DEBUG @@ -84,7 +89,6 @@ build:dbg-linux --config=dbg --platforms=@//tools/platforms:linux_x86_64 ## # release version: optimized, with debug symbols and version information build:release-common --define release=true -build:release-common --define mimalloc=true build:release-common --compilation_mode=opt build:release-common --config=backtracesymbols build:release-common --config=static-libs @@ -107,6 +111,18 @@ build:untyped-blame --copt=-DTRACK_UNTYPED_BLAME_MODE # harden: mark relocation sections read-only build:release-linux --linkopt=-Wl,-z,relro,-z,now build:release-linux --config=lto-linux --config=release-common +# Separate config for aarch64, so x86_64 can be differently optimized +build:release-linux-aarch64 --linkopt=-Wl,-z,relro,-z,now +build:release-linux-aarch64 --config=lto-linux --config=release-common + +# It would be nice to move this back to release-common, but there is a build +# failure when using clang 15 on macOS to build GNU make via rules_foreign_cc: +# +# We only use rules_foreign_cc for mimalloc and we only use mimalloc in release +# builds, so I'm moving this here until we can figure out a better workaround, +# or a fix lands upstream. +build:release-linux --define mimalloc=true +build:release-linux-aarch64 --define mimalloc=true # This is to turn on vector instructions where available. # We used to do this unconditionally, but Rosetta 2 doesn't translate all vector instructions well. @@ -118,6 +134,7 @@ build:release-linux --config=lto-linux --config=release-common # however some AWS instances in our fleet still run Sandy Bridge (Skylake predecessor), as of 2018. build:release-linux --copt=-march=sandybridge build:release-sanitized-linux --copt=-march=sandybridge +build:release-linux-aarch64 --copt=-march=armv8.1a build:release-mac --config=release-common --platforms=@//tools/platforms:darwin_x86_64 @@ -214,10 +231,13 @@ build:ubsan --linkopt=-fsanitize=undefined --copt=-fno-sanitize-recover=undefine build:ubsan --define unsanitized=false build:ubsan --copt=-DHAS_SANITIZER -# Bazel links C++ files with $CC, not $CXX, this breaks UBSan -build:sanitize-linux --linkopt=external/llvm_toolchain_12_0_0/lib/clang/12.0.0/lib/linux/libclang_rt.asan_cxx-x86_64.a -build:sanitize-linux --linkopt=external/llvm_toolchain_12_0_0/lib/clang/12.0.0/lib/linux/libclang_rt.ubsan_standalone_cxx-x86_64.a -build:sanitize-linux --linkopt=external/llvm_toolchain_12_0_0/lib/clang/12.0.0/lib/linux/libclang_rt.ubsan_standalone-x86_64.a +# TODO(jez) It's not clear whether we still need these tricks to get sanitizers to work, +# because we're using the toolchain's lld linker now. +# We could consider replacing this with something more typical. +# Original motivation: Bazel links C++ files with $CC, not $CXX, this breaks UBSan +build:sanitize-linux --linkopt=../../external/llvm_toolchain_15_0_7_llvm/lib/clang/15.0.7/lib/x86_64-unknown-linux-gnu/libclang_rt.asan_cxx.a +build:sanitize-linux --linkopt=../../external/llvm_toolchain_15_0_7_llvm/lib/clang/15.0.7/lib/x86_64-unknown-linux-gnu/libclang_rt.ubsan_standalone_cxx.a +build:sanitize-linux --linkopt=../../external/llvm_toolchain_15_0_7_llvm/lib/clang/15.0.7/lib/x86_64-unknown-linux-gnu/libclang_rt.ubsan_standalone.a build:sanitize-linux --config=sanitize build:sanitize-mac --config=sanitize @@ -255,18 +275,13 @@ build:fuzz --copt=-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION ## ## Webasm config. Please use either of those depending on your platform ## -build:webasm-linux --crosstool_top=//tools/toolchain/webasm-linux --config=webasm -build:webasm-darwin --crosstool_top=//tools/toolchain/webasm-darwin --config=webasm - -# common webasm config -# Use --cpu as a differentiator. -build:webasm --cpu=webasm --spawn_strategy=local --genrule_strategy=local --copt=-Oz --linkopt=-Oz --copt=-DMDB_USE_ROBUST=0 +build:webasm --copt=-Oz --linkopt=-Oz --copt=-DMDB_USE_ROBUST=0 build:webasm --define release=true build:webasm --compilation_mode=opt -build:webasm --copt=-DNDEBUG --linkopt=-DNDEBUG # for some reason emscripten doesn't pass those when -O2\-Oz are specified -build:webasm --copt=--llvm-lto --copt=3 --linkopt=--llvm-lto --linkopt=3 -# Specify a "sane" C++ toolchain for the host platform. -build:webasm --host_crosstool_top=@llvm_toolchain_12_0_0//:toolchain +# https://emscripten.org/docs/porting/exceptions.html#webassembly-exception-handling-based-support +build:webasm --features=exceptions --cxxopt=-fwasm-exceptions --linkopt=-fwasm-exceptions +# Blocked on https://github.com/emscripten-core/emscripten/issues/9780 +build:webasm --copt=-fno-stack-protector ## ## Stripe's ci passes --config=ci, we need it to exist diff --git a/.bazelversion b/.bazelversion new file mode 100644 index 0000000000..f22d756da3 --- /dev/null +++ b/.bazelversion @@ -0,0 +1 @@ +6.5.0 diff --git a/.buildkite/test-rbi-gen.sh b/.buildkite/test-rbi-gen.sh new file mode 100755 index 0000000000..2dc9ba18fa --- /dev/null +++ b/.buildkite/test-rbi-gen.sh @@ -0,0 +1,61 @@ +#!/bin/bash + +set -euo pipefail + +unameOut="$(uname -s)" +case "${unameOut}" in + Linux*) platform="linux";; + Darwin*) platform="mac";; + *) exit 1 +esac + +export JOB_NAME=test-rbi-gen +source .buildkite/tools/setup-bazel.sh + +err=0 + +echo "+++ running tests" + +# `-c opt` is required, otherwise the tests are too slow +# forcedebug is really the ~only thing in `--config=dbg` we care about. +# must come after `-c opt` because `-c opt` will define NDEBUG on its own +test_args=( + "//test:end_to_end_rbi_test" + "//test:single_package_runner" + "-c" + "opt" + "--config=forcedebug" + "--spawn_strategy=local" +) + +./bazel test \ + --experimental_generate_json_trace_profile \ + --profile=_out_/profile.json \ + --test_summary=terse \ + --test_output=errors \ + "${test_args[@]}" || err=$? + +if [ "$err" -ne 0 ]; then + echo "--- annotating build result" + failing_tests="$(mktemp)" + + echo 'Run this command to run failing tests locally:' >> "$failing_tests" + echo >> "$failing_tests" + echo '```bash' >> "$failing_tests" + echo "./bazel test \\" >> "$failing_tests" + + # Take the lines that start with target labels. + # Lines look like "//foo FAILED in 10s" + { ./bazel test --test_summary=terse "${test_args[@]}" || true ; } | \ + grep '^//' | \ + sed -e 's/ .*/ \\/' | \ + sed -e 's/^/ /' >> "$failing_tests" + + # Put this last as an easy way to not have a `\` on the last line. + echo ' -c opt --config=forcedebug' >> "$failing_tests" + echo '```' >> "$failing_tests" + + buildkite-agent annotate --context "test-rbi-gen.sh" --style error --append < "$failing_tests" + + exit "$err" +fi diff --git a/.gitattributes b/.gitattributes index d8c910b4f7..c98c5f828a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,6 +1,8 @@ *.rbi linguist-language=Ruby +*.rbedited linguist-language=Ruby *.rb diff=ruby *.rbi diff=ruby +*.rbedited diff=ruby *.md diff=markdown test/**/*.out diff test/cli/windows-line-endings/windows-line-endings.rb eol=crlf diff --git a/.gitignore b/.gitignore index 2039344e1d..80ebcc68f0 100644 --- a/.gitignore +++ b/.gitignore @@ -42,14 +42,15 @@ gems/sorbet-static-and-runtime/Gemfile.lock # Used by tools/scripts/ll-view.sh /ll-view.out -compiler/IREmitter/Intrinsics/PayloadIntrinsics.c -compiler/IREmitter/Intrinsics/PayloadIntrinsics.formatted.c -compiler/IREmitter/Intrinsics/WrappedIntrinsics.h -compiler/IREmitter/Intrinsics/WrappedIntrinsics.formatted.h - -# clangd caches data in this folder. +# clangd caches data in these folders /.clangd +/.cache/clangd # for YARD documentation .yardoc/ doc/ + +# Sandbox folders +test/sandbox/*/*.log +test/sandbox/*/*.rb +test/sandbox/*/*.rbi diff --git a/.ruby-version b/.ruby-version index 1f7da99d4e..ef538c2810 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.7.7 +3.1.2 diff --git a/.sorbet-buildkite/build-emscripten.sh b/.sorbet-buildkite/build-emscripten.sh index 089ebee29c..4e2aeaa5e0 100755 --- a/.sorbet-buildkite/build-emscripten.sh +++ b/.sorbet-buildkite/build-emscripten.sh @@ -12,8 +12,9 @@ source .buildkite/tools/setup-bazel.sh PATH=$PATH:$(pwd) export PATH -./bazel build //emscripten:sorbet-wasm.tar --config=webasm-linux --strip=always +./bazel build //emscripten:sorbet-wasm.d --config=webasm rm -rf _out_ mkdir -p _out_/webasm -cp bazel-bin/emscripten/sorbet-wasm.tar _out_/webasm/sorbet-wasm.tar +cp bazel-bin/emscripten/sorbet-wasm.d/sorbet-wasm.js _out_/webasm/ +cp bazel-bin/emscripten/sorbet-wasm.d/sorbet-wasm.wasm _out_/webasm/ diff --git a/.sorbet-buildkite/build-sorbet-runtime.sh b/.sorbet-buildkite/build-sorbet-runtime.sh index cd4806ec2d..e6cad08927 100755 --- a/.sorbet-buildkite/build-sorbet-runtime.sh +++ b/.sorbet-buildkite/build-sorbet-runtime.sh @@ -7,7 +7,7 @@ pushd gems/sorbet-runtime echo "--- setup :ruby:" eval "$(rbenv init -)" -runtime_versions=(2.7.7 3.1.2) +runtime_versions=(2.7.7 3.1.2 3.3.0) for runtime_version in "${runtime_versions[@]}"; do rbenv install --skip-existing "$runtime_version" diff --git a/.sorbet-buildkite/build-sorbet-static-and-runtime.sh b/.sorbet-buildkite/build-sorbet-static-and-runtime.sh index 01f202bca7..d1387ca7bf 100755 --- a/.sorbet-buildkite/build-sorbet-static-and-runtime.sh +++ b/.sorbet-buildkite/build-sorbet-static-and-runtime.sh @@ -7,12 +7,9 @@ pushd gems/sorbet-static-and-runtime echo "--- setup :ruby:" eval "$(rbenv init -)" -runtime_versions=(2.7.2 3.1.2) - -for runtime_version in "${runtime_versions[@]}"; do - rbenv install --skip-existing "$runtime_version" - rbenv shell "$runtime_version" -done +# Uses the version in .ruby-version +rbenv rehash +ruby --version echo "--- build" git_commit_count=$(git rev-list --count HEAD) diff --git a/.sorbet-buildkite/build-static-release-java.sh b/.sorbet-buildkite/build-static-release-java.sh index 91a0f063ab..1029a84ecb 100755 --- a/.sorbet-buildkite/build-static-release-java.sh +++ b/.sorbet-buildkite/build-static-release-java.sh @@ -8,7 +8,7 @@ set -o errexit echo "--- Dowloading artifacts" rm -rf release rm -rf _out_ -buildkite-agent artifact download "_out_/**/*" . +buildkite-agent artifact download "_out_/**/*.gem" . # Based on the output of build-static-release.sh # _out_/gems/ should have the Linux & Mac sorbet-static gem @@ -20,9 +20,7 @@ release_version="$prefix.${git_commit_count}" rbenv install --skip-existing -# we pin to universal-darwin-18 but it shouldn't matter -# the specific version; we just want one mac binary -for platform in universal-darwin-18 x86_64-linux +for platform in universal-darwin x86_64-linux do gem unpack _out_/gems/sorbet-static-"${release_version}"-${platform}*.gem @@ -31,8 +29,8 @@ do mv sorbet-static-"${release_version}"-${platform}/libexec/sorbet gems/sorbet-static/libexec/linux.sorbet ;; - universal-darwin-18) - mv sorbet-static-"${release_version}"-${platform}*/libexec/sorbet gems/sorbet-static/libexec/mac.sorbet + universal-darwin) + mv sorbet-static-"${release_version}"-${platform}/libexec/sorbet gems/sorbet-static/libexec/mac.sorbet ;; esac @@ -48,4 +46,6 @@ gem build sorbet-static.gemspec popd +rm -rf _out_ +mkdir -p _out_/gems mv gems/sorbet-static/sorbet-static-"${release_version}"-java.gem _out_/gems diff --git a/.sorbet-buildkite/build-static-release.sh b/.sorbet-buildkite/build-static-release.sh index 133352d3a8..e7b9dd38d1 100755 --- a/.sorbet-buildkite/build-static-release.sh +++ b/.sorbet-buildkite/build-static-release.sh @@ -10,9 +10,12 @@ processor_name="$(uname -m)" platform="${kernel_name}-${processor_name}" case "$platform" in - linux-x86_64|linux-aarch64) + linux-x86_64) CONFIG_OPTS="--config=release-linux" ;; + linux-aarch64) + CONFIG_OPTS="--config=release-${platform}" + ;; darwin-x86_64|darwin-arm64) CONFIG_OPTS="--config=release-mac" command -v autoconf >/dev/null 2>&1 || brew install autoconf @@ -37,13 +40,9 @@ git_commit_count=$(git rev-list --count HEAD) release_version="0.5.${git_commit_count}" sed -i.bak "s/0\\.0\\.0/${release_version}/" sorbet-static.gemspec if [[ "darwin" == "$kernel_name" ]]; then - # Our binary should work on almost all OSes. The oldest v8 publishes is -14 - # so I'm going with that for now. - for i in {14..22}; do - sed -i.bak "s/Gem::Platform::CURRENT/'universal-darwin-$i'/" sorbet-static.gemspec - gem build sorbet-static.gemspec - mv sorbet-static.gemspec.bak sorbet-static.gemspec - done + sed -i.bak "s/Gem::Platform::CURRENT/'universal-darwin'/" sorbet-static.gemspec + gem build sorbet-static.gemspec + mv sorbet-static.gemspec.bak sorbet-static.gemspec else gem build sorbet-static.gemspec fi @@ -75,8 +74,7 @@ rbenv exec gem uninstall --all --executables --ignore-dependencies sorbet sorbet trap 'rbenv exec gem uninstall --all --executables --ignore-dependencies sorbet sorbet-static' EXIT if [[ "darwin" == "$kernel_name" ]]; then - gem_platform="$(ruby -e "(platform = Gem::Platform.local).cpu = 'universal'; puts(platform.to_s)")" - rbenv exec gem install ../../gems/sorbet-static/sorbet-static-*-"$gem_platform".gem + rbenv exec gem install ../../gems/sorbet-static/sorbet-static-*-"universal-darwin".gem else rbenv exec gem install ../../gems/sorbet-static/sorbet-static-*-"$processor_name"-linux.gem fi @@ -108,7 +106,7 @@ rm -rf _out_ mkdir -p _out_/gems mv gems/sorbet-static/sorbet-static-*.gem _out_/gems/ -if [[ "$kernel_name" == "linux" ]]; then +if [[ "$platform" == "linux-x86_64" ]]; then mv gems/sorbet/sorbet*.gem _out_/gems/ fi diff --git a/.sorbet-buildkite/publish-ruby-gems.sh b/.sorbet-buildkite/publish-ruby-gems.sh index 5499b39afe..edf4632950 100755 --- a/.sorbet-buildkite/publish-ruby-gems.sh +++ b/.sorbet-buildkite/publish-ruby-gems.sh @@ -26,6 +26,19 @@ source .buildkite/tools/with_backoff.sh rbenv install --skip-existing +echo "--- Patching rubygems/package.rb" +# There's a bug in rubygems/package.rb where if there's a GZip reader error, in +# the process of reporting a good error saying what failed, there's a +# NoMethodError. +# https://github.com/rubygems/rubygems/pull/7539 +package_rb="$HOME/.rbenv/versions/3.1.2/lib/ruby/3.1.0/rubygems/package.rb" +if [ -f "$package_rb" ]; then + echo "Using sed to patch rubygems/package.rb" + sed -i 's/@path = source.path/@path = source.is_a?(String) ? source : source.path/' "$package_rb" +else + echo "Could not find rubygems/package.rb, skipping" +fi + publish_sorbet_static_gem() { gem_archive=$1 platform=$2 @@ -69,7 +82,16 @@ publish_gem() { fi # This is last so the exit code is used as the status code for with_backoff + # TODO(jez) All this extra garbage is attempting to debug the root cause of https://github.com/rubygems/rubygems/pull/7539 + set +e gem push --verbose "$gem_archive" + exit_code="$?" + set -e + if [ "$exit_code" -ne 0 ]; then + mkdir -p _out_/corrupt + cp "$gem_archive" _out_/corrupt + fi + return "$exit_code" } with_backoff publish_gem "sorbet-runtime" diff --git a/.sorbet-buildkite/publish.sh b/.sorbet-buildkite/publish.sh index be379bf097..82c5a9926c 100755 --- a/.sorbet-buildkite/publish.sh +++ b/.sorbet-buildkite/publish.sh @@ -39,10 +39,9 @@ if [ "$dryrun" = "" ]; then echo "--- releasing sorbet.run" rm -rf sorbet.run - git clone git@github.com:sorbet/sorbet.run.git --single-branch --branch master - tar -xvf ./_out_/webasm/sorbet-wasm.tar ./sorbet-wasm.wasm ./sorbet-wasm.js - mv sorbet-wasm.wasm sorbet.run/docs - mv sorbet-wasm.js sorbet.run/docs + git clone git@github.com:sorbet/sorbet.run.git --single-branch --branch master --depth 1 + mv _out_/webasm/sorbet-wasm.wasm sorbet.run/docs + mv _out_/webasm/sorbet-wasm.js sorbet.run/docs pushd sorbet.run/docs git add sorbet-wasm.wasm sorbet-wasm.js dirty= @@ -106,8 +105,7 @@ mv release/gems/* release rmdir release/gems rm release/website/website.tar.bz2 rmdir release/website -rm release/webasm/sorbet-wasm.tar -rmdir release/webasm +rm -r release/webasm pushd release files=() @@ -147,7 +145,7 @@ elif [ "$dryrun" = "" ]; then # It can take 4+ minutes for the marketplace to "verify" our extension # before it shows up as published according to `vsce show`. cat "$vsce_publish_output" - if grep -qF 'Version number cannot be the same'; then + if grep -qF 'Version number cannot be the same' "$vsce_publish_output"; then echo "... $extension_release_version is already published" else exit 1 diff --git a/.sorbet-buildkite/test-compiler.sh b/.sorbet-buildkite/test-compiler.sh deleted file mode 100755 index eea288f241..0000000000 --- a/.sorbet-buildkite/test-compiler.sh +++ /dev/null @@ -1,82 +0,0 @@ -#!/bin/bash - -set -euo pipefail - -unameOut="$(uname -s)" -case "${unameOut}" in - Linux*) platform="linux";; - Darwin*) platform="mac";; - *) exit 1 -esac - -if [[ "linux" == "$platform" ]]; then - curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - - apt-get update - apt-get install -yy libncurses5-dev libncursesw5-dev xxd -elif [[ "mac" == "$platform" ]]; then - if ! [ -x "$(command -v wget)" ]; then - brew install wget - fi -fi - -export JOB_NAME=test -source .buildkite/tools/setup-bazel.sh - -err=0 - -# Build sorbet_ruby once with gcc, to ensure that we can build it without depending on the clang toolchain in the -# sandbox -echo "--- building ruby with gcc" -./bazel build @sorbet_ruby_2_7_for_compiler//:ruby --crosstool_top=@bazel_tools//tools/cpp:toolchain - -echo "+++ running tests" - -mkdir -p _out_ - -# `-c opt` is required, otherwise the tests are too slow -# forcedebug is really the ~only thing in `--config=dbg` we care about. -# must come after `-c opt` because `-c opt` will define NDEBUG on its own -test_args=( - "//test:compiler" - "//test/cli/compiler" - # These are two tests that depend on sorbet_ruby, and it's annoying to have - # to build sorbet_ruby on the static sanitized job (delays test start time) - "//test:end_to_end_rbi_test" - "//test:single_package_runner" - "-c" - "opt" - "--config=forcedebug" - "--spawn_strategy=local" -) - -./bazel test \ - --experimental_generate_json_trace_profile \ - --profile=_out_/profile.json \ - --test_summary=terse \ - --test_output=errors \ - "${test_args[@]}" || err=$? - -if [ "$err" -ne 0 ]; then - echo "--- annotating build result" - failing_tests="$(mktemp)" - - echo 'Run this command to run failing tests locally:' >> "$failing_tests" - echo >> "$failing_tests" - echo '```bash' >> "$failing_tests" - echo "./bazel test \\" >> "$failing_tests" - - # Take the lines that start with target labels. - # Lines look like "//foo FAILED in 10s" - { ./bazel test --test_summary=terse "${test_args[@]}" || true ; } | \ - grep '^//' | \ - sed -e 's/ .*/ \\/' | \ - sed -e 's/^/ /' >> "$failing_tests" - - # Put this last as an easy way to not have a `\` on the last line. - echo ' -c opt --config=forcedebug' >> "$failing_tests" - echo '```' >> "$failing_tests" - - buildkite-agent annotate --context "test-static-sanitized.sh" --style error --append < "$failing_tests" - - exit "$err" -fi diff --git a/.sorbet-buildkite/test-static-sanitized.sh b/.sorbet-buildkite/test-static-sanitized.sh index 2dfdba554f..5c078758df 100755 --- a/.sorbet-buildkite/test-static-sanitized.sh +++ b/.sorbet-buildkite/test-static-sanitized.sh @@ -33,8 +33,6 @@ mkdir -p _out_ # NOTE: we skip the compiler tests because llvm doesn't interact well with the sanitizer test_args+=( - "--test_tag_filters=-compiler" - "--build_tag_filters=-compiler" "--build_tests_only" "//..." ) diff --git a/.vscode/settings.json b/.vscode/settings.json index 91e812f5b9..b942d6ff1d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -10,7 +10,7 @@ // Disable feature where Clangd auto-imports headers for missing references. // It inserts relative paths that we don't want. "clangd.arguments": ["--header-insertion=never"], - "clangd.path": "bazel-sorbet/external/llvm_toolchain_12_0_0/bin/clangd", + "clangd.path": "bazel-sorbet/external/llvm_toolchain_15_0_6/bin/clangd", "files.associations": { "*.rbi": "ruby", "*.rbupdated": "ruby", diff --git a/WORKSPACE b/WORKSPACE index fe4ce952c8..cc0f94e6cb 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -31,28 +31,42 @@ load("@com_grail_bazel_compdb//:deps.bzl", "bazel_compdb_deps") bazel_compdb_deps() -load("@com_grail_bazel_toolchain//toolchain:deps.bzl", "bazel_toolchain_dependencies") +load("@toolchains_llvm//toolchain:deps.bzl", "bazel_toolchain_dependencies") bazel_toolchain_dependencies() -load("@com_grail_bazel_toolchain//toolchain:rules.bzl", "llvm_toolchain") +load("@toolchains_llvm//toolchain:rules.bzl", "llvm_toolchain") llvm_toolchain( - name = "llvm_toolchain_12_0_0", + name = "llvm_toolchain_15_0_6", absolute_paths = True, - llvm_mirror_prefixes = [ - "https://sorbet-deps.s3-us-west-2.amazonaws.com/", - "https://artifactory-content.stripe.build/artifactory/github-archives/llvm/llvm-project/releases/download/llvmorg-", - "https://github.com/llvm/llvm-project/releases/download/llvmorg-", + alternative_llvm_sources = [ + "https://github.com/llvm/llvm-project/releases/download/llvmorg-{llvm_version}/{basename}", ], - llvm_version = "12.0.0", + llvm_version = "15.0.6", ) +load("@llvm_toolchain_15_0_6//:toolchains.bzl", "llvm_register_toolchains") + +llvm_register_toolchains() + +load("@emsdk//:deps.bzl", emsdk_deps = "deps") + +emsdk_deps() + +load("@emsdk//:emscripten_deps.bzl", emsdk_emscripten_deps = "emscripten_deps") + +emsdk_emscripten_deps(emscripten_version = "3.1.59") + +load("@emsdk//:toolchains.bzl", "register_emscripten_toolchains") + +register_emscripten_toolchains() + load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_dependencies") go_rules_dependencies() -go_register_toolchains() +go_register_toolchains(version = "1.20.7") load("@rules_ragel//ragel:ragel.bzl", "ragel_register_toolchains") @@ -74,10 +88,6 @@ load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps") protobuf_deps() -load("@build_bazel_rules_nodejs//:index.bzl", "node_repositories") - -node_repositories() - load("@rules_rust//rust:repositories.bzl", "rules_rust_dependencies", "rust_register_toolchains") rules_rust_dependencies() @@ -89,12 +99,20 @@ rust_register_toolchains( ], ) -BAZEL_VERSION = "5.2.0" +load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace") + +bazel_skylib_workspace() + +load("@aspect_bazel_lib//lib:repositories.bzl", "aspect_bazel_lib_dependencies") + +aspect_bazel_lib_dependencies() -BAZEL_INSTALLER_VERSION_LINUX_X86_64_SHA = "7d9ef51beab5726c55725fb36675c6fed0518576d3ba51fb4067580ddf7627c4" +BAZEL_INSTALLER_VERSION_LINUX_X86_64_SHA = "c0161a346b9c0d00e6eb3d3e8f9c4dece32f6292520248c5ab2e3527265601c1" -BAZEL_INSTALLER_VERSION_LINUX_ARM64_SHA = "ae50cb7d64aebee986287134ff8ca0335651a0c1685348b3216f3fdfa20ff7e7" +# Bazel for linux-arm64 doesn't have an installer at the moment. +# We have a workaround in `./bazel` to download the binary directly. +BAZEL_INSTALLER_VERSION_LINUX_ARM64_SHA = "5afe973cadc036496cac66f1414ca9be36881423f576db363d83afc9084c0c2f" -BAZEL_INSTALLER_VERSION_DARWIN_X86_64_SHA = "645e7c335efc3207905e98f0c56a598b7cb0282d54d9470e80f38fb698064fb3" +BAZEL_INSTALLER_VERSION_DARWIN_X86_64_SHA = "455589bbaedf26e7bdb949288f777492ba1c53d67fd8329bfe066fb988df0e5c" -BAZEL_INSTALLER_VERSION_DARWIN_ARM64_SHA = "bc018ee7980cdf1c3f0099ec1568847a1756a3c00f1f9440bca44c26ceb3d90f" +BAZEL_INSTALLER_VERSION_DARWIN_ARM64_SHA = "c2b5f82dcc1561d25bc05c734a7cc7a5ff58d4e69185f3d6d21b51ddb53b488b" diff --git a/ast/Helpers.cc b/ast/Helpers.cc index 453141c9d2..2fe2c1b41a 100644 --- a/ast/Helpers.cc +++ b/ast/Helpers.cc @@ -80,9 +80,8 @@ bool BehaviorHelpers::checkEmptyDeep(const ExpressionPtr &expr) { expr, [&](const ast::Send &send) { - result = send.fun == core::Names::keepForIde() || send.fun == core::Names::keepDef() || - send.fun == core::Names::keepSelfDef() || send.fun == core::Names::include() || - send.fun == core::Names::extend(); + result = send.fun == core::Names::keepDef() || send.fun == core::Names::keepSelfDef() || + send.fun == core::Names::include() || send.fun == core::Names::extend(); }, [&](const ast::EmptyTree &) { result = true; }, diff --git a/ast/Helpers.h b/ast/Helpers.h index ae85373ab3..42b38fb9c4 100644 --- a/ast/Helpers.h +++ b/ast/Helpers.h @@ -415,16 +415,11 @@ class MK { {core::Names::Constants::T(), core::Names::Constants::Boolean()}); } - static ExpressionPtr KeepForIDE(core::LocOffsets loc, ExpressionPtr arg) { - return Send1(loc, Constant(loc, core::Symbols::Sorbet_Private_Static()), core::Names::keepForIde(), loc, - std::move(arg)); - } - - static ExpressionPtr ZSuper(core::LocOffsets loc) { + static ExpressionPtr ZSuper(core::LocOffsets loc, core::NameRef method) { Send::Flags flags; flags.isPrivateOk = true; - return Send(loc, Self(loc), core::Names::super(), loc, 1, SendArgs(make_expression(loc)), - flags); + return Send(loc, Self(loc), method, loc, 1, + SendArgs(make_expression(loc.copyEndWithZeroLength())), flags); } static ExpressionPtr Magic(core::LocOffsets loc) { diff --git a/ast/TreeSanityChecks.cc b/ast/TreeSanityChecks.cc index 020bad2768..6c819bacc1 100644 --- a/ast/TreeSanityChecks.cc +++ b/ast/TreeSanityChecks.cc @@ -107,6 +107,17 @@ void EmptyTree::_sanityCheck() {} void Literal::_sanityCheck() { ENFORCE(value != nullptr); + + auto tag = value.tag(); + switch (tag) { + case core::TypePtr::Tag::IntegerLiteralType: + case core::TypePtr::Tag::FloatLiteralType: + case core::TypePtr::Tag::NamedLiteralType: + case core::TypePtr::Tag::ClassType: + break; + default: + ENFORCE(false, "unexpected TypePtr::Tag: {}", tag); + } } void Hash::_sanityCheck() { diff --git a/ast/Trees.cc b/ast/Trees.cc index e240795c7e..53f4b55257 100644 --- a/ast/Trees.cc +++ b/ast/Trees.cc @@ -123,10 +123,6 @@ bool isa_reference(const ExpressionPtr &what) { isa_tree(what); } -bool isa_declaration(const ExpressionPtr &what) { - return isa_tree(what) || isa_tree(what); -} - /** https://git.corp.stripe.com/gist/nelhage/51564501674174da24822e60ad770f64 * * [] - prototype only @@ -305,12 +301,11 @@ ConstantLit::ConstantLit(core::LocOffsets loc, core::SymbolRef symbol, Expressio _sanityCheck(); } -optional>> -ConstantLit::fullUnresolvedPath(const core::GlobalState &gs) const { +optional>> ConstantLit::fullUnresolvedPath(core::Context ctx) const { if (this->symbol != core::Symbols::StubModule()) { return nullopt; } - ENFORCE(this->resolutionScopes != nullptr && !this->resolutionScopes->empty()); + ENFORCE(this->resolutionScopes != nullptr && !this->resolutionScopes->empty(), "loc={}", this->loc.showRaw(ctx)); vector namesFailedToResolve; auto *nested = this; diff --git a/ast/Trees.h b/ast/Trees.h index 21dc847367..ce60f26953 100644 --- a/ast/Trees.h +++ b/ast/Trees.h @@ -287,8 +287,6 @@ template bool isa_tree(const ExpressionPtr &what) { bool isa_reference(const ExpressionPtr &what); -bool isa_declaration(const ExpressionPtr &what); - template To *cast_tree(ExpressionPtr &what) { if (isa_tree(what)) { return reinterpret_cast(what.get()); @@ -341,9 +339,11 @@ template <> inline const ExpressionPtr &ExpressionPtr::cast(const return tree; } -#define EXPRESSION(name) \ - class name; \ - template <> struct ExpressionToTag { static constexpr Tag value = Tag::name; }; \ +#define EXPRESSION(name) \ + class name; \ + template <> struct ExpressionToTag { \ + static constexpr Tag value = Tag::name; \ + }; \ class __attribute__((aligned(8))) name final EXPRESSION(ClassDef) { @@ -1091,8 +1091,7 @@ EXPRESSION(ConstantLit) { std::string toStringWithTabs(const core::GlobalState &gs, int tabs = 0) const; std::string showRaw(const core::GlobalState &gs, int tabs = 0) const; std::string nodeName() const; - std::optional>> fullUnresolvedPath( - const core::GlobalState &gs) const; + std::optional>> fullUnresolvedPath(core::Context ctx) const; void _sanityCheck(); }; diff --git a/ast/desugar/Desugar.cc b/ast/desugar/Desugar.cc index 1aee80422c..dd2a9959f2 100644 --- a/ast/desugar/Desugar.cc +++ b/ast/desugar/Desugar.cc @@ -24,11 +24,15 @@ struct DesugarContext final { core::NameRef enclosingBlockArg; core::LocOffsets enclosingMethodLoc; core::NameRef enclosingMethodName; + bool inAnyBlock; + bool inModule; DesugarContext(core::MutableContext ctx, uint32_t &uniqueCounter, core::NameRef enclosingBlockArg, - core::LocOffsets enclosingMethodLoc, core::NameRef enclosingMethodName) + core::LocOffsets enclosingMethodLoc, core::NameRef enclosingMethodName, bool inAnyBlock, + bool inModule) : ctx(ctx), uniqueCounter(uniqueCounter), enclosingBlockArg(enclosingBlockArg), - enclosingMethodLoc(enclosingMethodLoc), enclosingMethodName(enclosingMethodName){}; + enclosingMethodLoc(enclosingMethodLoc), enclosingMethodName(enclosingMethodName), inAnyBlock(inAnyBlock), + inModule(inModule){}; core::NameRef freshNameUnique(core::NameRef name) { return ctx.state.freshNameUnique(core::UniqueNameKind::Desugar, name, ++uniqueCounter); @@ -90,14 +94,14 @@ pair desugarArgs(DesugarContext dctx if (auto *oargs = parser::cast_node(argnode.get())) { args.reserve(oargs->args.size()); for (auto &arg : oargs->args) { - if (auto *lhs = parser::cast_node(arg.get())) { + if (parser::isa_node(arg.get())) { core::NameRef temporary = dctx.freshNameUnique(core::Names::destructureArg()); args.emplace_back(MK::Local(arg->loc, temporary)); unique_ptr lvarNode = make_unique(arg->loc, temporary); unique_ptr destructure = make_unique(arg->loc, std::move(arg), std::move(lvarNode)); destructures.emplace_back(node2TreeImpl(dctx, std::move(destructure))); - } else if (auto *lhs = parser::cast_node(arg.get())) { + } else if (parser::isa_node(arg.get())) { // TODO implement logic for `**nil` args } else if (auto *fargs = parser::cast_node(arg.get())) { // we desugar (m, n, ...) into (m, n, *, **, &) @@ -170,13 +174,48 @@ ExpressionPtr desugarBlock(DesugarContext dctx, core::LocOffsets loc, core::LocO ENFORCE(send != nullptr, "DesugarBlock: failed to find Send"); } auto [args, destructures] = desugarArgs(dctx, loc, blockArgs); - auto desugaredBody = desugarBody(dctx, loc, blockBody, std::move(destructures)); + auto inBlock = true; + DesugarContext dctx1(dctx.ctx, dctx.uniqueCounter, dctx.enclosingBlockArg, dctx.enclosingMethodLoc, + dctx.enclosingMethodName, inBlock, dctx.inModule); + auto desugaredBody = desugarBody(dctx1, loc, blockBody, std::move(destructures)); // TODO the send->block's loc is too big and includes the whole send send->setBlock(MK::Block(loc, std::move(desugaredBody), std::move(args))); return res; } +ExpressionPtr desugarBegin(DesugarContext dctx, core::LocOffsets loc, parser::NodeVec &stmts) { + if (stmts.empty()) { + return MK::Nil(loc); + } else { + InsSeq::STATS_store stats; + stats.reserve(stmts.size() - 1); + auto end = stmts.end(); + --end; + for (auto it = stmts.begin(); it != end; ++it) { + auto &stat = *it; + stats.emplace_back(node2TreeImpl(dctx, std::move(stat))); + }; + auto &last = stmts.back(); + + if (stmts.size() == 1 && last != nullptr) { + // If we're about to make an InsSeq, but the stmts are empty, MK::InsSeq will return just expr. + // But we want to treat `(0)` as having a loc that spans the parens too, for autocorrects. + // We patch that here, because the loc on an ExpressionPtr is const (maybe we should fix + // that, and then solve this in MK::InsSeq?) but it's easier to solve this here. + last->loc = loc; + } + + auto expr = node2TreeImpl(dctx, std::move(last)); + return MK::InsSeq(loc, std::move(stats), std::move(expr)); + } +} + +core::NameRef maybeTypedSuper(DesugarContext dctx) { + return (dctx.ctx.state.typedSuper && !dctx.inAnyBlock && !dctx.inModule) ? core::Names::super() + : core::Names::untypedSuper(); +} + bool isStringLit(DesugarContext dctx, ExpressionPtr &expr) { Literal *lit; return (lit = cast_tree(expr)) && lit->isString(); @@ -198,13 +237,29 @@ ExpressionPtr mergeStrings(DesugarContext dctx, core::LocOffsets loc, } } +// In DString, the `#{...}` are all wrapped in parser::Begin nodes. +// Normally, we have handling for parser::Begin nodes that treats the loc of such a node to include +// the enclosing brackets. So e.g. the loc of `(x)` would be length 3. +// But the parser also uses Begin nodes for `#{...}`, and for those we don't want the loc to include +// the braces (think: we don't want a `T.must` to look like `"T.must(#{x})"`, we want it to look +// like `"#{T.must(x)}"`, etc.) +// So we have this skipSingletonBegin to skip over these nodes before desugaring them in DString +unique_ptr skipSingletonBegin(unique_ptr node) { + auto begin = parser::cast_node(node.get()); + if (begin == nullptr || begin->stmts.size() != 1) { + return node; + } + + return move(begin->stmts[0]); +} + ExpressionPtr desugarDString(DesugarContext dctx, core::LocOffsets loc, parser::NodeVec nodes) { if (nodes.empty()) { return MK::String(loc, core::Names::empty()); } auto it = nodes.begin(); auto end = nodes.end(); - ExpressionPtr first = node2TreeImpl(dctx, std::move(*it)); + ExpressionPtr first = node2TreeImpl(dctx, skipSingletonBegin(std::move(*it))); InlinedVector stringsAccumulated; Send::ARGS_store interpArgs; @@ -221,7 +276,7 @@ ExpressionPtr desugarDString(DesugarContext dctx, core::LocOffsets loc, parser:: for (; it != end; ++it) { auto &stat = *it; - ExpressionPtr narg = node2TreeImpl(dctx, std::move(stat)); + ExpressionPtr narg = node2TreeImpl(dctx, skipSingletonBegin(std::move(stat))); if (allStringsSoFar && isStringLit(dctx, narg)) { stringsAccumulated.emplace_back(std::move(narg)); } else if (isa_tree(narg)) { @@ -305,7 +360,8 @@ ExpressionPtr buildMethod(DesugarContext dctx, core::LocOffsets loc, core::LocOf bool isSelf) { // Reset uniqueCounter within this scope (to keep numbers small) uint32_t uniqueCounter = 1; - DesugarContext dctx1(dctx.ctx, uniqueCounter, dctx.enclosingBlockArg, declLoc, name); + auto inModule = dctx.inModule && !isSelf; + DesugarContext dctx1(dctx.ctx, uniqueCounter, dctx.enclosingBlockArg, declLoc, name, dctx.inAnyBlock, inModule); auto [args, destructures] = desugarArgs(dctx1, loc, argnode); if (args.empty() || !isa_tree(args.back())) { @@ -317,7 +373,7 @@ ExpressionPtr buildMethod(DesugarContext dctx, core::LocOffsets loc, core::LocOf ENFORCE(blkArg != nullptr, "Every method's last arg must be a block arg by now."); auto enclosingBlockArg = blockArg2Name(dctx, *blkArg); - DesugarContext dctx2(dctx1.ctx, dctx1.uniqueCounter, enclosingBlockArg, declLoc, name); + DesugarContext dctx2(dctx1.ctx, dctx1.uniqueCounter, enclosingBlockArg, declLoc, name, dctx.inAnyBlock, inModule); ExpressionPtr desugaredBody = desugarBody(dctx2, loc, body, std::move(destructures)); desugaredBody = validateRBIBody(dctx2, move(desugaredBody)); @@ -351,6 +407,23 @@ ExpressionPtr unsupportedNode(DesugarContext dctx, parser::Node *node) { return MK::EmptyTree(); } +// Desugar multiple left hand side assignments into a sequence of assignments +// +// Considering this example: +// ```rb +// arr = [1, 2, 3] +// a, *b = arr +// ``` +// +// We desugar the assignment `a, *b = arr` into: +// ```rb +// tmp = ::.expandSplat(arr, 1, 0) +// a = tmp[0] +// b = tmp.to_ary +// ``` +// +// While calling `to_ary` doesn't return the correct value if we were to execute this code, +// it returns the correct type from a static point of view. ExpressionPtr desugarMlhs(DesugarContext dctx, core::LocOffsets loc, parser::Mlhs *lhs, ExpressionPtr rhs) { InsSeq::STATS_store stats; @@ -371,18 +444,15 @@ ExpressionPtr desugarMlhs(DesugarContext dctx, core::LocOffsets loc, parser::Mlh int left = i; int right = lhs->exprs.size() - left - 1; if (!isa_tree(lh)) { - auto exclusive = MK::True(lh.loc()); if (right == 0) { right = 1; - exclusive = MK::False(lh.loc()); } auto lhloc = lh.loc(); auto zlhloc = lhloc.copyWithZeroLength(); - auto index = MK::Send3(lhloc, MK::Constant(lhloc, core::Symbols::Range()), core::Names::new_(), zlhloc, - MK::Int(lhloc, left), MK::Int(lhloc, -right), std::move(exclusive)); - stats.emplace_back(MK::Assign( - lhloc, std::move(lh), - MK::Send1(loc, MK::Local(loc, tempExpanded), core::Names::slice(), zlhloc, std::move(index)))); + // Calling `to_ary` is not faithful to the runtime behavior, + // but that it is faithful to the expected static type-checking behavior. + auto ary = MK::Send0(loc, MK::Local(loc, tempExpanded), core::Names::toAry(), zlhloc); + stats.emplace_back(MK::Assign(lhloc, std::move(lh), std::move(ary))); } i = -right; } else { @@ -477,8 +547,10 @@ ClassDef::RHS_store scopeNodeToBody(DesugarContext dctx, unique_ptr(node.get())) { body.reserve(begin->stmts.size()); for (auto &stat : begin->stmts) { @@ -543,7 +615,7 @@ OpAsgnScaffolding copyArgsForOpAsgn(DesugarContext dctx, Send *s) { // we saw on the LHS. readArgs.reserve(numPosArgs); // these are the arguments for the second send, e.g. x.y=(val). That's why we need the space for the extra argument - // here: to accomodate the call to field= instead of just field. + // here: to accommodate the call to field= instead of just field. assgnArgs.reserve(numPosArgs + 1); for (auto &arg : s->posArgs()) { @@ -868,7 +940,7 @@ ExpressionPtr node2TreeImpl(DesugarContext dctx, unique_ptr what) return parser::isa_node(node.get()) || parser::isa_node(node.get()); })) { - // hold a reference to the node, and remove it from the back fo the send list + // hold a reference to the node, and remove it from the back of the send list auto node = std::move(send->args[kwargsHashIndex]); send->args.erase(send->args.begin() + kwargsHashIndex); @@ -1095,25 +1167,7 @@ ExpressionPtr node2TreeImpl(DesugarContext dctx, unique_ptr what) [&](parser::Block *block) { result = desugarBlock(dctx, loc, block->loc, block->send, block->args, block->body); }, - [&](parser::Begin *begin) { - if (!begin->stmts.empty()) { - InsSeq::STATS_store stats; - stats.reserve(begin->stmts.size() - 1); - auto end = begin->stmts.end(); - --end; - for (auto it = begin->stmts.begin(); it != end; ++it) { - auto &stat = *it; - stats.emplace_back(node2TreeImpl(dctx, std::move(stat))); - }; - auto &last = begin->stmts.back(); - auto expr = node2TreeImpl(dctx, std::move(last)); - auto block = MK::InsSeq(loc, std::move(stats), std::move(expr)); - result = std::move(block); - } else { - ExpressionPtr res = MK::Nil(loc); - result = std::move(res); - } - }, + [&](parser::Begin *begin) { result = desugarBegin(dctx, loc, begin->stmts); }, [&](parser::Assign *asgn) { auto lhs = node2TreeImpl(dctx, std::move(asgn->lhs)); auto rhs = node2TreeImpl(dctx, std::move(asgn->rhs)); @@ -1168,9 +1222,12 @@ ExpressionPtr node2TreeImpl(DesugarContext dctx, unique_ptr what) thenp = std::move(rhs); } auto lhsLoc = lhs.loc(); + auto condLoc = lhsLoc.exists() && thenp.loc().exists() + ? core::LocOffsets{lhsLoc.endPos(), thenp.loc().beginPos()} + : lhsLoc; auto temp = MK::Assign(loc, andAndTemp, std::move(lhs)); auto iff = - MK::If(loc, MK::Local(lhsLoc, andAndTemp), std::move(thenp), MK::Local(lhsLoc, andAndTemp)); + MK::If(loc, MK::Local(condLoc, andAndTemp), std::move(thenp), MK::Local(lhsLoc, andAndTemp)); auto wrapped = MK::InsSeq1(loc, std::move(temp), std::move(iff)); result = std::move(wrapped); } @@ -1185,8 +1242,11 @@ ExpressionPtr node2TreeImpl(DesugarContext dctx, unique_ptr what) } else { core::NameRef tempName = dctx.freshNameUnique(core::Names::orOr()); auto lhsLoc = lhs.loc(); + auto condLoc = lhsLoc.exists() && rhs.loc().exists() + ? core::LocOffsets{lhsLoc.endPos(), rhs.loc().beginPos()} + : lhsLoc; auto temp = MK::Assign(loc, tempName, std::move(lhs)); - auto iff = MK::If(loc, MK::Local(lhsLoc, tempName), MK::Local(lhsLoc, tempName), std::move(rhs)); + auto iff = MK::If(loc, MK::Local(condLoc, tempName), MK::Local(lhsLoc, tempName), std::move(rhs)); auto wrapped = MK::InsSeq1(loc, std::move(temp), std::move(iff)); result = std::move(wrapped); } @@ -1217,7 +1277,7 @@ ExpressionPtr node2TreeImpl(DesugarContext dctx, unique_ptr what) auto body = MK::Assign(loc, std::move(recv), std::move(arg)); auto iff = MK::If(loc, std::move(cond), std::move(body), std::move(elsep)); result = std::move(iff); - } else if (auto i = cast_tree(recv)) { + } else if (isa_tree(recv)) { if (auto e = dctx.ctx.beginError(what->loc, core::errors::Desugar::NoConstantReassignment)) { e.setHeader("Constant reassignment is not supported"); } @@ -1307,7 +1367,7 @@ ExpressionPtr node2TreeImpl(DesugarContext dctx, unique_ptr what) } auto iff = MK::If(loc, std::move(cond), std::move(body), std::move(elsep)); result = std::move(iff); - } else if (auto i = cast_tree(recv)) { + } else if (isa_tree(recv)) { if (auto e = dctx.ctx.beginError(what->loc, core::errors::Desugar::NoConstantReassignment)) { e.setHeader("Constant reassignment is not supported"); } @@ -1369,7 +1429,7 @@ ExpressionPtr node2TreeImpl(DesugarContext dctx, unique_ptr what) auto send = MK::Send1(loc, std::move(recv), opAsgn->op, opAsgn->opLoc, std::move(rhs)); auto res = MK::Assign(loc, std::move(lhs), std::move(send)); result = std::move(res); - } else if (auto i = cast_tree(recv)) { + } else if (isa_tree(recv)) { if (auto e = dctx.ctx.beginError(what->loc, core::errors::Desugar::NoConstantReassignment)) { e.setHeader("Constant reassignment is not supported"); } @@ -1478,42 +1538,28 @@ ExpressionPtr node2TreeImpl(DesugarContext dctx, unique_ptr what) ExpressionPtr res = MK::Constant(loc, core::Symbols::root()); result = std::move(res); }, - [&](parser::Kwbegin *kwbegin) { - if (!kwbegin->stmts.empty()) { - InsSeq::STATS_store stats; - stats.reserve(kwbegin->stmts.size() - 1); - auto end = kwbegin->stmts.end(); - --end; - for (auto it = kwbegin->stmts.begin(); it != end; ++it) { - auto &stat = *it; - stats.emplace_back(node2TreeImpl(dctx, std::move(stat))); - }; - auto &last = kwbegin->stmts.back(); - auto expr = node2TreeImpl(dctx, std::move(last)); - auto block = MK::InsSeq(loc, std::move(stats), std::move(expr)); - result = std::move(block); - } else { - ExpressionPtr res = MK::EmptyTree(); - result = std::move(res); - } - }, + [&](parser::Kwbegin *kwbegin) { result = desugarBegin(dctx, loc, kwbegin->stmts); }, [&](parser::Module *module) { - ClassDef::RHS_store body = scopeNodeToBody(dctx, std::move(module->body)); + DesugarContext dctx1(dctx.ctx, dctx.uniqueCounter, dctx.enclosingBlockArg, dctx.enclosingMethodLoc, + dctx.enclosingMethodName, dctx.inAnyBlock, true); + ClassDef::RHS_store body = scopeNodeToBody(dctx1, std::move(module->body)); ClassDef::ANCESTORS_store ancestors; ExpressionPtr res = MK::Module(module->loc, module->declLoc, node2TreeImpl(dctx, std::move(module->name)), std::move(ancestors), std::move(body)); result = std::move(res); }, - [&](parser::Class *claz) { - ClassDef::RHS_store body = scopeNodeToBody(dctx, std::move(claz->body)); + [&](parser::Class *klass) { + DesugarContext dctx1(dctx.ctx, dctx.uniqueCounter, dctx.enclosingBlockArg, dctx.enclosingMethodLoc, + dctx.enclosingMethodName, dctx.inAnyBlock, false); + ClassDef::RHS_store body = scopeNodeToBody(dctx1, std::move(klass->body)); ClassDef::ANCESTORS_store ancestors; - if (claz->superclass == nullptr) { + if (klass->superclass == nullptr) { ancestors.emplace_back(MK::Constant(loc, core::Symbols::todo())); } else { - ancestors.emplace_back(node2TreeImpl(dctx, std::move(claz->superclass))); + ancestors.emplace_back(node2TreeImpl(dctx, std::move(klass->superclass))); } - ExpressionPtr res = MK::Class(claz->loc, claz->declLoc, node2TreeImpl(dctx, std::move(claz->name)), + ExpressionPtr res = MK::Class(klass->loc, klass->declLoc, node2TreeImpl(dctx, std::move(klass->name)), std::move(ancestors), std::move(body)); result = std::move(res); }, @@ -1563,6 +1609,10 @@ ExpressionPtr node2TreeImpl(DesugarContext dctx, unique_ptr what) if (auto e = dctx.ctx.beginError(method->singleton->loc, core::errors::Desugar::InvalidSingletonDef)) { e.setHeader("`{}` is only supported for `{}`", "def EXPRESSION.method", "def self.method"); + e.addErrorNote("When it's imperative to define a singleton method on an object,\n" + " use `{}` instead.\n" + " The method will NOT be visible to Sorbet.", + "EXPRESSION.define_singleton_method(:method) { ... }"); } } bool isSelf = true; @@ -1583,7 +1633,9 @@ ExpressionPtr node2TreeImpl(DesugarContext dctx, unique_ptr what) return; } - ClassDef::RHS_store body = scopeNodeToBody(dctx, std::move(sclass->body)); + DesugarContext dctx1(dctx.ctx, dctx.uniqueCounter, dctx.enclosingBlockArg, dctx.enclosingMethodLoc, + dctx.enclosingMethodName, dctx.inAnyBlock, false); + ClassDef::RHS_store body = scopeNodeToBody(dctx1, std::move(sclass->body)); ClassDef::ANCESTORS_store emptyAncestors; ExpressionPtr res = MK::Class(sclass->loc, sclass->declLoc, @@ -1593,7 +1645,9 @@ ExpressionPtr node2TreeImpl(DesugarContext dctx, unique_ptr what) result = std::move(res); }, [&](parser::NumBlock *block) { - result = desugarBlock(dctx, loc, block->loc, block->send, block->args, block->body); + DesugarContext dctx1(dctx.ctx, dctx.uniqueCounter, dctx.enclosingBlockArg, dctx.enclosingMethodLoc, + dctx.enclosingMethodName, true, dctx.inModule); + result = desugarBlock(dctx1, loc, block->loc, block->send, block->args, block->body); }, [&](parser::While *wl) { auto cond = node2TreeImpl(dctx, std::move(wl->cond)); @@ -1671,12 +1725,12 @@ ExpressionPtr node2TreeImpl(DesugarContext dctx, unique_ptr what) // Desugar super into a call to a normal method named `super`; // Do this by synthesizing a `Send` parse node and letting our // Send desugar handle it. - auto method = core::Names::super(); + auto method = maybeTypedSuper(dctx); auto send = make_unique(super->loc, nullptr, method, super->loc, std::move(super->args)); auto res = node2TreeImpl(dctx, std::move(send)); result = std::move(res); }, - [&](parser::ZSuper *zuper) { result = MK::ZSuper(loc); }, + [&](parser::ZSuper *zuper) { result = MK::ZSuper(loc, maybeTypedSuper(dctx)); }, [&](parser::For *for_) { MethodDef::ARGS_store args; bool canProvideNiceDesugar = true; @@ -1890,6 +1944,7 @@ ExpressionPtr node2TreeImpl(DesugarContext dctx, unique_ptr what) }, [&](parser::Return *ret) { if (ret->exprs.size() > 1) { + auto arrayLoc = ret->exprs.front()->loc.join(ret->exprs.back()->loc); Array::ENTRY_store elems; elems.reserve(ret->exprs.size()); for (auto &stat : ret->exprs) { @@ -1901,7 +1956,7 @@ ExpressionPtr node2TreeImpl(DesugarContext dctx, unique_ptr what) } elems.emplace_back(node2TreeImpl(dctx, std::move(stat))); }; - ExpressionPtr arr = MK::Array(loc, std::move(elems)); + ExpressionPtr arr = MK::Array(arrayLoc, std::move(elems)); ExpressionPtr res = MK::Return(loc, std::move(arr)); result = std::move(res); } else if (ret->exprs.size() == 1) { @@ -1922,6 +1977,7 @@ ExpressionPtr node2TreeImpl(DesugarContext dctx, unique_ptr what) }, [&](parser::Break *ret) { if (ret->exprs.size() > 1) { + auto arrayLoc = ret->exprs.front()->loc.join(ret->exprs.back()->loc); Array::ENTRY_store elems; elems.reserve(ret->exprs.size()); for (auto &stat : ret->exprs) { @@ -1933,7 +1989,7 @@ ExpressionPtr node2TreeImpl(DesugarContext dctx, unique_ptr what) } elems.emplace_back(node2TreeImpl(dctx, std::move(stat))); }; - ExpressionPtr arr = MK::Array(loc, std::move(elems)); + ExpressionPtr arr = MK::Array(arrayLoc, std::move(elems)); ExpressionPtr res = MK::Break(loc, std::move(arr)); result = std::move(res); } else if (ret->exprs.size() == 1) { @@ -1954,6 +2010,7 @@ ExpressionPtr node2TreeImpl(DesugarContext dctx, unique_ptr what) }, [&](parser::Next *ret) { if (ret->exprs.size() > 1) { + auto arrayLoc = ret->exprs.front()->loc.join(ret->exprs.back()->loc); Array::ENTRY_store elems; elems.reserve(ret->exprs.size()); for (auto &stat : ret->exprs) { @@ -1965,7 +2022,7 @@ ExpressionPtr node2TreeImpl(DesugarContext dctx, unique_ptr what) } elems.emplace_back(node2TreeImpl(dctx, std::move(stat))); }; - ExpressionPtr arr = MK::Array(loc, std::move(elems)); + ExpressionPtr arr = MK::Array(arrayLoc, std::move(elems)); ExpressionPtr res = MK::Next(loc, std::move(arr)); result = std::move(res); } else if (ret->exprs.size() == 1) { @@ -2356,7 +2413,7 @@ ExpressionPtr node2Tree(core::MutableContext ctx, unique_ptr what) uint32_t uniqueCounter = 1; // We don't have an enclosing block arg to start off. DesugarContext dctx(ctx, uniqueCounter, core::NameRef::noName(), core::LocOffsets::none(), - core::NameRef::noName()); + core::NameRef::noName(), false, false); auto loc = what->loc; auto result = node2TreeImpl(dctx, std::move(what)); result = liftTopLevel(dctx, loc, std::move(result)); diff --git a/ast/treemap/treemap.h b/ast/treemap/treemap.h index ad4ed9c425..5d51329655 100644 --- a/ast/treemap/treemap.h +++ b/ast/treemap/treemap.h @@ -18,6 +18,8 @@ class FUNC_EXAMPLE { // Not including the member will skip the branch // you may return the same pointer that you are given // caller is responsible to handle it + ExpressionPtr preTransformExpression(core::MutableContext ctx, ExpressionPtr original); + ExpressionPtr preTransformClassDef(core::MutableContext ctx, ClassDef *original); ExpressionPtr postTransformClassDef(core::MutableContext ctx, ExpressionPtr original); @@ -72,7 +74,7 @@ class FUNC_EXAMPLE { ExpressionPtr postTransformInsSeq(core::MutableContext ctx, ExpressionPtr original); }; -// NOTE: Implementations must use a context type parameter that `MutableContext` is convertable to. +// NOTE: Implementations must use a context type parameter that `MutableContext` is convertible to. // That is, either `Context` or `MutableContext`. #define GENERATE_HAS_MEMBER_VISITOR(X, arg_types...) GENERATE_HAS_MEMBER(X, arg_types) @@ -86,56 +88,58 @@ class FUNC_EXAMPLE { GENERATE_CALL_MEMBER(postTransform##X, Exception::raise("should never be called. Incorrect use of TreeMap?"); \ return nullptr, arg_types) -#define GENERATE_METAPROGRAMMING_FOR(arg_types...) \ - GENERATE_HAS_MEMBER_VISITOR(preTransformUnresolvedIdent, arg_types); \ - GENERATE_HAS_MEMBER_VISITOR(preTransformLocal, arg_types); \ - GENERATE_HAS_MEMBER_VISITOR(preTransformUnresolvedConstantLit, arg_types); \ - GENERATE_HAS_MEMBER_VISITOR(preTransformConstantLit, arg_types); \ - GENERATE_HAS_MEMBER_VISITOR(preTransformLiteral, arg_types); \ - GENERATE_HAS_MEMBER_VISITOR(preTransformRuntimeMethodDefinition, arg_types); \ - \ - GENERATE_POSTPONE_PRECLASS(Expression, arg_types); \ - GENERATE_POSTPONE_PRECLASS(ClassDef, arg_types); \ - GENERATE_POSTPONE_PRECLASS(MethodDef, arg_types); \ - GENERATE_POSTPONE_PRECLASS(If, arg_types); \ - GENERATE_POSTPONE_PRECLASS(While, arg_types); \ - GENERATE_POSTPONE_PRECLASS(Break, arg_types); \ - GENERATE_POSTPONE_PRECLASS(Retry, arg_types); \ - GENERATE_POSTPONE_PRECLASS(Next, arg_types); \ - GENERATE_POSTPONE_PRECLASS(Return, arg_types); \ - GENERATE_POSTPONE_PRECLASS(RescueCase, arg_types); \ - GENERATE_POSTPONE_PRECLASS(Rescue, arg_types); \ - GENERATE_POSTPONE_PRECLASS(Assign, arg_types); \ - GENERATE_POSTPONE_PRECLASS(Send, arg_types); \ - GENERATE_POSTPONE_PRECLASS(Hash, arg_types); \ - GENERATE_POSTPONE_PRECLASS(Array, arg_types); \ - GENERATE_POSTPONE_PRECLASS(Block, arg_types); \ - GENERATE_POSTPONE_PRECLASS(InsSeq, arg_types); \ - GENERATE_POSTPONE_PRECLASS(Cast, arg_types); \ - \ - GENERATE_POSTPONE_POSTCLASS(ClassDef, arg_types); \ - GENERATE_POSTPONE_POSTCLASS(MethodDef, arg_types); \ - GENERATE_POSTPONE_POSTCLASS(If, arg_types); \ - GENERATE_POSTPONE_POSTCLASS(While, arg_types); \ - GENERATE_POSTPONE_POSTCLASS(Break, arg_types); \ - GENERATE_POSTPONE_POSTCLASS(Retry, arg_types); \ - GENERATE_POSTPONE_POSTCLASS(Next, arg_types); \ - GENERATE_POSTPONE_POSTCLASS(Return, arg_types); \ - GENERATE_POSTPONE_POSTCLASS(RescueCase, arg_types); \ - GENERATE_POSTPONE_POSTCLASS(Rescue, arg_types); \ - GENERATE_POSTPONE_POSTCLASS(UnresolvedIdent, arg_types); \ - GENERATE_POSTPONE_POSTCLASS(Assign, arg_types); \ - GENERATE_POSTPONE_POSTCLASS(Send, arg_types); \ - GENERATE_POSTPONE_POSTCLASS(Hash, arg_types); \ - GENERATE_POSTPONE_POSTCLASS(Array, arg_types); \ - GENERATE_POSTPONE_POSTCLASS(Local, arg_types); \ - GENERATE_POSTPONE_POSTCLASS(Literal, arg_types); \ - GENERATE_POSTPONE_POSTCLASS(UnresolvedConstantLit, arg_types); \ - GENERATE_POSTPONE_POSTCLASS(ConstantLit, arg_types); \ - GENERATE_POSTPONE_POSTCLASS(Block, arg_types); \ - GENERATE_POSTPONE_POSTCLASS(InsSeq, arg_types); \ - GENERATE_POSTPONE_POSTCLASS(Cast, arg_types); \ - GENERATE_POSTPONE_POSTCLASS(RuntimeMethodDefinition, arg_types); +#define GENERATE_METAPROGRAMMING_FOR(arg_types...) \ + GENERATE_HAS_MEMBER_VISITOR(preTransformUnresolvedIdent, arg_types, VISITOR_ARG_TYPE(UnresolvedIdent)); \ + GENERATE_HAS_MEMBER_VISITOR(preTransformLocal, arg_types, VISITOR_ARG_TYPE(Local)); \ + GENERATE_HAS_MEMBER_VISITOR(preTransformUnresolvedConstantLit, arg_types, \ + VISITOR_ARG_TYPE(UnresolvedConstantLit)); \ + GENERATE_HAS_MEMBER_VISITOR(preTransformConstantLit, arg_types, VISITOR_ARG_TYPE(ConstantLit)); \ + GENERATE_HAS_MEMBER_VISITOR(preTransformLiteral, arg_types, VISITOR_ARG_TYPE(Literal)); \ + GENERATE_HAS_MEMBER_VISITOR(preTransformRuntimeMethodDefinition, arg_types, \ + VISITOR_ARG_TYPE(RuntimeMethodDefinition)); \ + \ + GENERATE_POSTPONE_PRECLASS(ExpressionPtr, arg_types, VISITOR_ARG_TYPE(ExpressionPtr)); \ + GENERATE_POSTPONE_PRECLASS(ClassDef, arg_types, VISITOR_ARG_TYPE(ClassDef)); \ + GENERATE_POSTPONE_PRECLASS(MethodDef, arg_types, VISITOR_ARG_TYPE(MethodDef)); \ + GENERATE_POSTPONE_PRECLASS(If, arg_types, VISITOR_ARG_TYPE(If)); \ + GENERATE_POSTPONE_PRECLASS(While, arg_types, VISITOR_ARG_TYPE(While)); \ + GENERATE_POSTPONE_PRECLASS(Break, arg_types, VISITOR_ARG_TYPE(Break)); \ + GENERATE_POSTPONE_PRECLASS(Retry, arg_types, VISITOR_ARG_TYPE(Retry)); \ + GENERATE_POSTPONE_PRECLASS(Next, arg_types, VISITOR_ARG_TYPE(Next)); \ + GENERATE_POSTPONE_PRECLASS(Return, arg_types, VISITOR_ARG_TYPE(Return)); \ + GENERATE_POSTPONE_PRECLASS(RescueCase, arg_types, VISITOR_ARG_TYPE(RescueCase)); \ + GENERATE_POSTPONE_PRECLASS(Rescue, arg_types, VISITOR_ARG_TYPE(Rescue)); \ + GENERATE_POSTPONE_PRECLASS(Assign, arg_types, VISITOR_ARG_TYPE(Assign)); \ + GENERATE_POSTPONE_PRECLASS(Send, arg_types, VISITOR_ARG_TYPE(Send)); \ + GENERATE_POSTPONE_PRECLASS(Hash, arg_types, VISITOR_ARG_TYPE(Hash)); \ + GENERATE_POSTPONE_PRECLASS(Array, arg_types, VISITOR_ARG_TYPE(Array)); \ + GENERATE_POSTPONE_PRECLASS(Block, arg_types, VISITOR_ARG_TYPE(Block)); \ + GENERATE_POSTPONE_PRECLASS(InsSeq, arg_types, VISITOR_ARG_TYPE(InsSeq)); \ + GENERATE_POSTPONE_PRECLASS(Cast, arg_types, VISITOR_ARG_TYPE(Cast)); \ + \ + GENERATE_POSTPONE_POSTCLASS(ClassDef, arg_types, VISITOR_ARG_TYPE(ClassDef)); \ + GENERATE_POSTPONE_POSTCLASS(MethodDef, arg_types, VISITOR_ARG_TYPE(MethodDef)); \ + GENERATE_POSTPONE_POSTCLASS(If, arg_types, VISITOR_ARG_TYPE(If)); \ + GENERATE_POSTPONE_POSTCLASS(While, arg_types, VISITOR_ARG_TYPE(While)); \ + GENERATE_POSTPONE_POSTCLASS(Break, arg_types, VISITOR_ARG_TYPE(Break)); \ + GENERATE_POSTPONE_POSTCLASS(Retry, arg_types, VISITOR_ARG_TYPE(Retry)); \ + GENERATE_POSTPONE_POSTCLASS(Next, arg_types, VISITOR_ARG_TYPE(Next)); \ + GENERATE_POSTPONE_POSTCLASS(Return, arg_types, VISITOR_ARG_TYPE(Return)); \ + GENERATE_POSTPONE_POSTCLASS(RescueCase, arg_types, VISITOR_ARG_TYPE(RescueCase)); \ + GENERATE_POSTPONE_POSTCLASS(Rescue, arg_types, VISITOR_ARG_TYPE(Rescue)); \ + GENERATE_POSTPONE_POSTCLASS(UnresolvedIdent, arg_types, VISITOR_ARG_TYPE(UnresolvedIdent)); \ + GENERATE_POSTPONE_POSTCLASS(Assign, arg_types, VISITOR_ARG_TYPE(Assign)); \ + GENERATE_POSTPONE_POSTCLASS(Send, arg_types, VISITOR_ARG_TYPE(Send)); \ + GENERATE_POSTPONE_POSTCLASS(Hash, arg_types, VISITOR_ARG_TYPE(Hash)); \ + GENERATE_POSTPONE_POSTCLASS(Array, arg_types, VISITOR_ARG_TYPE(Array)); \ + GENERATE_POSTPONE_POSTCLASS(Local, arg_types, VISITOR_ARG_TYPE(Local)); \ + GENERATE_POSTPONE_POSTCLASS(Literal, arg_types, VISITOR_ARG_TYPE(Literal)); \ + GENERATE_POSTPONE_POSTCLASS(UnresolvedConstantLit, arg_types, VISITOR_ARG_TYPE(UnresolvedConstantLit)); \ + GENERATE_POSTPONE_POSTCLASS(ConstantLit, arg_types, VISITOR_ARG_TYPE(ConstantLit)); \ + GENERATE_POSTPONE_POSTCLASS(Block, arg_types, VISITOR_ARG_TYPE(Block)); \ + GENERATE_POSTPONE_POSTCLASS(InsSeq, arg_types, VISITOR_ARG_TYPE(InsSeq)); \ + GENERATE_POSTPONE_POSTCLASS(Cast, arg_types, VISITOR_ARG_TYPE(Cast)); \ + GENERATE_POSTPONE_POSTCLASS(RuntimeMethodDefinition, arg_types, VISITOR_ARG_TYPE(RuntimeMethodDefinition)); // Used to indicate that TreeMap has already reported location for this exception struct ReportedRubyException { @@ -146,6 +150,7 @@ struct ReportedRubyException { enum class TreeMapKind { Map, Walk, + ConstWalk, }; template struct MapFunctions; @@ -156,7 +161,9 @@ template <> struct MapFunctions { static ExpressionPtr &&pass(ExpressionPtr &p) { return static_cast(p); } - GENERATE_METAPROGRAMMING_FOR(std::declval(), std::declval()); +#define VISITOR_ARG_TYPE(_class_name) std::declval() + GENERATE_METAPROGRAMMING_FOR(std::declval()); +#undef VISITOR_ARG_TYPE }; template <> struct MapFunctions { @@ -165,7 +172,20 @@ template <> struct MapFunctions { static ExpressionPtr &pass(ExpressionPtr &p) { return p; } - GENERATE_METAPROGRAMMING_FOR(std::declval(), std::declval()); +#define VISITOR_ARG_TYPE(_class_name) std::declval() + GENERATE_METAPROGRAMMING_FOR(std::declval()); +#undef VISITOR_ARG_TYPE +}; + +template <> struct MapFunctions { + using return_type = void; + using arg_type = const ExpressionPtr &; + static const ExpressionPtr &pass(const ExpressionPtr &p) { + return p; + } +#define VISITOR_ARG_TYPE(class_name) std::declval() + GENERATE_METAPROGRAMMING_FOR(std::declval()); +#undef VISITOR_ARG_TYPE }; enum class TreeMapDepthKind { @@ -185,6 +205,8 @@ template c friend class ShallowMap; friend class TreeWalk; friend class ShallowWalk; + friend class ConstTreeWalk; + friend class ConstShallowWalk; using Funcs = MapFunctions; using return_type = typename Funcs::return_type; @@ -202,32 +224,40 @@ template c TreeMapper(FUNC &func) : func(func) {} -#define CALL_PRE(member) \ - if constexpr (Funcs::template HAS_MEMBER_preTransform##member()) { \ - if constexpr (Kind == TreeMapKind::Map) { \ - v = Funcs::template CALL_MEMBER_preTransform##member::call(func, ctx, Funcs::pass(v)); \ - } else if (Kind == TreeMapKind::Walk) { \ - Funcs::template CALL_MEMBER_preTransform##member::call(func, ctx, Funcs::pass(v)); \ - } \ - } - -#define CALL_POST(member) \ - if constexpr (Kind == TreeMapKind::Map) { \ - if constexpr (Funcs::template HAS_MEMBER_postTransform##member()) { \ - return Funcs::template CALL_MEMBER_postTransform##member::call(func, ctx, Funcs::pass(v)); \ - } \ - return v; \ - } else if constexpr (Kind == TreeMapKind::Walk) { \ - if constexpr (Funcs::template HAS_MEMBER_postTransform##member()) { \ - Funcs::template CALL_MEMBER_postTransform##member::call(func, ctx, Funcs::pass(v)); \ - } \ - } - -#define CALL_MAP(tree, ctx) \ - if constexpr (Kind == TreeMapKind::Map) { \ - tree = mapIt(Funcs::pass(tree), ctx); \ - } else if constexpr (Kind == TreeMapKind::Walk) { \ - mapIt(Funcs::pass(tree), ctx); \ +#define CALL_PRE(member) \ + if constexpr (Funcs::template HAS_MEMBER_preTransform##member()) { \ + if constexpr (Kind == TreeMapKind::Map) { \ + v = Funcs::template CALL_MEMBER_preTransform##member::call(func, ctx, Funcs::pass(v)); \ + } else if constexpr (Kind == TreeMapKind::Walk) { \ + Funcs::template CALL_MEMBER_preTransform##member::call(func, ctx, Funcs::pass(v)); \ + } else if constexpr (Kind == TreeMapKind::ConstWalk) { \ + Funcs::template CALL_MEMBER_preTransform##member::call(func, ctx, cast_tree_nonnull(v)); \ + } \ + } + +#define CALL_POST(member) \ + if constexpr (Kind == TreeMapKind::Map) { \ + if constexpr (Funcs::template HAS_MEMBER_postTransform##member()) { \ + return Funcs::template CALL_MEMBER_postTransform##member::call(func, ctx, Funcs::pass(v)); \ + } \ + return v; \ + } else if constexpr (Kind == TreeMapKind::Walk) { \ + if constexpr (Funcs::template HAS_MEMBER_postTransform##member()) { \ + Funcs::template CALL_MEMBER_postTransform##member::call(func, ctx, Funcs::pass(v)); \ + } \ + } else if constexpr (Kind == TreeMapKind::ConstWalk) { \ + if constexpr (Funcs::template HAS_MEMBER_postTransform##member()) { \ + Funcs::template CALL_MEMBER_postTransform##member::call(func, ctx, cast_tree_nonnull(v)); \ + } \ + } + +#define CALL_MAP(tree, ctx) \ + if constexpr (Kind == TreeMapKind::Map) { \ + tree = mapIt(Funcs::pass(tree), ctx); \ + } else if constexpr (Kind == TreeMapKind::Walk) { \ + mapIt(Funcs::pass(tree), ctx); \ + } else if constexpr (Kind == TreeMapKind::ConstWalk) { \ + mapIt(Funcs::pass(tree), ctx); \ } return_type mapClassDef(arg_type v, CTX ctx) { @@ -340,6 +370,8 @@ template c el = mapRescueCase(Funcs::pass(el), ctx); } else if constexpr (Kind == TreeMapKind::Walk) { mapRescueCase(Funcs::pass(el), ctx); + } else if constexpr (Kind == TreeMapKind::ConstWalk) { + mapRescueCase(Funcs::pass(el), ctx); } ENFORCE(isa_tree(el), "rescue case was mapped into non-rescue case"); } @@ -466,16 +498,21 @@ template c return what; } else if constexpr (Kind == TreeMapKind::Walk) { return; + } else if constexpr (Kind == TreeMapKind::ConstWalk) { + return; } } try { // TODO: reorder by frequency - if constexpr (Funcs::template HAS_MEMBER_preTransformExpression()) { + if constexpr (Funcs::template HAS_MEMBER_preTransformExpressionPtr()) { if constexpr (Kind == TreeMapKind::Map) { - what = Funcs::template CALL_MEMBER_preTransformExpression::call(func, ctx, Funcs::pass(what)); + what = + Funcs::template CALL_MEMBER_preTransformExpressionPtr::call(func, ctx, Funcs::pass(what)); } else if constexpr (Kind == TreeMapKind::Walk) { - Funcs::template CALL_MEMBER_preTransformExpression::call(func, ctx, Funcs::pass(what)); + Funcs::template CALL_MEMBER_preTransformExpressionPtr::call(func, ctx, Funcs::pass(what)); + } else if constexpr (Kind == TreeMapKind::ConstWalk) { + Funcs::template CALL_MEMBER_preTransformExpressionPtr::call(func, ctx, Funcs::pass(what)); } } @@ -485,6 +522,8 @@ template c return what; } else if constexpr (Kind == TreeMapKind::Walk) { return; + } else if constexpr (Kind == TreeMapKind::ConstWalk) { + return; } case Tag::Send: @@ -573,6 +612,8 @@ template c return what; } else if constexpr (Kind == TreeMapKind::Walk) { return; + } else if constexpr (Kind == TreeMapKind::ConstWalk) { + return; } case Tag::Block: @@ -630,6 +671,22 @@ class TreeWalk { } }; +class ConstTreeWalk { +public: + template static void apply(CTX ctx, FUNC &func, const ExpressionPtr &to) { + TreeMapper walker(func); + try { + walker.mapIt(to, ctx); + } catch (ReportedRubyException &exception) { + Exception::failInFuzzer(); + if (auto e = ctx.beginError(exception.onLoc, core::errors::Internal::InternalError)) { + e.setHeader("Failed to process tree (backtrace is above)"); + } + throw exception.reported; + } + } +}; + class ShallowMap { public: template static ExpressionPtr apply(CTX ctx, FUNC &func, ExpressionPtr to) { @@ -662,5 +719,21 @@ class ShallowWalk { } }; +class ConstShallowWalk { +public: + template static void apply(CTX ctx, FUNC &func, const ExpressionPtr &to) { + TreeMapper walker(func); + try { + walker.mapIt(to, ctx); + } catch (ReportedRubyException &exception) { + Exception::failInFuzzer(); + if (auto e = ctx.beginError(exception.onLoc, core::errors::Internal::InternalError)) { + e.setHeader("Failed to process tree (backtrace is above)"); + } + throw exception.reported; + } + } +}; + } // namespace sorbet::ast #endif // SORBET_TREEMAP_H diff --git a/ast/verifier/Verifier.cc b/ast/verifier/Verifier.cc index a355414c6a..b36ad787ed 100644 --- a/ast/verifier/Verifier.cc +++ b/ast/verifier/Verifier.cc @@ -8,7 +8,7 @@ class VerifierWalker { uint32_t methodDepth = 0; public: - void preTransformExpression(core::Context ctx, ExpressionPtr &original) { + void preTransformExpressionPtr(core::Context ctx, const ExpressionPtr &original) { if (!isa_tree(original)) { ENFORCE(original.loc().exists(), "location is unset"); } @@ -16,17 +16,16 @@ class VerifierWalker { original._sanityCheck(); } - void preTransformMethodDef(core::Context ctx, ExpressionPtr &original) { + void preTransformMethodDef(core::Context ctx, const MethodDef &original) { methodDepth++; } - void postTransformMethodDef(core::Context ctx, ExpressionPtr &original) { + void postTransformMethodDef(core::Context ctx, const MethodDef &original) { methodDepth--; } - void postTransformAssign(core::Context ctx, ExpressionPtr &original) { - auto *assign = cast_tree(original); - if (ast::isa_tree(assign->lhs)) { + void postTransformAssign(core::Context ctx, const Assign &assign) { + if (ast::isa_tree(assign.lhs)) { ENFORCE(methodDepth == 0, "Found constant definition inside method definition"); } } @@ -41,7 +40,7 @@ ExpressionPtr Verifier::run(core::Context ctx, ExpressionPtr node) { return node; } VerifierWalker vw; - TreeWalk::apply(ctx, vw, node); + ConstTreeWalk::apply(ctx, vw, node); return node; } diff --git a/bazel b/bazel index fc59b5698d..c7d7adce3c 100755 --- a/bazel +++ b/bazel @@ -28,15 +28,8 @@ fi cd "$old_pwd" -workspace_contents=$(< "$repo_root/WORKSPACE") - -bazel_version_regex='BAZEL_VERSION = "([^"]+)"' -if [[ $workspace_contents =~ $bazel_version_regex ]]; then - export BAZEL_VERSION="${BASH_REMATCH[1]}" -else - echo >&2 "$0: Failed to extract BAZEL_VERSION from WORKSPACE" - exit 1 -fi +BAZEL_VERSION=$(< "$repo_root/.bazelversion") +export BAZEL_VERSION bazel_bin_loc="${bazel_bin_loc:-$HOME/.bazel_binaries}" bazel_exec_path="$bazel_bin_loc/$BAZEL_VERSION/bin/bazel-real" @@ -61,7 +54,7 @@ case "$bazel_installer_platform" in darwin-x86_64) ;; darwin-arm64) # Pseudo Apple Silicon support by forcing x86_64 (Rosetta) for now - bazel_installer_platform="darwin-x86_64" + # bazel_installer_platform="darwin-x86_64" ;; *) echo >&2 "Building on $bazel_installer_platform is not implemented" @@ -69,6 +62,8 @@ case "$bazel_installer_platform" in ;; esac +workspace_contents=$(< "$repo_root/WORKSPACE") + bazel_installer_platform_var="$(echo "$bazel_installer_platform" | tr 'a-z-' 'A-Z_')" bazel_installer_sha_variable="BAZEL_INSTALLER_VERSION_${bazel_installer_platform_var}_SHA" bazel_installer_sha_regex="$bazel_installer_sha_variable = \"([^\"]+)\"" diff --git a/cfg/CFG.cc b/cfg/CFG.cc index 62d6cfb37f..18f986b128 100644 --- a/cfg/CFG.cc +++ b/cfg/CFG.cc @@ -31,7 +31,12 @@ int CFG::numLocalVariables() const { return this->localVariables.size(); } -BasicBlock *CFG::freshBlock(int outerLoops, int rubyRegionId) { +BasicBlock *CFG::freshBlock(int outerLoops, BasicBlock *current) { + ENFORCE(current != nullptr); + return this->freshBlockWithRegion(outerLoops, current->rubyRegionId); +} + +BasicBlock *CFG::freshBlockWithRegion(int outerLoops, int rubyRegionId) { int id = this->maxBasicBlockId++; auto &r = this->basicBlocks.emplace_back(make_unique()); r->id = id; @@ -64,8 +69,8 @@ LocalRef CFG::enterLocal(core::LocalVariable variable) { } CFG::CFG() { - freshBlock(0, 0); // entry; - freshBlock(0, 0); // dead code; + freshBlockWithRegion(0, 0); // entry; + freshBlockWithRegion(0, 0); // dead code; deadBlock()->bexit.elseb = deadBlock(); deadBlock()->bexit.thenb = deadBlock(); deadBlock()->bexit.cond.variable = LocalRef::unconditional(); @@ -217,7 +222,7 @@ string CFG::toString(const core::GlobalState &gs) const { auto text = basicBlock->toString(gs, *this); auto lines = absl::StrSplit(text, "\n"); - auto shape = basicBlock->id == 0 ? "invhouse" : basicBlock->id == 1 ? "parallelogram" : "rectangle"; + auto shape = basicBlock->id == 0 ? "cds" : basicBlock->id == 1 ? "parallelogram" : "rectangle"; // whole block red if whole block is dead auto color = basicBlock->firstDeadInstructionIdx == 0 ? "red" : "black"; fmt::format_to(std::back_inserter(buf), @@ -282,7 +287,7 @@ string CFG::showRaw(core::Context ctx) const { auto text = basicBlock->showRaw(ctx, *this); auto lines = absl::StrSplit(text, "\n"); - auto shape = basicBlock->id == 0 ? "invhouse" : basicBlock->id == 1 ? "parallelogram" : "rectangle"; + auto shape = basicBlock->id == 0 ? "cds" : basicBlock->id == 1 ? "parallelogram" : "rectangle"; // whole block red if whole block is dead auto color = basicBlock->firstDeadInstructionIdx == 0 ? "red" : "black"; fmt::format_to(std::back_inserter(buf), @@ -348,7 +353,7 @@ optional BasicBlock::maybeGetUpdateKnowledgeRecei if (send->fun == core::Names::bang() && this->exprs.size() >= 2) { // Heuristic, because it's overwhelmingly common to see `!x.foo.nil?`, in which case the - // relevent receiver is the second-last, not the last, send binding. + // relevant receiver is the second-last, not the last, send binding. auto bangRecv = send->recv.variable; auto &secondLastBinding = this->exprs[this->exprs.size() - 2]; auto secondLastSend = looksLikeUpdateKnowledgeSend(inWhat, secondLastBinding, bangRecv); @@ -367,7 +372,7 @@ string BasicBlock::toString(const core::GlobalState &gs, const CFG &cfg) const { fmt::memory_buffer buf; fmt::format_to(std::back_inserter(buf), "block[id={}, rubyRegionId={}]({})\n", this->id, this->rubyRegionId, fmt::map_join( - this->args, ", ", [&](const auto &arg) -> auto { return arg.toString(gs, cfg); })); + this->args, ", ", [&](const auto &arg) -> auto{ return arg.toString(gs, cfg); })); if (this->outerLoops > 0) { fmt::format_to(std::back_inserter(buf), "outerLoops: {}\n", this->outerLoops); @@ -384,7 +389,7 @@ string BasicBlock::toTextualString(const core::GlobalState &gs, optionalid, this->rubyRegionId, this->firstDeadInstructionIdx, fmt::map_join( - this->args, ", ", [&](const auto &arg) -> auto { return arg.toString(gs, cfg); })); + this->args, ", ", [&](const auto &arg) -> auto{ return arg.toString(gs, cfg); })); if (this->outerLoops > 0) { fmt::format_to(std::back_inserter(buf), " # outerLoops: {}\n", this->outerLoops); @@ -421,7 +426,7 @@ string BasicBlock::showRaw(const core::GlobalState &gs, const CFG &cfg) const { fmt::memory_buffer buf; fmt::format_to(std::back_inserter(buf), "block[id={}]({})\n", this->id, fmt::map_join( - this->args, ", ", [&](const auto &arg) -> auto { return arg.showRaw(gs, cfg); })); + this->args, ", ", [&](const auto &arg) -> auto{ return arg.showRaw(gs, cfg); })); if (this->outerLoops > 0) { fmt::format_to(std::back_inserter(buf), "outerLoops: {}\n", this->outerLoops); diff --git a/cfg/CFG.h b/cfg/CFG.h index a782238884..72fa0a6746 100644 --- a/cfg/CFG.h +++ b/cfg/CFG.h @@ -68,7 +68,13 @@ class BasicBlock final { int id = 0; int fwdId = -1; int bwdId = -1; - int flags = 0; + struct Flags { + bool isLoopHeader : 1; + bool wasJumpDestination : 1; + // In C++20 we can replace this with bit field initializers + Flags() : isLoopHeader(false), wasJumpDestination(false) {} + }; + Flags flags; int outerLoops = 0; // Tracks which Ruby block (do ... end) or Ruby exception-handling region // (in begin ... rescue ... else ... ensure ... end, each `...` is its own @@ -97,7 +103,7 @@ class BasicBlock final { // updateKnowledge is aware of, return information about the receiver of that `Send`. // // Should only be used to *improve* existing error messages, not as a reliable source of truth, - // because the implemention is only a heuristic, and may fail to find a receiver even when it + // because the implementation is only a heuristic, and may fail to find a receiver even when it // might be otherwise expected to. // // More specifically, Sorbet's CFG is not SSA, so finding the `Send` that computes the variable @@ -172,10 +178,6 @@ class CFG final { // Verbose debug output std::string showRaw(core::Context ctx) const; - // Flags - static constexpr int LOOP_HEADER = 1 << 0; - static constexpr int WAS_JUMP_DESTINATION = 1 << 1; - // special minLoops static constexpr int MIN_LOOP_FIELD = -1; // static constexpr int MIN_LOOP_GLOBAL = -2; @@ -207,7 +209,8 @@ class CFG final { private: CFG(); - BasicBlock *freshBlock(int outerLoops, int rubyBlockid); + BasicBlock *freshBlock(int outerLoops, BasicBlock *current); + BasicBlock *freshBlockWithRegion(int outerLoops, int rubyRegionId); void enterLocalInternal(core::LocalVariable variable, LocalRef &ref); std::vector minLoops; std::vector maxLoopWrite; diff --git a/cfg/Instructions.h b/cfg/Instructions.h index f820264006..55be754075 100644 --- a/cfg/Instructions.h +++ b/cfg/Instructions.h @@ -98,9 +98,11 @@ class Instruction { friend InstructionPtr; }; -#define INSN(name) \ - class name; \ - template <> struct InsnToTag { static constexpr Tag value = Tag::name; }; \ +#define INSN(name) \ + class name; \ + template <> struct InsnToTag { \ + static constexpr Tag value = Tag::name; \ + }; \ class __attribute__((aligned(8))) name final INSN(Ident) : public Instruction { diff --git a/cfg/builder/builder_finalize.cc b/cfg/builder/builder_finalize.cc index 468c54628b..840201bf96 100644 --- a/cfg/builder/builder_finalize.cc +++ b/cfg/builder/builder_finalize.cc @@ -11,10 +11,6 @@ using namespace std; namespace sorbet::cfg { void CFGBuilder::simplify(core::Context ctx, CFG &cfg) { - if (!ctx.state.lspQuery.isEmpty()) { - return; - } - sanityCheck(ctx, cfg); bool changed = true; while (changed) { @@ -50,7 +46,7 @@ void CFGBuilder::simplify(core::Context ctx, CFG &cfg) { bb->bexit.cond = LocalOccurrence{LocalRef::unconditional(), bb->bexit.loc}; // TODO(varun): right loc? } if (thenb == elseb && thenb != cfg.deadBlock() && thenb != bb && - bb->rubyRegionId == thenb->rubyRegionId) { // can be squashed togather + bb->rubyRegionId == thenb->rubyRegionId) { // can be squashed together if (thenb->backEdges.size() == 1 && thenb->outerLoops == bb->outerLoops) { bb->exprs.insert(bb->exprs.end(), make_move_iterator(thenb->exprs.begin()), make_move_iterator(thenb->exprs.end())); @@ -124,7 +120,7 @@ void CFGBuilder::sanityCheck(core::Context ctx, CFG &cfg) { continue; } if (bb.get() != cfg.entry()) { - ENFORCE((bb->flags & CFG::WAS_JUMP_DESTINATION) != 0, "block {} was never linked into cfg", bb->id); + ENFORCE(bb->flags.wasJumpDestination, "block {} was never linked into cfg", bb->id); } auto thenFnd = absl::c_find(bb->bexit.thenb->backEdges, bb.get()); auto elseFnd = absl::c_find(bb->bexit.elseb->backEdges, bb.get()); @@ -242,7 +238,7 @@ void CFGBuilder::markLoopHeaders(core::Context ctx, CFG &cfg) { for (unique_ptr &bb : cfg.basicBlocks) { for (auto *parent : bb->backEdges) { if (parent->outerLoops < bb->outerLoops) { - bb->flags |= CFG::LOOP_HEADER; + bb->flags.isLoopHeader = true; continue; } } diff --git a/cfg/builder/builder_walk.cc b/cfg/builder/builder_walk.cc index e2ad3cfc5f..7081f0711d 100644 --- a/cfg/builder/builder_walk.cc +++ b/cfg/builder/builder_walk.cc @@ -12,8 +12,8 @@ namespace sorbet::cfg { void CFGBuilder::conditionalJump(BasicBlock *from, LocalRef cond, BasicBlock *thenb, BasicBlock *elseb, CFG &inWhat, core::LocOffsets loc) { - thenb->flags |= CFG::WAS_JUMP_DESTINATION; - elseb->flags |= CFG::WAS_JUMP_DESTINATION; + thenb->flags.wasJumpDestination = true; + elseb->flags.wasJumpDestination = true; if (from != inWhat.deadBlock()) { ENFORCE(!from->bexit.isCondSet(), "condition for block already set"); ENFORCE(from->bexit.thenb == nullptr, "thenb already set"); @@ -28,7 +28,7 @@ void CFGBuilder::conditionalJump(BasicBlock *from, LocalRef cond, BasicBlock *th } void CFGBuilder::unconditionalJump(BasicBlock *from, BasicBlock *to, CFG &inWhat, core::LocOffsets loc) { - to->flags |= CFG::WAS_JUMP_DESTINATION; + to->flags.wasJumpDestination = true; if (from != inWhat.deadBlock()) { ENFORCE(!from->bexit.isCondSet(), "condition for block already set"); ENFORCE(from->bexit.thenb == nullptr, "thenb already set"); @@ -170,6 +170,21 @@ InstructionPtr maybeMakeTypeParameterAlias(CFGContext &cctx, ast::Send &s) { return make_insn(typeParam); } +ast::Send *isKernelProcOrLambda(ast::ExpressionPtr &expr) { + auto *send = ast::cast_tree(expr); + if (send == nullptr || send->hasNonBlockArgs() || + (send->fun != core::Names::lambda() && send->fun != core::Names::proc())) { + return nullptr; + } + + auto *recv = ast::cast_tree(send->recv); + if (recv == nullptr || recv->symbol != core::Symbols::Kernel()) { + return nullptr; + } + + return send; +} + } // namespace void CFGBuilder::jumpToDead(BasicBlock *from, CFG &inWhat, core::LocOffsets loc) { @@ -219,7 +234,7 @@ BasicBlock *CFGBuilder::walkHash(CFGContext cctx, ast::Hash &h, BasicBlock *curr } BasicBlock *CFGBuilder::joinBlocks(CFGContext cctx, BasicBlock *a, BasicBlock *b) { - auto *join = cctx.inWhat.freshBlock(cctx.loops, a->rubyRegionId); + auto *join = cctx.inWhat.freshBlock(cctx.loops, a); unconditionalJump(a, join, cctx.inWhat, core::LocOffsets::none()); unconditionalJump(b, join, cctx.inWhat, core::LocOffsets::none()); return join; @@ -231,8 +246,8 @@ tuple CFGBuilder::walkDefault(CFGContext c BasicBlock *presentCont, BasicBlock *defaultCont) { auto defLoc = def.loc(); - auto *presentNext = cctx.inWhat.freshBlock(cctx.loops, presentCont->rubyRegionId); - auto *defaultNext = cctx.inWhat.freshBlock(cctx.loops, presentCont->rubyRegionId); + auto *presentNext = cctx.inWhat.freshBlock(cctx.loops, presentCont); + auto *defaultNext = cctx.inWhat.freshBlock(cctx.loops, presentCont); auto present = cctx.newTemporary(core::Names::argPresent()); auto methodSymbol = cctx.inWhat.symbol; @@ -274,18 +289,18 @@ BasicBlock *CFGBuilder::walk(CFGContext cctx, ast::ExpressionPtr &what, BasicBlo typecase( what, [&](ast::While &a) { - auto headerBlock = cctx.inWhat.freshBlock(cctx.loops + 1, current->rubyRegionId); + auto headerBlock = cctx.inWhat.freshBlock(cctx.loops + 1, current); // breakNotCalledBlock is only entered if break is not called in // the loop body - auto breakNotCalledBlock = cctx.inWhat.freshBlock(cctx.loops, current->rubyRegionId); - auto continueBlock = cctx.inWhat.freshBlock(cctx.loops, current->rubyRegionId); + auto breakNotCalledBlock = cctx.inWhat.freshBlock(cctx.loops, current); + auto continueBlock = cctx.inWhat.freshBlock(cctx.loops, current); unconditionalJump(current, headerBlock, cctx.inWhat, a.loc); LocalRef condSym = cctx.newTemporary(core::Names::whileTemp()); auto headerEnd = walk(cctx.withTarget(LocalOccurrence::synthetic(condSym)).withLoopScope(headerBlock, continueBlock), a.cond, headerBlock); - auto bodyBlock = cctx.inWhat.freshBlock(cctx.loops + 1, current->rubyRegionId); + auto bodyBlock = cctx.inWhat.freshBlock(cctx.loops + 1, current); conditionalJump(headerEnd, condSym, bodyBlock, breakNotCalledBlock, cctx.inWhat, a.cond.loc()); // finishHeader LocalRef bodySym = cctx.newTemporary(core::Names::statTemp()); @@ -332,8 +347,8 @@ BasicBlock *CFGBuilder::walk(CFGContext cctx, ast::ExpressionPtr &what, BasicBlo LocalRef ifSym = cctx.newTemporary(core::Names::ifTemp()); ENFORCE(ifSym.exists(), "ifSym does not exist"); auto cont = walk(cctx.withTarget(LocalOccurrence::synthetic(ifSym)), a.cond, current); - auto thenBlock = cctx.inWhat.freshBlock(cctx.loops, current->rubyRegionId); - auto elseBlock = cctx.inWhat.freshBlock(cctx.loops, current->rubyRegionId); + auto thenBlock = cctx.inWhat.freshBlock(cctx.loops, current); + auto elseBlock = cctx.inWhat.freshBlock(cctx.loops, current); conditionalJump(cont, ifSym, thenBlock, elseBlock, cctx.inWhat, a.cond.loc()); auto thenEnd = walk(cctx, a.thenp, thenBlock); @@ -344,7 +359,7 @@ BasicBlock *CFGBuilder::walk(CFGContext cctx, ast::ExpressionPtr &what, BasicBlo } else if (elseEnd == cctx.inWhat.deadBlock()) { ret = thenEnd; } else { - ret = cctx.inWhat.freshBlock(cctx.loops, current->rubyRegionId); + ret = cctx.inWhat.freshBlock(cctx.loops, current); unconditionalJump(thenEnd, ret, cctx.inWhat, a.loc); unconditionalJump(elseEnd, ret, cctx.inWhat, a.loc); } @@ -590,13 +605,13 @@ BasicBlock *CFGBuilder::walk(CFGContext cctx, ast::ExpressionPtr &what, BasicBlo synthesizeExpr(current, restoreSelf, core::LocOffsets::none(), make_insn(LocalRef::selfVariable())); - auto headerBlock = cctx.inWhat.freshBlock(cctx.loops + 1, newRubyRegionId); + auto headerBlock = cctx.inWhat.freshBlockWithRegion(cctx.loops + 1, newRubyRegionId); // solveConstraintBlock is only entered if break is not called // in the block body. - auto solveConstraintBlock = cctx.inWhat.freshBlock(cctx.loops, current->rubyRegionId); - auto postBlock = cctx.inWhat.freshBlock(cctx.loops, current->rubyRegionId); + auto solveConstraintBlock = cctx.inWhat.freshBlock(cctx.loops, current); + auto postBlock = cctx.inWhat.freshBlock(cctx.loops, current); auto bodyLoops = cctx.loops + 1; - auto bodyBlock = cctx.inWhat.freshBlock(bodyLoops, newRubyRegionId); + auto bodyBlock = cctx.inWhat.freshBlockWithRegion(bodyLoops, newRubyRegionId); bodyBlock->exprs.emplace_back(LocalOccurrence::synthetic(LocalRef::selfVariable()), s.loc, make_insn(link, LocalRef::selfVariable())); @@ -620,8 +635,8 @@ BasicBlock *CFGBuilder::walk(CFGContext cctx, ast::ExpressionPtr &what, BasicBlo } if (auto *opt = ast::cast_tree(blockArgs[i])) { - auto *presentBlock = cctx.inWhat.freshBlock(bodyLoops, newRubyRegionId); - auto *missingBlock = cctx.inWhat.freshBlock(bodyLoops, newRubyRegionId); + auto *presentBlock = cctx.inWhat.freshBlockWithRegion(bodyLoops, newRubyRegionId); + auto *missingBlock = cctx.inWhat.freshBlockWithRegion(bodyLoops, newRubyRegionId); // add a test for YieldParamPresent auto present = cctx.newTemporary(core::Names::argPresent()); @@ -630,7 +645,7 @@ BasicBlock *CFGBuilder::walk(CFGContext cctx, ast::ExpressionPtr &what, BasicBlo conditionalJump(argBlock, present, presentBlock, missingBlock, cctx.inWhat, arg.loc); // make a new block for the present and missing blocks to join - argBlock = cctx.inWhat.freshBlock(bodyLoops, newRubyRegionId); + argBlock = cctx.inWhat.freshBlockWithRegion(bodyLoops, newRubyRegionId); // compile the argument fetch in the present block presentBlock->exprs.emplace_back(argLoc, arg.loc, @@ -824,7 +839,7 @@ BasicBlock *CFGBuilder::walk(CFGContext cctx, ast::ExpressionPtr &what, BasicBlo auto elseRubyRegionId = bodyRubyRegionId + CFG::ELSE_REGION_OFFSET; cctx.inWhat.maxRubyRegionId = elseRubyRegionId; - auto rescueHeaderBlock = cctx.inWhat.freshBlock(cctx.loops, current->rubyRegionId); + auto rescueHeaderBlock = cctx.inWhat.freshBlock(cctx.loops, current); unconditionalJump(current, rescueHeaderBlock, cctx.inWhat, a.loc); cctx.rescueScope = rescueHeaderBlock; @@ -836,8 +851,8 @@ BasicBlock *CFGBuilder::walk(CFGContext cctx, ast::ExpressionPtr &what, BasicBlo // Unanalyzable variable at the top of the body using // `exceptionValue` and one at the end of the else using // `rescueEndTemp` which can jump into the rescue handlers. - auto rescueHandlersBlock = cctx.inWhat.freshBlock(cctx.loops, handlersRubyRegionId); - auto bodyBlock = cctx.inWhat.freshBlock(cctx.loops, bodyRubyRegionId); + auto rescueHandlersBlock = cctx.inWhat.freshBlockWithRegion(cctx.loops, handlersRubyRegionId); + auto bodyBlock = cctx.inWhat.freshBlockWithRegion(cctx.loops, bodyRubyRegionId); auto exceptionValue = cctx.newTemporary(core::Names::exceptionValue()); // In `rescue; ...; end`, we don't want the conditional jumps' variables nor the // GetCurrentException calls to look like they blame to the whole `rescue; ...; end` @@ -858,13 +873,13 @@ BasicBlock *CFGBuilder::walk(CFGContext cctx, ast::ExpressionPtr &what, BasicBlo bodyBlock = walk(cctx, a.body, bodyBlock); // else is only executed if body didn't raise an exception - auto elseBody = cctx.inWhat.freshBlock(cctx.loops, elseRubyRegionId); + auto elseBody = cctx.inWhat.freshBlockWithRegion(cctx.loops, elseRubyRegionId); synthesizeExpr(bodyBlock, exceptionValue, rescueKeywordLoc, make_insn()); conditionalJump(bodyBlock, exceptionValue, rescueHandlersBlock, elseBody, cctx.inWhat, rescueKeywordLoc); elseBody = walk(cctx, a.else_, elseBody); - auto ensureBody = cctx.inWhat.freshBlock(cctx.loops, ensureRubyRegionId); + auto ensureBody = cctx.inWhat.freshBlockWithRegion(cctx.loops, ensureRubyRegionId); unconditionalJump(elseBody, ensureBody, cctx.inWhat, a.loc); auto magic = cctx.newTemporary(core::Names::magic()); @@ -872,15 +887,26 @@ BasicBlock *CFGBuilder::walk(CFGContext cctx, ast::ExpressionPtr &what, BasicBlo for (auto &expr : a.rescueCases) { auto *rescueCase = ast::cast_tree(expr); - auto caseBody = cctx.inWhat.freshBlock(cctx.loops, handlersRubyRegionId); + auto caseBody = cctx.inWhat.freshBlockWithRegion(cctx.loops, handlersRubyRegionId); auto &exceptions = rescueCase->exceptions; auto added = false; auto *local = ast::cast_tree(rescueCase->var); ENFORCE(local != nullptr, "rescue case var not a local?"); auto localVar = cctx.inWhat.enterLocal(local->localVariable); - rescueHandlersBlock->exprs.emplace_back(LocalOccurrence{localVar, local->loc}, - rescueCase->var.loc(), make_insn(exceptionValue)); + caseBody->exprs.emplace_back(LocalOccurrence{localVar, local->loc}, rescueCase->var.loc(), make_insn(exceptionValue)); + + // We don't support typed exceptions in `ensure` yet. + // We have a lot of tests that show why, but it boils down to a combination of + // Sorbet's "simplified view of the control flow" for exceptions (see comment above) + // and Sorbet's dead code detection. + if (!ast::isa_tree(a.ensure)) { + auto zloc = rescueCase->var.loc().copyWithZeroLength(); + auto unsafe = ast::MK::Unsafe( + zloc, ast::make_expression(zloc, exceptionValue.data(cctx.inWhat))); + ensureBody = walk(cctx.withTarget(LocalOccurrence{localVar, local->loc}), unsafe, ensureBody); + //CHECK:VARUN - ^Need LocalOccurrence above? + } // Mark the exception as handled synthesizeExpr(caseBody, exceptionValue, core::LocOffsets::none(), @@ -910,7 +936,7 @@ BasicBlock *CFGBuilder::walk(CFGContext cctx, ast::ExpressionPtr &what, BasicBlo auto isaCheck = cctx.newTemporary(core::Names::isaCheckTemp()); InlinedVector args; InlinedVector argLocs = {loc}; - args.emplace_back(localVar); + args.emplace_back(exceptionValue); auto isPrivateOk = false; rescueHandlersBlock->exprs.emplace_back( @@ -918,7 +944,7 @@ BasicBlock *CFGBuilder::walk(CFGContext cctx, ast::ExpressionPtr &what, BasicBlo make_insn(exceptionClass, loc, core::Names::tripleEq(), loc.copyWithZeroLength(), args.size(), args, std::move(argLocs), isPrivateOk)); - auto otherHandlerBlock = cctx.inWhat.freshBlock(cctx.loops, handlersRubyRegionId); + auto otherHandlerBlock = cctx.inWhat.freshBlockWithRegion(cctx.loops, handlersRubyRegionId); conditionalJump(rescueHandlersBlock, isaCheck, caseBody, otherHandlerBlock, cctx.inWhat, loc); rescueHandlersBlock = otherHandlerBlock; } @@ -939,7 +965,7 @@ BasicBlock *CFGBuilder::walk(CFGContext cctx, ast::ExpressionPtr &what, BasicBlo auto throwAway = cctx.newTemporary(core::Names::throwAwayTemp()); ensureBody = walk(cctx.withTarget(LocalOccurrence::synthetic(throwAway)), a.ensure, ensureBody); - ret = cctx.inWhat.freshBlock(cctx.loops, current->rubyRegionId); + ret = cctx.inWhat.freshBlock(cctx.loops, current); conditionalJump(ensureBody, gotoDeadTemp, cctx.inWhat.deadBlock(), ret, cctx.inWhat, a.loc); }, @@ -965,13 +991,18 @@ BasicBlock *CFGBuilder::walk(CFGContext cctx, ast::ExpressionPtr &what, BasicBlo }, [&](ast::Cast &c) { - // This is kind of gross, but it is the only way to ensure that the bits in the - // type expression make it into the CFG for LSP to hit on their locations. - LocalRef deadSym = cctx.newTemporary(core::Names::keepForIde()); - auto deadSymOcc = LocalOccurrence::synthetic(deadSym); - current = walk(cctx.withTarget(deadSymOcc), c.typeExpr, current); - // Ensure later passes don't delete the results of the typeExpr. - current->exprs.emplace_back(deadSymOcc, core::LocOffsets::none(), make_insn(deadSym)); + if (auto *kernelLambda = isKernelProcOrLambda(c.arg)) { + kernelLambda->fun = core::Names::lambdaTLet(); + kernelLambda->addPosArg(move(c.typeExpr)); + } else { + // This is kind of gross, but it is the only way to ensure that the bits in the + // type expression make it into the CFG for LSP to hit on their locations. + LocalRef deadSym = cctx.newTemporary(core::Names::keepForIde()); + auto deadSymOcc = LocalOccurrence::synthetic(deadSym); + current = walk(cctx.withTarget(deadSymOcc), c.typeExpr, current); + // Ensure later passes don't delete the results of the typeExpr. + current->exprs.emplace_back(deadSymOcc, core::LocOffsets::none(), make_insn(deadSym)); + } LocalRef tmp = cctx.newTemporary(core::Names::castTemp()); core::LocOffsets argLoc = c.arg.loc(); current = walk(cctx.withTarget(LocalOccurrence::synthetic(tmp)), c.arg, current); diff --git a/class_flatten/class_flatten.cc b/class_flatten/class_flatten.cc index df7c789176..be8f663f04 100644 --- a/class_flatten/class_flatten.cc +++ b/class_flatten/class_flatten.cc @@ -30,7 +30,7 @@ bool shouldExtract(core::Context ctx, const ast::ExpressionPtr &what) { } // pull all the non-definitions (i.e. anything that's not a method definition, a class definition, or a constant -// defintion) from a class or file into their own instruction sequence (or, if there is only one, simply move it out of +// definition) from a class or file into their own instruction sequence (or, if there is only one, simply move it out of // the class body and return it.) ast::ExpressionPtr extractClassInit(core::Context ctx, ast::ClassDef *klass) { ast::InsSeq::STATS_store inits; diff --git a/common/common.cc b/common/common.cc index 816160340a..96ea0b2189 100644 --- a/common/common.cc +++ b/common/common.cc @@ -1,4 +1,5 @@ #include "common/common.h" +#include "absl/strings/escaping.h" #include "common/FileOps.h" #include "common/concurrency/ConcurrentQueue.h" #include "common/concurrency/WorkerPool.h" @@ -324,7 +325,7 @@ void appendFilesInDir(string_view basePath, const string &path, const sorbet::Un if (!result.gotItem()) { continue; } - if (auto *token = std::get_if(&job)) { + if (std::holds_alternative(job)) { break; } @@ -419,6 +420,8 @@ void appendFilesInDir(string_view basePath, const string &path, const sorbet::Un { JobOutput threadResult; + optional fileNotFound; + optional fileNotDir; auto &logger = *spdlog::default_logger(); for (auto result = resultq->wait_pop_timed(threadResult, sorbet::WorkerPool::BLOCK_INTERVAL(), logger); !result.done(); @@ -431,14 +434,28 @@ void appendFilesInDir(string_view basePath, const string &path, const sorbet::Un } if (auto *e = std::get_if(&threadResult)) { - throw *e; + if (!fileNotFound.has_value()) { + fileNotFound = *e; + } } else if (auto *e = std::get_if(&threadResult)) { - throw *e; + if (!fileNotDir.has_value()) { + fileNotDir = *e; + } } else { ENFORCE(false, "should never get here!"); } } } + + // If there was an error, don't raise it until after all the worker threads have finished, + // because they might be working on something, attempting to write to the pendingJobs + // variable stored on our stack, and then suddenly see that stack address gone because we've + // raised and jumped elsewhere. + if (fileNotFound.has_value()) { + throw fileNotFound.value(); + } else if (fileNotDir.has_value()) { + throw fileNotDir.value(); + } } } @@ -492,7 +509,39 @@ vector sorbet::findLineBreaks(string_view s) { class SetTerminateHandler { public: static void on_terminate() { + auto eptr = current_exception(); + if (eptr) { + try { + // Apparently this is the only way to convert a std::exception_ptr to std::exception + std::rethrow_exception(eptr); + } catch (const std::exception &e) { + sorbet::fatalLogger->error("Sorbet raised uncaught exception type=\"{}\" what=\"{}\"", + demangle(typeid(e).name()), absl::CEscape(e.what())); + } catch (const std::string &s) { + sorbet::fatalLogger->error("Sorbet raised uncaught exception type=std::string what=\"{}\"", + absl::CEscape(s)); + } catch (const char *s) { + sorbet::fatalLogger->error("Sorbet raised uncaught exception type=\"char *\" what=\"{}\"", + absl::CEscape(s)); + } catch (...) { + sorbet::fatalLogger->error("Sorbet raised uncaught exception type= what=\"\""); + } + } else { + sorbet::fatalLogger->error("Sorbet raised uncaught exception type= what=\"\""); + } + + // Might print the backtrace twice if it's a SorbetException, because + // Exception::raise already prints the backtrace when the exception is raised. But + // SorbetException are caught at various places inside Sorbet, and so might not + // always terminate the process. sorbet::Exception::printBacktrace(); + + // "A std::terminate_handler shall terminate execution of the program without returning to + // the caller, otherwise the behavior is undefined." + // + // In libc++, the behavior is to print "terminate_handler unexpectedly returned" and then + // call abort() + abort(); } SetTerminateHandler() { diff --git a/common/counters/Counters.cc b/common/counters/Counters.cc index 7ad20c569b..038946ba92 100644 --- a/common/counters/Counters.cc +++ b/common/counters/Counters.cc @@ -151,6 +151,13 @@ void counterConsume(CounterState cs) { } } +CounterState mergeCounters(CounterState counters) { + if (!counters.hasNullCounters()) { + counterConsume(move(counters)); + } + return getAndClearThreadCounters(); +} + void counterAdd(ConstExprStr counter, unsigned long value) { if constexpr (enable_counters) { counterState.counterAdd(counter.str, value); @@ -417,7 +424,7 @@ string getCounterStatistics() { fmt::format_to(std::back_inserter(buf), "{}", fmt::map_join( - sortedTimings, "", [](const auto &el) -> auto { return el.second; })); + sortedTimings, "", [](const auto &el) -> auto{ return el.second; })); } { @@ -433,7 +440,7 @@ string getCounterStatistics() { fmt::format_to(std::back_inserter(buf), "{}", fmt::map_join( - sortedOther, "", [](const auto &el) -> auto { return el.second; })); + sortedOther, "", [](const auto &el) -> auto{ return el.second; })); } return to_string(buf); } diff --git a/common/counters/Counters.h b/common/counters/Counters.h index 6d61b2f3c5..eed4dde362 100644 --- a/common/counters/Counters.h +++ b/common/counters/Counters.h @@ -78,6 +78,7 @@ struct CounterState { CounterState getAndClearThreadCounters(); void counterConsume(CounterState cs); +CounterState mergeCounters(CounterState counters); void prodCounterInc(ConstExprStr counter); void prodCounterAdd(ConstExprStr counter, unsigned long value); diff --git a/common/crypto_hashing/BUILD b/common/crypto_hashing/BUILD index 0e7670aef4..c36bd0bca9 100644 --- a/common/crypto_hashing/BUILD +++ b/common/crypto_hashing/BUILD @@ -7,9 +7,8 @@ cc_library( }), visibility = ["//visibility:public"], deps = ["//common"] + select({ - "//tools/config:webasm": ["@com_github_blake2_blake2"], - "//tools/config:darwin_x86_64": ["@com_github_blake2_libb2"], - "//tools/config:linux_x86_64": ["@com_github_blake2_libb2"], - "//conditions:default": ["@com_github_blake2_blake2"], + "@platforms//cpu:x86_64": ["@com_github_blake2_libb2"], + "@platforms//cpu:arm64": ["@com_github_blake2_blake2//:com_github_blake2_blake2_neon"], + "//conditions:default": ["@com_github_blake2_blake2//:com_github_blake2_blake2_ref"], }), ) diff --git a/common/crypto_hashing/crypto_hashing.h b/common/crypto_hashing/crypto_hashing.h index da09d4e519..f81a71808a 100644 --- a/common/crypto_hashing/crypto_hashing.h +++ b/common/crypto_hashing/crypto_hashing.h @@ -5,6 +5,8 @@ extern "C" { #if defined(__i386__) || defined(__x86_64__) #include "blake2.h" +#elif defined(__aarch64__) +#include "neon/blake2.h" #else #include "ref/blake2.h" #endif diff --git a/common/has_member.h b/common/has_member.h index 6bd7f037ff..701ba5944e 100644 --- a/common/has_member.h +++ b/common/has_member.h @@ -23,9 +23,7 @@ template struct sfinae_true : std::true_type {}; template \ static constexpr auto __has_##name(int)->sfinae_true().name(arg_types))>; \ template static constexpr auto __has_##name(long)->std::false_type; \ - template static constexpr bool HAS_MEMBER_##name() { \ - return decltype(__has_##name(0)){}; \ - } + template static constexpr bool HAS_MEMBER_##name() { return decltype(__has_##name(0)){}; } /** * Given a method name, a default statement to run, and a list of argument types, do the following: @@ -39,27 +37,25 @@ template struct sfinae_true : std::true_type {}; * CALL_MEMBER_toString::call(name, gs); // calls name.toString(gs) * CALL_MEMBER_toString::call(10, gs); // returns "" */ -#define GENERATE_CALL_MEMBER(method_name, default_behavior, arg_types...) \ - GENERATE_HAS_MEMBER(method_name, arg_types) \ - template class CALL_MEMBER_impl_##method_name { \ - public: \ - template static decltype(auto) call(T &self, Args &&...args) { \ - Exception::raise("should never be called"); \ - }; \ - }; \ - template class CALL_MEMBER_impl_##method_name { \ - public: \ - template static decltype(auto) call(T &self, Args &&...args) { \ - return self.method_name(std::forward(args)...); \ - }; \ - }; \ - template class CALL_MEMBER_impl_##method_name { \ - public: \ - template static decltype(auto) call(T &self, Args &&...args) { \ - default_behavior; \ - }; \ - }; \ - template \ +#define GENERATE_CALL_MEMBER(method_name, default_behavior, arg_types...) \ + GENERATE_HAS_MEMBER(method_name, arg_types) \ + template class CALL_MEMBER_impl_##method_name { \ + public: \ + template static decltype(auto) call(T &self, Args &&...args) { \ + Exception::raise("should never be called"); \ + }; \ + }; \ + template class CALL_MEMBER_impl_##method_name { \ + public: \ + template static decltype(auto) call(T &self, Args &&...args) { \ + return self.method_name(std::forward(args)...); \ + }; \ + }; \ + template class CALL_MEMBER_impl_##method_name { \ + public: \ + template static decltype(auto) call(T &self, Args &&...args) { default_behavior; }; \ + }; \ + template \ class CALL_MEMBER_##method_name : public CALL_MEMBER_impl_##method_name()> {}; } // namespace sorbet diff --git a/common/kvstore/BUILD b/common/kvstore/BUILD index 11404227ca..9d06dd20c5 100644 --- a/common/kvstore/BUILD +++ b/common/kvstore/BUILD @@ -1,7 +1,7 @@ cc_library( name = "kvstore", srcs = select({ - "//tools/config:webasm": ["KeyValueStore-emscripten.cc"], + "@platforms//cpu:wasm32": ["KeyValueStore-emscripten.cc"], "//conditions:default": ["KeyValueStore.cc"], }), hdrs = [ @@ -21,7 +21,7 @@ cc_library( "@com_google_absl//absl/strings", "@com_google_absl//absl/synchronization", ] + select({ - "//tools/config:webasm": [], + "@platforms//cpu:wasm32": [], "//conditions:default": ["@lmdb"], }), ) diff --git a/common/kvstore/KeyValueStore.cc b/common/kvstore/KeyValueStore.cc index 928659a41b..b0cd1e425e 100644 --- a/common/kvstore/KeyValueStore.cc +++ b/common/kvstore/KeyValueStore.cc @@ -5,6 +5,10 @@ #include "lmdb.h" #include "spdlog/spdlog.h" +// TODO(jez) We do not use lmdb when building for emscripten, so we're guaranteed that the pinned +// LLVM we compile Sorbet with has a libc++ version high enough to use `` (from C++17) +#include + #include using namespace std; @@ -100,11 +104,24 @@ KeyValueStore::KeyValueStore(shared_ptr logger, string version, // Avoids MDB_READERS_FULL issues with concurrent Sorbet processes. rc = mdb_env_open(dbState->env, this->path.c_str(), MDB_NOTLS, 0664); if (rc == ENOENT) { - fmt::print(stderr, "'{}' does not exist. When using --cache-dir, create the directory before hand.\n", - this->path); + try { + filesystem::create_directories(this->path); + } catch (filesystem::filesystem_error &e) { + fmt::print(stderr, + "'{}' does not exist and could not be created. " + "When using --cache-dir, create the directory before hand.\n", + this->path); + throw EarlyReturnWithCode(1); + } + rc = mdb_env_open(dbState->env, this->path.c_str(), MDB_NOTLS, 0664); + if (rc != 0) { + goto fail; + } + } else if (rc == ENOTDIR) { + fmt::print(stderr, "'{}' is not a directory, and so cannot store the Sorbet disk cache.\n", this->path); throw EarlyReturnWithCode(1); } else if (rc == EACCES) { - fmt::print(stderr, "No read permissions for '{}'", this->path); + fmt::print(stderr, "No read permissions for '{}'\n", this->path); throw EarlyReturnWithCode(1); } else if (rc != 0) { goto fail; diff --git a/common/os/BUILD b/common/os/BUILD index 5c3af96263..694023c4e6 100644 --- a/common/os/BUILD +++ b/common/os/BUILD @@ -5,7 +5,7 @@ cc_library( "os.cc", ] + select({ "@platforms//os:osx": ["mac.cc"], - "//tools/config:webasm": ["emscripten.cc"], + "@platforms//cpu:wasm32": ["emscripten.cc"], "//conditions:default": ["linux.cc"], }), hdrs = [ diff --git a/common/os/emscripten.cc b/common/os/emscripten.cc index 31c97a3300..b25b93958e 100644 --- a/common/os/emscripten.cc +++ b/common/os/emscripten.cc @@ -20,8 +20,6 @@ void initializeSymbolizer(char *argv0) { // Our version of emscripten doesn't provide the offset converter that the debugging library // from absl is looking for, so we can't call absl::InitializeSymbolizer here, otherwise it will // log an INFO message to stdout when it starts up suggesting how to work around this. - // - // TODO(trevor) We can uncomment this by upgrading emscripten at some point in the future // absl::InitializeSymbolizer(argv0); } diff --git a/common/statsd/BUILD b/common/statsd/BUILD index 00398d1c68..4ecc41f487 100644 --- a/common/statsd/BUILD +++ b/common/statsd/BUILD @@ -1,7 +1,7 @@ cc_library( name = "statsd", srcs = select({ - "//tools/config:webasm": ["statsd-emscripten.cc"], + "@platforms//cpu:wasm32": ["statsd-emscripten.cc"], "//conditions:default": ["statsd.cc"], }), hdrs = [ @@ -17,7 +17,7 @@ cc_library( "//core", "//sorbet_version", ] + select({ - "//tools/config:webasm": [], + "@platforms//cpu:wasm32": [], "//conditions:default": ["@statsd"], }), ) diff --git a/common/timers/Timer.cc b/common/timers/Timer.cc index cde11a3405..4572e8a61d 100644 --- a/common/timers/Timer.cc +++ b/common/timers/Timer.cc @@ -5,7 +5,7 @@ namespace sorbet { namespace { // We are using instead of similar APIs from the C++ library, -// because this was measured to make timers noticably faster. +// because this was measured to make timers noticeably faster. // // https://stackoverflow.com/questions/48609413/fastest-way-to-get-a-timestamp clockid_t clock_monotonic_coarse() { diff --git a/common/typecase.h b/common/typecase.h index 4e66ae1892..cd0a44458f 100644 --- a/common/typecase.h +++ b/common/typecase.h @@ -14,9 +14,15 @@ namespace sorbet { // Begin ecatmur's code template struct remove_class {}; -template struct remove_class { using type = R(A...); }; -template struct remove_class { using type = R(A...); }; -template struct remove_class { using type = R(A...); }; +template struct remove_class { + using type = R(A...); +}; +template struct remove_class { + using type = R(A...); +}; +template struct remove_class { + using type = R(A...); +}; template struct remove_class { using type = R(A...); }; @@ -24,9 +30,15 @@ template struct remove_class struct get_signature_impl { using type = typename remove_class::type::operator())>::type; }; -template struct get_signature_impl { using type = R(A...); }; -template struct get_signature_impl { using type = R(A...); }; -template struct get_signature_impl { using type = R(A...); }; +template struct get_signature_impl { + using type = R(A...); +}; +template struct get_signature_impl { + using type = R(A...); +}; +template struct get_signature_impl { + using type = R(A...); +}; template using get_signature = typename get_signature_impl::type; // End ecatmur's code diff --git a/common/web_tracer_framework/tracing.cc b/common/web_tracer_framework/tracing.cc index 1cbcc6805e..5fb1308ed8 100644 --- a/common/web_tracer_framework/tracing.cc +++ b/common/web_tracer_framework/tracing.cc @@ -34,7 +34,7 @@ void endLine(rapidjson::StringBuffer &result, rapidjson::Writer - -using namespace std; -namespace sorbet::compiler { - -CompilerState::CompilerState(const core::GlobalState &gs, llvm::LLVMContext &lctx, llvm::Module *module, - llvm::DIBuilder *debug, llvm::DICompileUnit *compileUnit, core::FileRef file, - llvm::BasicBlock *allocRubyIdsEntry, llvm::BasicBlock *globalConstructorsEntry, - StringTable &stringTable, IDTable &idTable, RubyStringTable &rubyStringTable) - : gs(gs), lctx(lctx), module(module), allocRubyIdsEntry(allocRubyIdsEntry), - globalConstructorsEntry(globalConstructorsEntry), debug(debug), compileUnit(compileUnit), - functionEntryInitializers(nullptr), file(file), stringTable(stringTable), idTable(idTable), - rubyStringTable(rubyStringTable) {} - -llvm::StructType *CompilerState::getValueType() { - auto intType = llvm::Type::getInt64Ty(lctx); - return llvm::StructType::create(lctx, intType, "RV"); -}; - -void CompilerState::trace(string_view msg) const { - gs.trace(msg); -} - -namespace { -llvm::GlobalVariable *declareNullptrPlaceholder(llvm::Module &module, llvm::Type *type, const string &name) { - // This variable is conceptually `const` (even though we're going to change its - // initializer during compilation, at runtime its value doesn't change), but - // if we declare that up front, LLVM will fold loads of its value, which - // causes problems. So save declaring it as `const` until later. - const auto isConstant = false; - llvm::Constant *initializer = llvm::Constant::getNullValue(type); - auto *global = - new llvm::GlobalVariable(module, type, isConstant, llvm::GlobalVariable::InternalLinkage, initializer, name); - global->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global); - global->setAlignment(llvm::MaybeAlign(8)); - return global; -} -} // namespace - -StringTable::Entry CompilerState::insertIntoStringTable(std::string_view str) { - auto it = this->stringTable.map.find(str); - - // We would like to return &sorbet_moduleStringTable[offset], but that would - // require knowing the length of the string table at this point (we would - // need to know the length to declare the global variable holding the string - // table properly). So instead what we're going to do is return a - // (as-yet uninitialized) variable that would hold &sorbet_moduleStringTable[offset]. - // - // Then, when we're finished compiling the module, we can declare the string - // table with the proper length (i.e. type) and go back and properly initialize - // all of the temporary variables we created along the way. - if (it != this->stringTable.map.end()) { - return it->second; - } - - auto offset = this->stringTable.size; - auto globalName = fmt::format("addr_str_{}", str); - auto *type = llvm::Type::getInt8PtrTy(this->lctx); - auto *global = declareNullptrPlaceholder(*this->module, type, globalName); - auto &entry = this->stringTable.map[str]; - entry = StringTable::Entry{offset, global}; - // +1 for the null terminator. - this->stringTable.size += str.size() + 1; - - return entry; -} - -llvm::Value *CompilerState::stringTableRef(std::string_view str) { - auto entry = this->insertIntoStringTable(str); - return entry.addrVar; -} - -llvm::Value *CompilerState::idTableRef(std::string_view str) { - auto it = this->idTable.map.find(str); - - if (it != this->idTable.map.end()) { - return it->second.addrVar; - } - - auto entry = this->insertIntoStringTable(str); - auto offset = static_cast(this->idTable.map.size()); - auto globalName = fmt::format("addr_id_{}", str); - // TODO(froydnj): IDs are 32 bits, but Payload.cc was declaring them as 64? - auto *type = llvm::Type::getInt64PtrTy(this->lctx); - auto *global = declareNullptrPlaceholder(*this->module, type, globalName); - auto strSize = static_cast(str.size()); - this->idTable.map[str] = IDTable::Entry{offset, entry.offset, strSize, global}; - - return global; -} - -llvm::Value *CompilerState::rubyStringTableRef(std::string_view str) { - auto it = this->rubyStringTable.map.find(str); - - if (it != this->rubyStringTable.map.end()) { - return it->second.addrVar; - } - - auto entry = this->insertIntoStringTable(str); - auto offset = static_cast(this->rubyStringTable.map.size()); - auto globalName = fmt::format("addr_rubystr_{}", str); - auto *type = llvm::Type::getInt64PtrTy(this->lctx); - auto *global = declareNullptrPlaceholder(*this->module, type, globalName); - auto strSize = static_cast(str.size()); - this->rubyStringTable.map[str] = RubyStringTable::Entry{offset, entry.offset, strSize, global}; - - return global; -} - -void StringTable::defineGlobalVariables(llvm::LLVMContext &lctx, llvm::Module &module) { - vector> tableElements; - tableElements.reserve(this->map.size()); - absl::c_copy(this->map, std::back_inserter(tableElements)); - fast_sort(tableElements, [](const auto &l, const auto &r) -> bool { return l.second.offset < r.second.offset; }); - string tableInitializer; - tableInitializer.reserve(this->size); - for (auto &elem : tableElements) { - tableInitializer += elem.first; - tableInitializer += '\0'; - } - ENFORCE(tableInitializer.size() == this->size); - - auto *arrayType = llvm::ArrayType::get(llvm::Type::getInt8Ty(lctx), this->size); - const auto isConstant = true; - const auto addNull = false; - auto *initializer = llvm::ConstantDataArray::getString(lctx, tableInitializer, addNull); - auto *table = new llvm::GlobalVariable(module, arrayType, isConstant, llvm::GlobalVariable::InternalLinkage, - initializer, "sorbet_moduleStringTable"); - table->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global); - table->setAlignment(llvm::MaybeAlign(1)); - - for (auto &elem : tableElements) { - auto *var = elem.second.addrVar; - auto *zero = llvm::ConstantInt::get(lctx, llvm::APInt(64, 0)); - auto *index = llvm::ConstantInt::get(lctx, llvm::APInt(64, elem.second.offset)); - llvm::Constant *indices[] = {zero, index}; - auto *initializer = llvm::ConstantExpr::getInBoundsGetElementPtr(table->getValueType(), table, indices); - var->setInitializer(initializer); - var->setConstant(true); - } -} - -void IDTable::defineGlobalVariables(llvm::LLVMContext &lctx, llvm::Module &module, llvm::IRBuilderBase &builder) { - vector> tableElements; - tableElements.reserve(this->map.size()); - absl::c_copy(this->map, std::back_inserter(tableElements)); - fast_sort(tableElements, [](const auto &l, const auto &r) -> bool { return l.second.offset < r.second.offset; }); - - llvm::GlobalVariable *table; - - { - auto *arrayType = llvm::ArrayType::get(llvm::Type::getInt64Ty(lctx), this->map.size()); - const auto isConstant = false; - auto *initializer = llvm::ConstantAggregateZero::get(arrayType); - table = new llvm::GlobalVariable(module, arrayType, isConstant, llvm::GlobalVariable::InternalLinkage, - initializer, "sorbet_moduleIDTable"); - table->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global); - table->setAlignment(llvm::MaybeAlign(8)); - } - - // Fill in all the addr vars that we indirected through. - for (auto &elem : tableElements) { - auto *var = elem.second.addrVar; - auto *zero = llvm::ConstantInt::get(lctx, llvm::APInt(64, 0)); - auto *index = llvm::ConstantInt::get(lctx, llvm::APInt(64, elem.second.offset)); - llvm::Constant *indices[] = {zero, index}; - auto *initializer = llvm::ConstantExpr::getInBoundsGetElementPtr(table->getValueType(), table, indices); - var->setInitializer(initializer); - var->setConstant(true); - } - - // Descriptors for the runtime initialization of members of the above. - // These types are known to the runtime. - auto *func = module.getFunction("sorbet_vm_intern_ids"); - ENFORCE(func != nullptr); - auto *descOffsetTy = llvm::Type::getInt32Ty(lctx); - auto *descLengthTy = descOffsetTy; - auto *structType = llvm::dyn_cast( - func->getType()->getElementType()->getFunctionParamType(1)->getPointerElementType()); - ENFORCE(structType != nullptr); - auto *arrayType = llvm::ArrayType::get(structType, this->map.size()); - const auto isConstant = true; - std::vector descsConstants; - descsConstants.reserve(this->map.size()); - - for (auto &elem : tableElements) { - auto *offset = llvm::ConstantInt::get(descOffsetTy, elem.second.stringTableOffset); - auto *length = llvm::ConstantInt::get(descLengthTy, elem.second.stringLength); - auto *desc = llvm::ConstantStruct::get(structType, offset, length); - descsConstants.push_back(desc); - } - - auto *initializer = llvm::ConstantArray::get(arrayType, descsConstants); - auto *descTable = new llvm::GlobalVariable(module, arrayType, isConstant, llvm::GlobalVariable::InternalLinkage, - initializer, "sorbet_moduleIDDescriptors"); - descTable->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global); - descTable->setAlignment(llvm::MaybeAlign(8)); - - // Call into the runtime to initialize everything. - const auto allowInternal = true; - auto *stringTable = module.getGlobalVariable("sorbet_moduleStringTable", allowInternal); - ENFORCE(stringTable != nullptr); - builder.CreateCall(func, {builder.CreateConstGEP2_32(nullptr, table, 0, 0), - builder.CreateConstGEP2_32(nullptr, descTable, 0, 0), - llvm::ConstantInt::get(llvm::Type::getInt32Ty(lctx), this->map.size()), - builder.CreateConstGEP2_32(nullptr, stringTable, 0, 0)}); -} - -void RubyStringTable::defineGlobalVariables(llvm::LLVMContext &lctx, llvm::Module &module, - llvm::IRBuilderBase &builder) { - vector> tableElements; - tableElements.reserve(this->map.size()); - absl::c_copy(this->map, std::back_inserter(tableElements)); - fast_sort(tableElements, [](const auto &l, const auto &r) -> bool { return l.second.offset < r.second.offset; }); - - llvm::GlobalVariable *table; - - { - auto *arrayType = llvm::ArrayType::get(llvm::Type::getInt64Ty(lctx), this->map.size()); - const auto isConstant = false; - auto *initializer = llvm::ConstantAggregateZero::get(arrayType); - table = new llvm::GlobalVariable(module, arrayType, isConstant, llvm::GlobalVariable::InternalLinkage, - initializer, "sorbet_moduleRubyStringTable"); - table->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global); - table->setAlignment(llvm::MaybeAlign(8)); - } - - // Fill in all the addr vars that we indirected through. - for (auto &elem : tableElements) { - auto *var = elem.second.addrVar; - auto *zero = llvm::ConstantInt::get(lctx, llvm::APInt(64, 0)); - auto *index = llvm::ConstantInt::get(lctx, llvm::APInt(64, elem.second.offset)); - llvm::Constant *indices[] = {zero, index}; - auto *initializer = llvm::ConstantExpr::getInBoundsGetElementPtr(table->getValueType(), table, indices); - var->setInitializer(initializer); - var->setConstant(true); - } - - // Descriptors for the runtime initialization of members of the above. - // These types are known to the runtime. - auto *func = module.getFunction("sorbet_vm_init_string_table"); - ENFORCE(func != nullptr); - auto *descOffsetTy = llvm::Type::getInt32Ty(lctx); - auto *descLengthTy = descOffsetTy; - auto *structType = llvm::dyn_cast( - func->getType()->getElementType()->getFunctionParamType(1)->getPointerElementType()); - ENFORCE(structType != nullptr); - auto *arrayType = llvm::ArrayType::get(structType, this->map.size()); - const auto isConstant = true; - std::vector descsConstants; - descsConstants.reserve(this->map.size()); - - for (auto &elem : tableElements) { - auto *offset = llvm::ConstantInt::get(descOffsetTy, elem.second.stringTableOffset); - auto *length = llvm::ConstantInt::get(descLengthTy, elem.second.stringLength); - auto *desc = llvm::ConstantStruct::get(structType, offset, length); - descsConstants.push_back(desc); - } - - auto *initializer = llvm::ConstantArray::get(arrayType, descsConstants); - auto *descTable = new llvm::GlobalVariable(module, arrayType, isConstant, llvm::GlobalVariable::InternalLinkage, - initializer, "sorbet_moduleRubyStringDescriptors"); - descTable->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global); - descTable->setAlignment(llvm::MaybeAlign(8)); - - // Call into the runtime to initialize everything. - const auto allowInternal = true; - auto *stringTable = module.getGlobalVariable("sorbet_moduleStringTable", allowInternal); - ENFORCE(stringTable != nullptr); - builder.CreateCall(func, {builder.CreateConstGEP2_32(nullptr, table, 0, 0), - builder.CreateConstGEP2_32(nullptr, descTable, 0, 0), - llvm::ConstantInt::get(llvm::Type::getInt32Ty(lctx), this->map.size()), - builder.CreateConstGEP2_32(nullptr, stringTable, 0, 0)}); -} - -llvm::FunctionType *CompilerState::getRubyFFIType() { - llvm::Type *args[] = { - llvm::Type::getInt32Ty(lctx), // arg count - llvm::Type::getInt64PtrTy(lctx), // argArray - llvm::Type::getInt64Ty(lctx), // self - llvm::StructType::getTypeByName(lctx, "struct.rb_control_frame_struct")->getPointerTo(), - llvm::Type::getInt8PtrTy(lctx), // void* (struct rb_calling_info) - llvm::Type::getInt8PtrTy(lctx), // void* (struct rb_call_data / struct rb_kwarg_call_data) - }; - return llvm::FunctionType::get(llvm::Type::getInt64Ty(lctx), args, false /*not varargs*/); -} - -llvm::FunctionType *CompilerState::getDirectWrapperFunctionType() { - llvm::Type *args[] = { - llvm::StructType::getTypeByName(lctx, "struct.FunctionInlineCache")->getPointerTo(), // cache - llvm::Type::getInt32Ty(lctx), // arg count - llvm::Type::getInt64PtrTy(lctx), // argArray - llvm::Type::getInt64Ty(lctx), // self - }; - return llvm::FunctionType::get(llvm::Type::getInt64Ty(lctx), args, false /*not varargs*/); -} - -llvm::FunctionType *CompilerState::getRubyBlockFFIType() { - llvm::Type *args[] = { - llvm::Type::getInt64Ty(lctx), // first yielded argument(first argument is both here and in argArray - llvm::Type::getInt64Ty(lctx), // data - llvm::Type::getInt32Ty(lctx), // arg count - llvm::Type::getInt64PtrTy(lctx), // argArray - llvm::Type::getInt64Ty(lctx), // blockArg - }; - return llvm::FunctionType::get(llvm::Type::getInt64Ty(lctx), args, false /*not varargs*/); -} - -llvm::FunctionType *CompilerState::getRubyExceptionFFIType() { - llvm::Type *args[] = { - llvm::Type::getInt64PtrTy(lctx)->getPointerTo(), // VALUE **pc - llvm::Type::getInt64Ty(lctx), // VALUE captures - llvm::StructType::getTypeByName(lctx, "struct.rb_control_frame_struct")->getPointerTo(), // cfp - }; - return llvm::FunctionType::get(llvm::Type::getInt64Ty(lctx), args, false /*not varargs*/); -} - -llvm::FunctionType *CompilerState::getInlineForwarderType() { - llvm::Type *args[] = { - llvm::Type::getInt64Ty(lctx), // VALUE val - }; - return llvm::FunctionType::get(llvm::Type::getInt64Ty(lctx), args, false /*not varargs*/); -} - -llvm::FunctionType *CompilerState::getAnyRubyCApiFunctionType() { - auto args = vector{}; - return llvm::FunctionType::get(llvm::Type::getInt64Ty(lctx), args, true /*IS varargs*/); -} - -llvm::Function *CompilerState::getFunction(llvm::StringRef name) const { - llvm::Function *f = module->getFunction(name); - ENFORCE(f, "could not find {} in the payload", name.str()); - return f; -} - -CompilerState CompilerState::withFunctionEntry(llvm::BasicBlock *entry) { - auto res = CompilerState(*this); - res.functionEntryInitializers = entry; - return res; -} - -void CompilerState::runCheapOptimizations(llvm::Function *func) { - llvm::legacy::FunctionPassManager pm(module); - llvm::PassManagerBuilder pmbuilder; - int optLevel = 2; - int sizeLevel = 0; - pmbuilder.OptLevel = optLevel; - pmbuilder.SizeLevel = sizeLevel; - pmbuilder.Inliner = nullptr; - pmbuilder.DisableUnrollLoops = false; - pmbuilder.LoopVectorize = true; - pmbuilder.SLPVectorize = true; - pmbuilder.VerifyInput = debug_mode; - pmbuilder.populateFunctionPassManager(pm); - pm.run(*func); -} - -} // namespace sorbet::compiler diff --git a/compiler/Core/CompilerState.h b/compiler/Core/CompilerState.h deleted file mode 100644 index dfb2cdbc7e..0000000000 --- a/compiler/Core/CompilerState.h +++ /dev/null @@ -1,135 +0,0 @@ -#ifndef SORBET_COMPILER_CORE_COMPILER_STATE_H -#define SORBET_COMPILER_CORE_COMPILER_STATE_H - -#include "compiler/Core/ForwardDeclarations.h" -#include "core/Files.h" -#include -#include - -namespace sorbet::compiler { - -struct StringTable { - struct Entry { - uint32_t offset = 0; - llvm::GlobalVariable *addrVar = nullptr; - }; - - UnorderedMap map; - uint32_t size = 0; - - void clear() { - this->map.clear(); - this->size = 0; - } - - void defineGlobalVariables(llvm::LLVMContext &lctx, llvm::Module &module); -}; - -struct IDTable { - struct Entry { - uint32_t offset; - uint32_t stringTableOffset = 0; - uint32_t stringLength = 0; - llvm::GlobalVariable *addrVar = nullptr; - }; - - UnorderedMap map; - - void clear() { - this->map.clear(); - } - - void defineGlobalVariables(llvm::LLVMContext &lctx, llvm::Module &module, llvm::IRBuilderBase &builder); -}; - -struct RubyStringTable { - struct Entry { - uint32_t offset; - uint32_t stringTableOffset = 0; - uint32_t stringLength = 0; - llvm::GlobalVariable *addrVar = nullptr; - }; - - UnorderedMap map; - - void clear() { - this->map.clear(); - } - - void defineGlobalVariables(llvm::LLVMContext &lctx, llvm::Module &module, llvm::IRBuilderBase &builder); -}; - -// Like GlobalState, but for the Sorbet Compiler. -class CompilerState { -public: - // Things created and managed ouside of us (by either Sorbet or plugin_injector) - CompilerState(const core::GlobalState &gs, llvm::LLVMContext &lctx, llvm::Module *, llvm::DIBuilder *, - llvm::DICompileUnit *, core::FileRef, llvm::BasicBlock *allocRubyIdsEntry, - llvm::BasicBlock *globalConstructorsEntry, StringTable &stringTable, IDTable &idTable, - RubyStringTable &rubyStringTable); - - const core::GlobalState &gs; - llvm::LLVMContext &lctx; - llvm::Module *module; - llvm::BasicBlock *allocRubyIdsEntry; - llvm::BasicBlock *globalConstructorsEntry; - - // Debug info - llvm::DIBuilder *debug; - llvm::DICompileUnit *compileUnit; - - // Our own state - - // TODO(jez) Only used by IREmitter, thus this probably shouldn't live in CompilerState longer term. - // (A better option might be to create an IREmitterContext of some sort.) - llvm::BasicBlock *functionEntryInitializers; - - core::FileRef file; - StringTable &stringTable; - IDTable &idTable; - RubyStringTable &rubyStringTable; - -private: - StringTable::Entry insertIntoStringTable(std::string_view str); - -public: - llvm::Value *stringTableRef(std::string_view str); - llvm::Value *idTableRef(std::string_view str); - llvm::Value *rubyStringTableRef(std::string_view str); - - // useful apis for getting common types - - llvm::StructType *getValueType(); - llvm::FunctionType *getRubyFFIType(); - llvm::FunctionType *getRubyBlockFFIType(); - llvm::FunctionType *getRubyExceptionFFIType(); - llvm::FunctionType *getInlineForwarderType(); - llvm::FunctionType *getAnyRubyCApiFunctionType(); - llvm::FunctionType *getDirectWrapperFunctionType(); - - // Run some cheap, per-function optimizations immediately after IR emission. - void runCheapOptimizations(llvm::Function *); - - // Implicit conversion to Sorbet state - operator const sorbet::core::GlobalState &() const { - return gs; - } - - // Implicit conversion to LLVMContext - operator llvm::LLVMContext &() const { - return lctx; - } - - // Forwards to `GlobalState::trace` - void trace(std::string_view) const; - - // A wrapper around `llvm::Module::getFunction` that displays useful errors when - // the function is not found - llvm::Function *getFunction(llvm::StringRef) const; - - CompilerState withFunctionEntry(llvm::BasicBlock *functionEntry); -}; - -} // namespace sorbet::compiler - -#endif diff --git a/compiler/Core/FailCompilation.h b/compiler/Core/FailCompilation.h deleted file mode 100644 index 0b338477a7..0000000000 --- a/compiler/Core/FailCompilation.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef SORBET_COMPILER_CORE_FAIL_COMPILATION_H -#define SORBET_COMPILER_CORE_FAIL_COMPILATION_H - -#include "compiler/Core/AbortCompilation.h" -#include "compiler/Errors/Errors.h" -#include "core/GlobalState.h" - -namespace sorbet::compiler { - -// Add an error to GlobalState, and then throw to abort compilation. -// Use only when compilation CANNOT continue. -// (Emitting any old GlobalState error will still cause Sorbet to exit non-zero.) -template -[[noreturn]] void failCompilation(const core::GlobalState &gs, const core::Loc &loc, fmt::format_string msg, - Args &&...args) { - if (auto e = gs.beginError(loc, core::errors::Compiler::Unanalyzable)) { - e.setHeader(msg, std::forward(args)...); - } - - throw AbortCompilation(fmt::format(msg, std::forward(args)...)); -} - -} // namespace sorbet::compiler -#endif diff --git a/compiler/Core/ForwardDeclarations.h b/compiler/Core/ForwardDeclarations.h deleted file mode 100644 index 71f96526b0..0000000000 --- a/compiler/Core/ForwardDeclarations.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef SORBET_COMPILER_FORWARD_DECLARATIONS_H -#define SORBET_COMPILER_FORWARD_DECLARATIONS_H - -namespace llvm { -class AllocaInst; -class BasicBlock; -class DIBuilder; -class DICompileUnit; -class Function; -class FunctionType; -class GlobalVariable; -class IRBuilderBase; -class LLVMContext; -class Module; -class StructType; -class Type; -class Value; -} // namespace llvm - -namespace sorbet::core { -class GlobalState; -class Loc; -class MutableContext; -class SymbolRef; -class MethodRef; -class NameRef; -} // namespace sorbet::core - -namespace sorbet::ast { -class ClassDef; -class MethodDef; -} // namespace sorbet::ast - -namespace sorbet::cfg { -class CFG; -class Send; -} // namespace sorbet::cfg - -#endif diff --git a/compiler/Core/OptimizerException.h b/compiler/Core/OptimizerException.h deleted file mode 100644 index 525e4b3195..0000000000 --- a/compiler/Core/OptimizerException.h +++ /dev/null @@ -1,14 +0,0 @@ -#include "common/exception/Exception.h" - -namespace sorbet::compiler { -// Occasionally we need to throw errors from within LLVM optimization passes, -// where we don't have access to GlobalState and therefore cannot use failCompilation. -// We have this separate class so that the compiler plugin can differentiate this -// exception from AbortCompilation and know that a Sorbet-level error needs to be -// emitted. The user can therefore have some idea of why Sorbet failed. -class OptimizerException : public sorbet::SorbetException { -public: - OptimizerException(const std::string &message) : SorbetException(message){}; - OptimizerException(const char *message) : SorbetException(message){}; -}; -} // namespace sorbet::compiler diff --git a/compiler/Errors/BUILD b/compiler/Errors/BUILD deleted file mode 100644 index 8dc6fc1825..0000000000 --- a/compiler/Errors/BUILD +++ /dev/null @@ -1,14 +0,0 @@ -cc_library( - name = "Errors", - srcs = [ - "Errors.h", - ], - linkstatic = select({ - "@com_stripe_ruby_typer//tools/config:linkshared": 0, - "//conditions:default": 1, - }), - visibility = ["//visibility:public"], - deps = [ - "@com_stripe_ruby_typer//core", - ], -) diff --git a/compiler/Errors/Errors.h b/compiler/Errors/Errors.h deleted file mode 100644 index b65617685d..0000000000 --- a/compiler/Errors/Errors.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef SORBET_COMPILER_ERRORS_H -#define SORBET_COMPILER_ERRORS_H -#include "core/Error.h" - -namespace sorbet::core::errors::Compiler { -constexpr ErrorClass Untyped{10001, StrictLevel::True}; -constexpr ErrorClass Unanalyzable{10002, StrictLevel::True}; -constexpr ErrorClass OptimizerFailure{10003, StrictLevel::True}; -} // namespace sorbet::core::errors::Compiler -#endif diff --git a/compiler/IREmitter/BUILD b/compiler/IREmitter/BUILD deleted file mode 100644 index 9f237f6eec..0000000000 --- a/compiler/IREmitter/BUILD +++ /dev/null @@ -1,22 +0,0 @@ -cc_library( - name = "IREmitter", - srcs = glob([ - "*.cc", - "*.h", - ]), - hdrs = ["IREmitter.h"], - linkstatic = select({ - "@com_stripe_ruby_typer//tools/config:linkshared": 0, - "//conditions:default": 1, - }), - visibility = ["//visibility:public"], - deps = [ - "//compiler/Core", - "//compiler/Errors", - "//compiler/IREmitter/Payload", - "@com_stripe_ruby_typer//ast", - "@com_stripe_ruby_typer//cfg", - "@com_stripe_ruby_typer//core", - "@spdlog", - ], -) diff --git a/compiler/IREmitter/CFGHelpers.cc b/compiler/IREmitter/CFGHelpers.cc deleted file mode 100644 index 7d4e55b160..0000000000 --- a/compiler/IREmitter/CFGHelpers.cc +++ /dev/null @@ -1,65 +0,0 @@ -#include "compiler/IREmitter/CFGHelpers.h" -#include "cfg/CFG.h" - -#include - -using namespace std; - -namespace sorbet::compiler { - -namespace { - -bool validExit(cfg::CFG &cfg, int targetRegionId, int rubyRegionId, vector &exits, - cfg::BasicBlock *candidate) { - if (candidate->rubyRegionId == rubyRegionId) { - return false; - } - - if (candidate == cfg.deadBlock()) { - return false; - } - - if (absl::c_find(exits, candidate) != exits.end()) { - return false; - } - - return candidate->rubyRegionId == targetRegionId; -} - -} // namespace - -vector CFGHelpers::findRegionExits(cfg::CFG &cfg, int targetRegionId, int sourceRegionId) { - vector exits; - - for (auto &node : cfg.basicBlocks) { - if (node->rubyRegionId != sourceRegionId) { - continue; - } - - auto *thenb = node->bexit.thenb; - auto *elseb = node->bexit.elseb; - - if (validExit(cfg, targetRegionId, sourceRegionId, exits, thenb)) { - exits.emplace_back(thenb); - } - - if (validExit(cfg, targetRegionId, sourceRegionId, exits, elseb)) { - exits.emplace_back(elseb); - } - } - - return exits; -} - -cfg::BasicBlock *CFGHelpers::findRegionEntry(cfg::CFG &cfg, int rubyRegionId) { - for (auto &bb : cfg.basicBlocks) { - if (bb->rubyRegionId == rubyRegionId) { - return bb.get(); - } - } - - // This can happen when the ruby block is completely optimized away. - return nullptr; -} - -} // namespace sorbet::compiler diff --git a/compiler/IREmitter/CFGHelpers.h b/compiler/IREmitter/CFGHelpers.h deleted file mode 100644 index ec6311a0ef..0000000000 --- a/compiler/IREmitter/CFGHelpers.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef SORBET_COMPILER_IREMITTER_CFG_HELPERS_H -#define SORBET_COMPILER_IREMITTER_CFG_HELPERS_H - -#include "cfg/CFG.h" -#include - -namespace sorbet::compiler { - -class CFGHelpers { -public: - // A heuristic for finding the first block in a region: find the basic-block with the lowest id for the given ruby - // block id. - static cfg::BasicBlock *findRegionEntry(cfg::CFG &cfg, int regionId); - - // Find the blocks in the target region that are jumped to from the source region. This explicitly ignores the dead - // block as a valid jump target. - static std::vector findRegionExits(cfg::CFG &cfg, int targetRegionId, int sourceRegionId); -}; - -} // namespace sorbet::compiler - -#endif diff --git a/compiler/IREmitter/CallCacheFlags.h b/compiler/IREmitter/CallCacheFlags.h deleted file mode 100644 index c6852cce2d..0000000000 --- a/compiler/IREmitter/CallCacheFlags.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef SORBET_COMPILER_CALLCACHEFLAGS_H -#define SORBET_COMPILER_CALLCACHEFLAGS_H - -#include "compiler/Core/ForwardDeclarations.h" - -namespace sorbet::compiler { -class CompilerState; - -struct CallCacheFlags { - // Field names correspond to VM_CALL_* flags. - bool args_simple = false; - bool args_splat = false; - bool kwarg = false; - bool kw_splat = false; - bool fcall = false; - bool blockarg = false; - - llvm::Value *build(CompilerState &cs, llvm::IRBuilderBase &builder); -}; - -} // namespace sorbet::compiler - -#endif diff --git a/compiler/IREmitter/Exceptions.cc b/compiler/IREmitter/Exceptions.cc deleted file mode 100644 index 99aa973948..0000000000 --- a/compiler/IREmitter/Exceptions.cc +++ /dev/null @@ -1,68 +0,0 @@ -#include "llvm/IR/IRBuilder.h" -#include "llvm/IR/Type.h" - -#include "cfg/CFG.h" -#include "compiler/Core/CompilerState.h" -#include "compiler/IREmitter/IREmitterContext.h" -#include "compiler/IREmitter/IREmitterHelpers.h" -#include "compiler/IREmitter/Payload.h" - -using namespace std; - -namespace sorbet::compiler { - -namespace { -llvm::Function *getDoNothing(CompilerState &cs) { - return cs.getFunction("sorbet_blockReturnUndef"); -} - -llvm::Function *getExceptionFunc(CompilerState &cs, const IREmitterContext &irctx, int rubyRegionId) { - auto present = irctx.rubyBlockType[rubyRegionId] != FunctionType::Unused; - return present ? irctx.rubyBlocks2Functions[rubyRegionId] : getDoNothing(cs); -} -} // namespace - -void IREmitterHelpers::emitExceptionHandlers(CompilerState &cs, llvm::IRBuilderBase &builder, - const IREmitterContext &irctx, int rubyRegionId, int bodyRubyRegionId, - cfg::LocalRef exceptionValue) { - const int handlersRubyRegionId = bodyRubyRegionId + cfg::CFG::HANDLERS_REGION_OFFSET; - const int ensureRubyRegionId = bodyRubyRegionId + cfg::CFG::ENSURE_REGION_OFFSET; - const int elseRubyRegionId = bodyRubyRegionId + cfg::CFG::ELSE_REGION_OFFSET; - - auto *currentFunc = irctx.rubyBlocks2Functions[rubyRegionId]; - // TODO: it would be nice if we could detect some of these functions - // were empty and/or not useful so that we could eliminate dealing with - // them from the exception handling logic. - ENFORCE(irctx.rubyBlockType[handlersRubyRegionId] != FunctionType::Unused); - // ENFORCE(irctx.rubyBlockType[ensureRubyRegionId] != FunctionType::Unused); - // ENFORCE(irctx.rubyBlockType[elseRubyRegionId] != FunctionType::Unused); - auto *handlersFunc = getExceptionFunc(cs, irctx, handlersRubyRegionId); - auto *ensureFunc = getExceptionFunc(cs, irctx, ensureRubyRegionId); - auto *elseFunc = getExceptionFunc(cs, irctx, elseRubyRegionId); - - auto *ec = builder.CreateCall(cs.getFunction("sorbet_getEC"), {}, "ec"); - - auto *pc = builder.CreateLoad(irctx.lineNumberPtrsByFunction[rubyRegionId]); - auto *closure = Payload::buildLocalsOffset(cs); - auto *cfp = Payload::getCFPForBlock(cs, builder, irctx, rubyRegionId); - - auto info = Payload::escapedVariableInfo(cs, exceptionValue, irctx, bodyRubyRegionId); - auto *v = - builder.CreateCall(cs.getFunction("sorbet_run_exception_handling"), - {ec, irctx.rubyBlocks2Functions[bodyRubyRegionId], pc, closure, cfp, handlersFunc, elseFunc, - ensureFunc, Payload::retrySingleton(cs, builder, irctx), info.index, info.level}); - - auto *exceptionContinue = llvm::BasicBlock::Create(cs, "exception-continue", currentFunc); - auto *exceptionReturn = llvm::BasicBlock::Create(cs, "exception-return", currentFunc); - - auto *notUndef = builder.CreateICmpNE(v, Payload::rubyUndef(cs, builder), "ensureReturnValue"); - builder.CreateCondBr(notUndef, exceptionReturn, exceptionContinue); - - builder.SetInsertPoint(exceptionReturn); - IREmitterHelpers::emitReturn(cs, builder, irctx, rubyRegionId, v); - - builder.SetInsertPoint(exceptionContinue); - return; -} - -} // namespace sorbet::compiler diff --git a/compiler/IREmitter/IREmitter.cc b/compiler/IREmitter/IREmitter.cc deleted file mode 100644 index 629b2a565e..0000000000 --- a/compiler/IREmitter/IREmitter.cc +++ /dev/null @@ -1,1133 +0,0 @@ -// These violate our poisons so have to happen first -#include "llvm/IR/Attributes.h" -#include "llvm/IR/DIBuilder.h" -#include "llvm/IR/DerivedTypes.h" // FunctionType, StructType -#include "llvm/IR/IRBuilder.h" -#include "llvm/IR/Verifier.h" -#include "llvm/Transforms/Utils/Cloning.h" - -#include "ast/Helpers.h" -#include "ast/ast.h" -#include "cfg/CFG.h" -#include "common/FileOps.h" -#include "common/sort/sort.h" -#include "common/timers/Timer.h" -#include "common/typecase.h" -#include "compiler/Core/CompilerState.h" -#include "compiler/Core/FailCompilation.h" -#include "compiler/Errors/Errors.h" -#include "compiler/IREmitter/IREmitter.h" -#include "compiler/IREmitter/IREmitterContext.h" -#include "compiler/IREmitter/IREmitterHelpers.h" -#include "compiler/IREmitter/MethodCallContext.h" -#include "compiler/IREmitter/Payload.h" -#include - -using namespace std; -namespace sorbet::compiler { - -namespace { - -vector getArgFlagsForBlockId(CompilerState &cs, int blockId, core::MethodRef method, - const IREmitterContext &irctx) { - auto ty = irctx.rubyBlockType[blockId]; - ENFORCE(ty == FunctionType::Block || ty == FunctionType::Method || ty == FunctionType::StaticInitFile || - ty == FunctionType::StaticInitModule); - - if (ty == FunctionType::Block) { - auto blockLink = irctx.blockLinks[blockId]; - return blockLink->argFlags; - } - - vector res; - for (auto &argInfo : method.data(cs)->arguments) { - res.emplace_back(argInfo.flags); - } - - return res; -} - -void setupStackFrame(CompilerState &cs, const ast::MethodDef &md, const IREmitterContext &irctx, - llvm::IRBuilderBase &builder, int rubyRegionId) { - switch (irctx.rubyBlockType[rubyRegionId]) { - case FunctionType::Method: - case FunctionType::StaticInitFile: - case FunctionType::StaticInitModule: - case FunctionType::Block: - case FunctionType::Rescue: - case FunctionType::Ensure: { - // Switch the current control frame from a C frame to a Ruby-esque one - auto pc = Payload::setRubyStackFrame(cs, builder, irctx, md, rubyRegionId); - builder.CreateStore(pc, irctx.lineNumberPtrsByFunction[rubyRegionId]); - break; - } - - case FunctionType::ExceptionBegin: { - // Exception functions get their pc and iseq_encoded values as arguments - auto func = irctx.rubyBlocks2Functions[rubyRegionId]; - auto *pc = func->arg_begin(); - builder.CreateStore(pc, irctx.lineNumberPtrsByFunction[rubyRegionId]); - break; - } - - case FunctionType::Unused: - break; - } -} - -} // namespace - -void setupStackFrames(CompilerState &base, const ast::MethodDef &md, const IREmitterContext &irctx) { - llvm::IRBuilder<> builder(base); - for (auto rubyRegionId = 0; rubyRegionId < irctx.rubyBlocks2Functions.size(); rubyRegionId++) { - auto cs = base.withFunctionEntry(irctx.functionInitializersByFunction[rubyRegionId]); - - builder.SetInsertPoint(irctx.functionInitializersByFunction[rubyRegionId]); - - switch (irctx.rubyBlockType[rubyRegionId]) { - case FunctionType::Method: - case FunctionType::StaticInitFile: - case FunctionType::StaticInitModule: { - // We could get this from the frame, as below, but it is slightly more - // efficient to get it from the function arguments. - auto selfArgRaw = irctx.rubyBlocks2Functions[rubyRegionId]->arg_begin() + 2; - Payload::varSet(cs, cfg::LocalRef::selfVariable(), selfArgRaw, builder, irctx, rubyRegionId); - break; - } - case FunctionType::Block: - case FunctionType::Rescue: - case FunctionType::Ensure: - case FunctionType::ExceptionBegin: { - auto selfArgRaw = builder.CreateCall(cs.getFunction("sorbet_getSelfFromFrame"), {}, "self"); - Payload::varSet(cs, cfg::LocalRef::selfVariable(), selfArgRaw, builder, irctx, rubyRegionId); - break; - } - default: - break; - } - - if (irctx.rubyBlockType[rubyRegionId] == FunctionType::Block) { - auto *cfp = builder.CreateCall(cs.getFunction("sorbet_getCFP"), {}, "cfp"); - builder.CreateStore(cfp, irctx.blockControlFramePtrs.at(rubyRegionId)); - } - - setupStackFrame(cs, md, irctx, builder, rubyRegionId); - auto lastLoc = core::Loc::none(); - auto startLoc = md.symbol.data(base)->loc(); - Payload::setLineNumber(cs, builder, core::Loc(cs.file, md.loc), startLoc, lastLoc, - irctx.lineNumberPtrsByFunction[rubyRegionId]); - } -} - -void parseKeywordArgsFromCallData(CompilerState &cs, llvm::IRBuilderBase &builder, const IREmitterContext &irctx, - cfg::LocalRef kwRestArgName, llvm::Value *argCountRaw, llvm::Value *argArrayRaw, - llvm::Value *hashArgs, int maxPositionalArgCount, - const vector &argsFlags, int rubyRegionId) { - ENFORCE(rubyRegionId == 0); - auto *func = irctx.rubyBlocks2Functions[rubyRegionId]; - auto &argPresentVariables = irctx.argPresentVariables[rubyRegionId]; - // required arguments remaining to be parsed - auto numRequiredKwArgs = absl::c_count_if( - argsFlags, [](auto &argFlag) { return argFlag.isKeyword && !argFlag.isDefault && !argFlag.isRepeated; }); - auto *missingKwargs = Payload::rubyUndef(cs, builder); - - // optional arguments that are present - auto *optionalKwargs = IREmitterHelpers::buildS4(cs, 0); - - // Sorbet method functions are passed (int, VALUE *, VALUE, rb_control_frame_t *, void *, void *). - // The second void *, argument 6, is rb_call_data/rb_kwarg_call_data. - auto *callData = func->arg_begin() + 5; - // The keywords, for a non-kwsplat situation, are passed after the positional - // arguments. The passed argc (argument 1) reflects *only* the number of - // positional arguments passed; the number of keyword arguments is stored in the - // call data. But the keyword arguments are still passed in argv (argument 2). - llvm::Value *kwargvIndices[] = {argCountRaw}; - auto *kwargv = builder.CreateGEP(argArrayRaw, kwargvIndices, "kwargv"); - - for (int argId = maxPositionalArgCount; argId < argsFlags.size(); argId++) { - if (!argsFlags[argId].isKeyword || argsFlags[argId].isRepeated) { - continue; - } - auto name = irctx.rubyBlockArgs[rubyRegionId][argId]; - auto strviewName = name.data(irctx.cfg)._name.shortName(cs); - auto rawId = Payload::idIntern(cs, builder, strviewName); - - auto argPresent = argPresentVariables[argId]; - - auto *passedValue = builder.CreateCall(cs.getFunction("sorbet_kwarg_passed_value"), {callData, rawId, kwargv}, - fmt::format("kwargValueFor_{}", strviewName)); - auto *isItUndef = Payload::testIsUndef(cs, builder, passedValue); - - // Fill in the default value, if any. - auto kwArgSet = llvm::BasicBlock::Create(cs, fmt::format("kwArgSet_{}", strviewName), func); - auto kwArgDefault = llvm::BasicBlock::Create(cs, fmt::format("kwArgDefault_{}", strviewName), func); - auto kwArgContinue = llvm::BasicBlock::Create(cs, fmt::format("kwArgContinue_{}", strviewName), func); - - auto *missingPhi = llvm::PHINode::Create(missingKwargs->getType(), 2, - fmt::format("missingArgsPhi_{}", strviewName), kwArgContinue); - auto *optionalPhi = llvm::PHINode::Create(optionalKwargs->getType(), 2, - fmt::format("optionalArgsPhi_{}", strviewName), kwArgContinue); - - builder.CreateCondBr(isItUndef, kwArgDefault, kwArgSet); - - // Write a default value out, and mark the variable as missing - builder.SetInsertPoint(kwArgDefault); - if (argPresent.exists()) { - Payload::varSet(cs, argPresent, Payload::rubyFalse(cs, builder), builder, irctx, rubyRegionId); - } - - auto *updatedMissingKwargs = missingKwargs; - if (!argsFlags[argId].isDefault) { - auto *rawRubySym = builder.CreateCall(cs.getFunction("rb_id2sym"), {rawId}, "rawSym"); - updatedMissingKwargs = Payload::addMissingKWArg(cs, builder, missingKwargs, rawRubySym); - } - - optionalPhi->addIncoming(optionalKwargs, builder.GetInsertBlock()); - missingPhi->addIncoming(updatedMissingKwargs, builder.GetInsertBlock()); - builder.CreateBr(kwArgContinue); - - builder.SetInsertPoint(kwArgSet); - auto *updatedOptionalKwargs = optionalKwargs; - if (argPresent.exists()) { - if (argsFlags[argId].isDefault) { - updatedOptionalKwargs = - builder.CreateBinOp(llvm::Instruction::Add, optionalKwargs, IREmitterHelpers::buildS4(cs, 1), - fmt::format("updatedOptional_{}", strviewName)); - } - - Payload::varSet(cs, argPresent, Payload::rubyTrue(cs, builder), builder, irctx, rubyRegionId); - } - Payload::varSet(cs, name, passedValue, builder, irctx, rubyRegionId); - - optionalPhi->addIncoming(updatedOptionalKwargs, builder.GetInsertBlock()); - missingPhi->addIncoming(missingKwargs, builder.GetInsertBlock()); - builder.CreateBr(kwArgContinue); - - builder.SetInsertPoint(kwArgContinue); - optionalKwargs = optionalPhi; - missingKwargs = missingPhi; - } - Payload::assertAllRequiredKWArgs(cs, builder, missingKwargs); - // We never want to use this path when we have a kwsplat arg, because the caller - // will roll things up into the kwsplat, so there wouldn't be anything for us. - ENFORCE(!kwRestArgName.exists()); - builder.CreateCall(cs.getFunction("sorbet_assertCallDataNoExtraKWArg"), - {callData, IREmitterHelpers::buildS4(cs, numRequiredKwArgs), optionalKwargs}); -} - -void parseKeywordArgsFromKwSplat(CompilerState &cs, llvm::IRBuilderBase &builder, const IREmitterContext &irctx, - cfg::LocalRef kwRestArgName, llvm::Value *hashArgs, int maxPositionalArgCount, - const vector &argsFlags, int rubyRegionId) { - auto *func = irctx.rubyBlocks2Functions[rubyRegionId]; - auto &argPresentVariables = irctx.argPresentVariables[rubyRegionId]; - // required arguments remaining to be parsed - auto numRequiredKwArgs = absl::c_count_if( - argsFlags, [](auto &argFlag) { return argFlag.isKeyword && !argFlag.isDefault && !argFlag.isRepeated; }); - auto *missingKwargs = Payload::rubyUndef(cs, builder); - - // optional arguments that are present - auto *optionalKwargs = IREmitterHelpers::buildS4(cs, 0); - - for (int argId = maxPositionalArgCount; argId < argsFlags.size(); argId++) { - if (!argsFlags[argId].isKeyword || argsFlags[argId].isRepeated) { - continue; - } - auto name = irctx.rubyBlockArgs[rubyRegionId][argId]; - auto rawId = Payload::idIntern(cs, builder, name.data(irctx.cfg)._name.shortName(cs)); - auto rawRubySym = builder.CreateCall(cs.getFunction("rb_id2sym"), {rawId}, "rawSym"); - - auto argPresent = argPresentVariables[argId]; - - llvm::Value *passedValue; - if (kwRestArgName.exists()) { - passedValue = Payload::removeKWArg(cs, builder, hashArgs, rawRubySym); - } else { - passedValue = Payload::getKWArg(cs, builder, hashArgs, rawRubySym); - } - - auto isItUndef = Payload::testIsUndef(cs, builder, passedValue); - - auto kwArgSet = llvm::BasicBlock::Create(cs, "kwArgSet", func); - auto kwArgDefault = llvm::BasicBlock::Create(cs, "kwArgDefault", func); - auto kwArgContinue = llvm::BasicBlock::Create(cs, "kwArgContinue", func); - - auto *missingPhi = llvm::PHINode::Create(missingKwargs->getType(), 2, "missingArgsPhi", kwArgContinue); - auto *optionalPhi = llvm::PHINode::Create(optionalKwargs->getType(), 2, "optionalArgsPhi", kwArgContinue); - - builder.CreateCondBr(isItUndef, kwArgDefault, kwArgSet); - - // Write a default value out, and mark the variable as missing - builder.SetInsertPoint(kwArgDefault); - if (argPresent.exists()) { - Payload::varSet(cs, argPresent, Payload::rubyFalse(cs, builder), builder, irctx, rubyRegionId); - } - - auto *updatedMissingKwargs = missingKwargs; - if (!argsFlags[argId].isDefault) { - updatedMissingKwargs = Payload::addMissingKWArg(cs, builder, missingKwargs, rawRubySym); - } - - optionalPhi->addIncoming(optionalKwargs, builder.GetInsertBlock()); - missingPhi->addIncoming(updatedMissingKwargs, builder.GetInsertBlock()); - builder.CreateBr(kwArgContinue); - - builder.SetInsertPoint(kwArgSet); - auto *updatedOptionalKwargs = optionalKwargs; - if (argPresent.exists()) { - if (argsFlags[argId].isDefault) { - updatedOptionalKwargs = - builder.CreateBinOp(llvm::Instruction::Add, optionalKwargs, IREmitterHelpers::buildS4(cs, 1)); - } - - Payload::varSet(cs, argPresent, Payload::rubyTrue(cs, builder), builder, irctx, rubyRegionId); - } - Payload::varSet(cs, name, passedValue, builder, irctx, rubyRegionId); - optionalPhi->addIncoming(updatedOptionalKwargs, builder.GetInsertBlock()); - missingPhi->addIncoming(missingKwargs, builder.GetInsertBlock()); - builder.CreateBr(kwArgContinue); - - builder.SetInsertPoint(kwArgContinue); - optionalKwargs = optionalPhi; - missingKwargs = missingPhi; - } - Payload::assertAllRequiredKWArgs(cs, builder, missingKwargs); - if (kwRestArgName.exists()) { - Payload::varSet(cs, kwRestArgName, Payload::readKWRestArg(cs, builder, hashArgs), builder, irctx, rubyRegionId); - } else { - Payload::assertNoExtraKWArg(cs, builder, hashArgs, IREmitterHelpers::buildS4(cs, numRequiredKwArgs), - optionalKwargs); - } -} - -void setupArguments(CompilerState &base, cfg::CFG &cfg, const ast::MethodDef &md, const IREmitterContext &irctx) { - // this function effectively generate an optimized build of - // https://github.com/ruby/ruby/blob/59c3b1c9c843fcd2d30393791fe224e5789d1677/include/ruby/ruby.h#L2522-L2675 - llvm::IRBuilder<> builder(base); - for (auto rubyRegionId = 0; rubyRegionId < irctx.rubyBlocks2Functions.size(); rubyRegionId++) { - auto cs = base.withFunctionEntry(irctx.functionInitializersByFunction[rubyRegionId]); - - builder.SetInsertPoint(irctx.argumentSetupBlocksByFunction[rubyRegionId]); - - // emit a location that corresponds to the function entry - auto loc = md.symbol.data(cs)->loc(); - IREmitterHelpers::emitDebugLoc(cs, builder, irctx, rubyRegionId, loc); - - auto blockType = irctx.rubyBlockType[rubyRegionId]; - if (blockType == FunctionType::Method || blockType == FunctionType::StaticInitFile || - blockType == FunctionType::StaticInitModule || blockType == FunctionType::Block) { - auto func = irctx.rubyBlocks2Functions[rubyRegionId]; - auto &argPresentVariables = irctx.argPresentVariables[rubyRegionId]; - auto maxPositionalArgCount = 0; - auto minPositionalArgCount = 0; - auto isBlock = blockType == FunctionType::Block; - auto hasRestArgs = false; - auto hasKWArgs = false; - auto hasKWRestArgs = false; - llvm::Value *argCountRaw = !isBlock ? func->arg_begin() : func->arg_begin() + 2; - llvm::Value *argArrayRaw = !isBlock ? func->arg_begin() + 1 : func->arg_begin() + 3; - llvm::Value *hashArgs; - - cfg::LocalRef blkArgName; - cfg::LocalRef restArgName; - cfg::LocalRef kwRestArgName; - - auto argsFlags = getArgFlagsForBlockId(cs, rubyRegionId, cfg.symbol, irctx); - { - auto argId = -1; - ENFORCE(argsFlags.size() == irctx.rubyBlockArgs[rubyRegionId].size()); - for (auto &argFlags : argsFlags) { - argId += 1; - if (argFlags.isKeyword) { - hasKWArgs = true; - if (argFlags.isRepeated) { - kwRestArgName = irctx.rubyBlockArgs[rubyRegionId][argId]; - hasKWRestArgs = true; - } - continue; - } - if (argFlags.isRepeated) { - restArgName = irctx.rubyBlockArgs[rubyRegionId][argId]; - hasRestArgs = true; - continue; - } - if (argFlags.isDefault) { - maxPositionalArgCount += 1; - continue; - } - if (argFlags.isBlock) { - blkArgName = irctx.rubyBlockArgs[rubyRegionId][argId]; - continue; - } - maxPositionalArgCount += 1; - minPositionalArgCount += 1; - } - } - - hashArgs = Payload::rubyUndef(cs, builder); - - if (hasKWArgs) { - // if last argument is a hash, it's not part of positional arguments - it's going to - // fullfill all kw arguments instead - auto *minPositionalArgValue = IREmitterHelpers::buildS4(cs, minPositionalArgCount); - auto *hashArgsCtx = - builder.CreateCall(cs.getFunction("sorbet_determineKwSplatArg"), - {argCountRaw, argArrayRaw, minPositionalArgValue}, "hashArgsCtx"); - argCountRaw = builder.CreateExtractValue(hashArgsCtx, {0}, "argCountRaw"); - hashArgs = builder.CreateExtractValue(hashArgsCtx, {1}, "hashArgs"); - } - - if (isBlock) { - if (maxPositionalArgCount > 1) { - // blocks can expand their first argument in arg array - auto arrayTestBlock = llvm::BasicBlock::Create(cs, "argArrayExpandArrayTest", func); - auto argExpandBlock = llvm::BasicBlock::Create(cs, "argArrayExpand", func); - auto afterArgArrayExpandBlock = llvm::BasicBlock::Create(cs, "afterArgArrayExpand", func); - auto argSizeForExpansionCheck = builder.CreateICmpEQ( - argCountRaw, llvm::ConstantInt::get(cs, llvm::APInt(32, 1)), "arrayExpansionSizeGuard"); - builder.CreateCondBr(argSizeForExpansionCheck, arrayTestBlock, afterArgArrayExpandBlock); - auto sizeTestEnd = builder.GetInsertBlock(); - builder.SetInsertPoint(arrayTestBlock); - auto rawArg1Value = - builder.CreateLoad(builder.CreateConstGEP1_32(argArrayRaw, 0), "arg1_maybeExpandToFullArgs"); - auto isArray = Payload::typeTest(cs, builder, rawArg1Value, core::Symbols::Array()); - auto typeTestEnd = builder.GetInsertBlock(); - - builder.CreateCondBr(isArray, argExpandBlock, afterArgArrayExpandBlock); - builder.SetInsertPoint(argExpandBlock); - auto newArgArray = builder.CreateCall(cs.getFunction("sorbet_rubyArrayInnerPtr"), {rawArg1Value}, - "expandedArgArray"); - auto newArgc = - builder.CreateCall(cs.getFunction("sorbet_rubyArrayLen"), {rawArg1Value}, "expandedArgc"); - auto expansionEnd = builder.GetInsertBlock(); - builder.CreateBr(afterArgArrayExpandBlock); - builder.SetInsertPoint(afterArgArrayExpandBlock); - auto argcPhi = builder.CreatePHI(builder.getInt32Ty(), 3, "argcPhi"); - argcPhi->addIncoming(argCountRaw, sizeTestEnd); - argcPhi->addIncoming(argCountRaw, typeTestEnd); - argcPhi->addIncoming(newArgc, expansionEnd); - argCountRaw = argcPhi; - auto argArrayPhi = builder.CreatePHI(llvm::Type::getInt64PtrTy(cs), 3, "argArrayPhi"); - argArrayPhi->addIncoming(argArrayRaw, sizeTestEnd); - argArrayPhi->addIncoming(argArrayRaw, typeTestEnd); - argArrayPhi->addIncoming(newArgArray, expansionEnd); - - argArrayRaw = argArrayPhi; - } - minPositionalArgCount = 0; - // blocks Can have 0 args always - } - - auto numOptionalArgs = maxPositionalArgCount - minPositionalArgCount; - if (!isBlock) { - // validate arg count - auto argCountFailBlock = llvm::BasicBlock::Create(cs, "argCountFailBlock", func); - auto argCountSecondCheckBlock = llvm::BasicBlock::Create(cs, "argCountSecondCheckBlock", func); - auto argCountSuccessBlock = llvm::BasicBlock::Create(cs, "argCountSuccess", func); - - if (!hasRestArgs) { - auto tooManyArgs = builder.CreateICmpUGT( - argCountRaw, llvm::ConstantInt::get(cs, llvm::APInt(32, maxPositionalArgCount)), "tooManyArgs"); - auto expected1 = Payload::setExpectedBool(cs, builder, tooManyArgs, false); - builder.CreateCondBr(expected1, argCountFailBlock, argCountSecondCheckBlock); - } else { - builder.CreateBr(argCountSecondCheckBlock); - } - - builder.SetInsertPoint(argCountSecondCheckBlock); - auto tooFewArgs = builder.CreateICmpULT( - argCountRaw, llvm::ConstantInt::get(cs, llvm::APInt(32, minPositionalArgCount)), "tooFewArgs"); - auto expected2 = Payload::setExpectedBool(cs, builder, tooFewArgs, false); - builder.CreateCondBr(expected2, argCountFailBlock, argCountSuccessBlock); - - builder.SetInsertPoint(argCountFailBlock); - Payload::raiseArity(cs, builder, argCountRaw, minPositionalArgCount, - hasRestArgs ? -1 : maxPositionalArgCount); - - builder.SetInsertPoint(argCountSuccessBlock); - } - - vector checkBlocks; - vector fillFromArgBlocks; - vector fillFromDefaultBlocks; - { - // create blocks for arg filling - for (auto i = 0; i < numOptionalArgs; i++) { - auto suffix = to_string(i); - checkBlocks.emplace_back(llvm::BasicBlock::Create(cs, {"checkBlock", suffix}, func)); - fillFromDefaultBlocks.emplace_back( - llvm::BasicBlock::Create(cs, {"fillFromDefaultBlock", suffix}, func)); - fillFromArgBlocks.emplace_back(llvm::BasicBlock::Create(cs, {"fillFromArgBlock", suffix}, func)); - } - - // create "Done" blocks (not needed for fillFromArgBlocks) - auto suffix = "Done" + to_string(numOptionalArgs); - checkBlocks.emplace_back(llvm::BasicBlock::Create(cs, {"checkBlock", suffix}, func)); - fillFromDefaultBlocks.emplace_back( - llvm::BasicBlock::Create(cs, {"fillFromDefaultBlock", suffix}, func)); - } - { - // fill local variables from args - auto fillRequiredArgs = llvm::BasicBlock::Create(cs, "fillRequiredArgs", func); - builder.CreateBr(fillRequiredArgs); - builder.SetInsertPoint(fillRequiredArgs); - - for (auto i = 0; i < maxPositionalArgCount; i++) { - if (i >= minPositionalArgCount) { - // if these are optional, put them in their own BasicBlock - // because we might not run it - auto &block = fillFromArgBlocks[i - minPositionalArgCount]; - builder.SetInsertPoint(block); - } - const auto a = irctx.rubyBlockArgs[rubyRegionId][i]; - if (!a.data(cfg)._name.exists()) { - failCompilation(cs, core::Loc(cs.file, md.declLoc), - "this method has a block argument construct that's not supported"); - } - - // mark the arg as present - auto &argPresent = argPresentVariables[i]; - if (argPresent.exists()) { - Payload::varSet(cs, argPresent, Payload::rubyTrue(cs, builder), builder, irctx, rubyRegionId); - } - - auto name = a.data(cfg)._name.shortName(cs); - llvm::StringRef nameRef(name.data(), name.length()); - auto rawValue = - builder.CreateLoad(builder.CreateConstGEP1_32(argArrayRaw, i), {"rawArg_", nameRef}); - Payload::varSet(cs, a, rawValue, builder, irctx, rubyRegionId); - if (i >= minPositionalArgCount) { - // check if we need to fill in the next variable from the arg - builder.CreateBr(checkBlocks[i - minPositionalArgCount + 1]); - } - } - - // make the last instruction in all the required args point at the first check block - builder.SetInsertPoint(fillRequiredArgs); - // - if (blkArgName.exists() && irctx.blockArgUsage == BlockArgUsage::Captured) { - // TODO: I don't think this correctly handles blocks with block args - Payload::varSet(cs, blkArgName, builder.CreateCall(cs.getFunction("sorbet_getMethodBlockAsProc")), - builder, irctx, 0); - } - builder.CreateBr(checkBlocks[0]); - } - { - // build check blocks - for (auto i = 0; i < numOptionalArgs; i++) { - auto &block = checkBlocks[i]; - builder.SetInsertPoint(block); - auto argCount = builder.CreateICmpEQ( - argCountRaw, llvm::ConstantInt::get(cs, llvm::APInt(32, i + minPositionalArgCount)), - llvm::Twine("default") + llvm::Twine(i)); - auto expected = Payload::setExpectedBool(cs, builder, argCount, false); - builder.CreateCondBr(expected, fillFromDefaultBlocks[i], fillFromArgBlocks[i]); - } - } - - { - // build fillFromDefaultBlocks - for (auto i = 0; i < numOptionalArgs; i++) { - builder.SetInsertPoint(fillFromDefaultBlocks[i]); - - auto argIndex = i + minPositionalArgCount; - auto argPresent = argPresentVariables[argIndex]; - if (argPresent.exists()) { - Payload::varSet(cs, argPresent, Payload::rubyFalse(cs, builder), builder, irctx, rubyRegionId); - } - - if (isBlock) { - auto a = irctx.rubyBlockArgs[rubyRegionId][argIndex]; - Payload::varSet(cs, a, Payload::rubyNil(cs, builder), builder, irctx, rubyRegionId); - } - - // fall through to the next default arg init block - builder.CreateBr(fillFromDefaultBlocks[i + 1]); - } - } - - { - // Tie up all the "Done" blocks at the end - builder.SetInsertPoint(checkBlocks[numOptionalArgs]); - builder.CreateBr(fillFromDefaultBlocks[numOptionalArgs]); - builder.SetInsertPoint(fillFromDefaultBlocks[numOptionalArgs]); - if (hasRestArgs) { - Payload::varSet(cs, restArgName, - Payload::readRestArgs(cs, builder, maxPositionalArgCount, argCountRaw, argArrayRaw), - builder, irctx, rubyRegionId); - } - if (hasKWArgs) { - // If we have a kwsplat arg, the caller/VM will always make sure that - // our keyword args are rolled up into the splat, so there's no point - // in attempting to take the efficient parsing route. - // - // Blocks also take the splat route always. - if (hasKWRestArgs || rubyRegionId != 0) { - parseKeywordArgsFromKwSplat(cs, builder, irctx, kwRestArgName, hashArgs, maxPositionalArgCount, - argsFlags, rubyRegionId); - } else { - // Due to the wonders of Ruby, we can't always be assured that - // our kwargs are passed directly on the stack. They might - // be in the kwsplat arg that we determined earlier. So we need - // a runtime check to determine how to parse the args. - auto *check = - builder.CreateCall(cs.getFunction("sorbet_can_efficiently_parse_kwargs"), - {hashArgs, func->arg_begin() + 4, func->arg_begin() + 5}, "efficient?"); - auto *efficientBlock = llvm::BasicBlock::Create(cs, "efficientParsing", func); - auto *hashBlock = llvm::BasicBlock::Create(cs, "kwsplatParsing", func); - auto *continuationBlock = llvm::BasicBlock::Create(cs, "continuationBlock", func); - - builder.CreateCondBr(check, efficientBlock, hashBlock); - - builder.SetInsertPoint(efficientBlock); - parseKeywordArgsFromCallData(cs, builder, irctx, kwRestArgName, argCountRaw, argArrayRaw, - hashArgs, maxPositionalArgCount, argsFlags, rubyRegionId); - builder.CreateBr(continuationBlock); - - builder.SetInsertPoint(hashBlock); - parseKeywordArgsFromKwSplat(cs, builder, irctx, kwRestArgName, hashArgs, maxPositionalArgCount, - argsFlags, rubyRegionId); - builder.CreateBr(continuationBlock); - - builder.SetInsertPoint(continuationBlock); - } - } - } - } - - switch (blockType) { - case FunctionType::Method: - case FunctionType::StaticInitFile: - case FunctionType::StaticInitModule: - case FunctionType::Block: - case FunctionType::ExceptionBegin: - case FunctionType::Rescue: - case FunctionType::Ensure: - // jump dirrectly to user body - builder.CreateBr(irctx.userEntryBlockByFunction[rubyRegionId]); - break; - - case FunctionType::Unused: - // this function will never be called - builder.CreateUnreachable(); - break; - } - } -} - -cfg::LocalRef returnValue(cfg::CFG &cfg, CompilerState &cs) { - return cfg.enterLocal({core::Names::returnValue(), 1}); -} - -llvm::BasicBlock *resolveJumpTarget(cfg::CFG &cfg, const IREmitterContext &irctx, const cfg::BasicBlock *from, - const cfg::BasicBlock *to) { - if (to == cfg.deadBlock()) { - return irctx.deadBlockMapping[from->rubyRegionId]; - } - - auto remapped = irctx.basicBlockJumpOverrides[to->id]; - if (from->rubyRegionId != irctx.basicBlockRubyBlockId[remapped]) { - return irctx.blockExitMapping[from->rubyRegionId]; - } else { - return irctx.llvmBlocksBySorbetBlocks[remapped]; - } -} - -void emitUserBody(CompilerState &base, cfg::CFG &cfg, const IREmitterContext &irctx) { - llvm::IRBuilder<> builder(base); - auto startLoc = cfg.symbol.data(base)->loc(); - auto &arguments = cfg.symbol.data(base)->arguments; - for (auto it = cfg.forwardsTopoSort.rbegin(); it != cfg.forwardsTopoSort.rend(); ++it) { - cfg::BasicBlock *bb = *it; - auto cs = base.withFunctionEntry(irctx.functionInitializersByFunction[bb->rubyRegionId]); - - auto block = irctx.llvmBlocksBySorbetBlocks[bb->id]; - bool isTerminated = false; - - builder.SetInsertPoint(block); - - // NOTE: explicitly clearing debug location information here so that we don't accidentally inherit the location - // information from blocks in different target functions. - builder.SetCurrentDebugLocation(llvm::DebugLoc()); - - core::Loc lastLoc; - if (bb == cfg.deadBlock()) { - continue; - } - - for (cfg::Binding &bind : bb->exprs) { - auto loc = core::Loc(cs.file, bind.loc); - - lastLoc = Payload::setLineNumber(cs, builder, loc, startLoc, lastLoc, - irctx.lineNumberPtrsByFunction[bb->rubyRegionId]); - - IREmitterHelpers::emitDebugLoc(cs, builder, irctx, bb->rubyRegionId, loc); - - typecase( - bind.value, - [&](cfg::Ident &i) { - auto var = Payload::varGet(cs, i.what, builder, irctx, bb->rubyRegionId); - Payload::varSet(cs, bind.bind.variable, var, builder, irctx, bb->rubyRegionId); - }, - [&](cfg::Alias &i) { - // We compute the alias map when IREmitterContext is first created, so if an entry is missing, - // there's a problem. - ENFORCE(irctx.aliases.find(bind.bind.variable) != irctx.aliases.end(), - "Alias is missing from the alias map"); - }, - [&](cfg::SolveConstraint &i) { - auto var = Payload::varGet(cs, i.send, builder, irctx, bb->rubyRegionId); - Payload::varSet(cs, bind.bind.variable, var, builder, irctx, bb->rubyRegionId); - }, - [&](cfg::Send &i) { - std::optional blk; - if (i.link != nullptr) { - blk.emplace(i.link->rubyRegionId); - } - auto mcctx = MethodCallContext::create(cs, builder, irctx, bb->rubyRegionId, &i, blk); - auto rawCall = IREmitterHelpers::emitMethodCall(mcctx); - mcctx.finalize(); - Payload::varSet(cs, bind.bind.variable, rawCall, builder, irctx, bb->rubyRegionId); - }, - [&](cfg::Return &i) { - isTerminated = true; - - auto *var = Payload::varGet(cs, i.what.variable, builder, irctx, bb->rubyRegionId); - bool hasBlockAncestor = false; - int rubyRegionId = bb->rubyRegionId; - - while (rubyRegionId != 0) { - // We iterate over the entire ancestor chain instead of breaking out early - // when we hit a Ruby block. We do this so we can check this ENFORCE and - // ensure that we're not throwing over something that would require postprocessing. - ENFORCE(!functionTypeNeedsPostprocessing(irctx.rubyBlockType[rubyRegionId])); - hasBlockAncestor = hasBlockAncestor || irctx.rubyBlockType[rubyRegionId] == FunctionType::Block; - rubyRegionId = irctx.rubyBlockParent[rubyRegionId]; - } - - if (hasBlockAncestor) { - ENFORCE(irctx.hasReturnAcrossBlock); - IREmitterHelpers::emitReturnAcrossBlock(cs, cfg, builder, irctx, bb->rubyRegionId, var); - } else { - IREmitterHelpers::emitReturn(cs, builder, irctx, bb->rubyRegionId, var); - } - }, - [&](cfg::BlockReturn &i) { - ENFORCE(bb->rubyRegionId != 0, "should never happen"); - isTerminated = true; - auto var = Payload::varGet(cs, i.what.variable, builder, irctx, bb->rubyRegionId); - IREmitterHelpers::emitReturn(cs, builder, irctx, bb->rubyRegionId, var); - }, - [&](cfg::LoadSelf &i) { - // it's done in function setup, no need to do anything here - }, - [&](cfg::Literal &i) { - auto rawValue = IREmitterHelpers::emitLiteralish(cs, builder, i.value); - Payload::varSet(cs, bind.bind.variable, rawValue, builder, irctx, bb->rubyRegionId); - }, - [&](cfg::GetCurrentException &i) { - // if this block isn't an exception block header, there's nothing to do here. - auto bodyRubyRegionId = irctx.exceptionBlockHeader[bb->id]; - if (bodyRubyRegionId == 0) { - return; - } - - IREmitterHelpers::emitExceptionHandlers(cs, builder, irctx, bb->rubyRegionId, bodyRubyRegionId, - bind.bind.variable); - }, - [&](cfg::ArgPresent &i) { - ENFORCE(bb->rubyRegionId == 0, "ArgPresent found outside of entry-method"); - // Intentionally omitted: the result of the ArgPresent call is filled out in `setupArguments` - }, - [&](cfg::LoadArg &i) { - ENFORCE(bb->rubyRegionId == 0, "LoadArg found outside of entry-method"); - - // Argument values are loaded by `setupArguments`, we just need to check their type here - auto &argInfo = arguments[i.argId]; - // The runtime doesn't check types for blocks, so we don't either. - if (argInfo.flags.isBlock) { - return; - } - - auto local = irctx.rubyBlockArgs[0][i.argId]; - auto var = Payload::varGet(cs, local, builder, irctx, 0); - if (auto &expectedType = argInfo.type) { - auto description = fmt::format("Parameter '{}'", bind.bind.variable.toString(cs, cfg)); - if (argInfo.flags.isRepeated && !argInfo.flags.isKeyword) { - // Signature types for rest arguments apply to each individual element of - // the rest arg, not the actual argument itself. - IREmitterHelpers::emitTypeTestForRestArg(cs, builder, var, expectedType, description); - } else { - IREmitterHelpers::emitTypeTest(cs, builder, var, expectedType, description); - } - } - }, - [&](cfg::LoadYieldParams &i) { - ENFORCE(bb->rubyRegionId != 0, "LoadYieldParams found outside of ruby block"); - /* intentionally omitted, it's part of method preambula */ - }, - [&](cfg::YieldParamPresent &i) { - ENFORCE(bb->rubyRegionId != 0, "YieldParamPresent found outside of ruby block"); - // Intentionally omitted: the result of the YieldParamPresent call is filled out in `setupArguments` - }, - [&](cfg::YieldLoadArg &i) { - ENFORCE(bb->rubyRegionId != 0, "YieldLoadArg found outside of ruby block"); - // Filled out as part of the method preamble. - }, - [&](cfg::Cast &i) { - auto val = Payload::varGet(cs, i.value.variable, builder, irctx, bb->rubyRegionId); - - // We skip the type test for Cast instructions that assign into . - // These instructions only exist in the CFG for the purpose of type checking. - // The Ruby VM already checks that self is a valid type when calling `.bind()` - // on an UnboundMethod object. - auto skipTypeTest = bind.bind.variable.data(cfg) == core::LocalVariable::selfVariable() || - i.cast == core::Names::assumeType(); - - if (!skipTypeTest) { - IREmitterHelpers::emitTypeTest(cs, builder, val, bind.bind.type, - fmt::format("T.{}", i.cast.shortName(cs))); - } - - if (i.cast == core::Names::let() || i.cast == core::Names::cast() || - i.cast == core::Names::assumeType()) { - Payload::varSet(cs, bind.bind.variable, val, builder, irctx, bb->rubyRegionId); - } else if (i.cast == core::Names::assertType()) { - Payload::varSet(cs, bind.bind.variable, Payload::rubyFalse(cs, builder), builder, irctx, - bb->rubyRegionId); - } - }, - [&](cfg::TAbsurd &i) { - auto val = Payload::varGet(cs, i.what.variable, builder, irctx, bb->rubyRegionId); - builder.CreateCall(cs.getFunction("sorbet_t_absurd"), {val}); - }); - if (isTerminated) { - break; - } - } - if (!isTerminated) { - auto *thenb = resolveJumpTarget(cfg, irctx, bb, bb->bexit.thenb); - auto *elseb = resolveJumpTarget(cfg, irctx, bb, bb->bexit.elseb); - - if (thenb != elseb && bb->bexit.cond.variable != cfg::LocalRef::blockCall()) { - // If the condition is a class variable reference, we can't just access it - // directly, because it might not have been defined yet, and accessing - // class variables prior to their definition is an error. - // - // The VM will insert an explicit `defined?(@@var)` check for `@@var ||= ...`. - // We desugar that expression to explicit conditionals (without the `defined?` - // check, so we have to insert the definedness check for safety. - // - // If you write `if @@var; @@var; else; @@var = ...; end`, the VM will - // test for truthiness directly without doing through `defined?`. Even - // so, the translation we're doing here is just as efficient: the VM would - // do a class variable lookup and truthiness test, which is exactly the - // same thing as sorbet_classVariableDefinedAndTruthy does. - auto testref = bb->bexit.cond.variable; - auto aliasEntry = irctx.aliases.find(testref); - llvm::Value *condValue; - if (aliasEntry != irctx.aliases.end() && aliasEntry->second.kind == Alias::AliasKind::ClassField) { - auto klass = Payload::getClassVariableStoreClass(cs, builder, irctx); - auto id = Payload::idIntern(cs, builder, aliasEntry->second.classField.shortName(cs)); - condValue = builder.CreateCall(cs.getFunction("sorbet_classVariableDefinedAndTruthy"), {klass, id}); - } else { - auto var = Payload::varGet(cs, testref, builder, irctx, bb->rubyRegionId); - condValue = Payload::testIsTruthy(cs, builder, var); - } - - builder.CreateCondBr(condValue, thenb, elseb); - } else { - builder.CreateBr(thenb); - } - } - } -} - -void emitDeadBlocks(CompilerState &cs, cfg::CFG &cfg, const IREmitterContext &irctx) { - llvm::IRBuilder<> builder(cs); - - // Emit the dead block body for each ruby block. It should be an error to transition to the dead block, so - // we mark its body as unreachable. - for (auto rubyRegionId = 0; rubyRegionId <= cfg.maxRubyRegionId; ++rubyRegionId) { - auto *dead = irctx.deadBlockMapping[rubyRegionId]; - builder.SetInsertPoint(dead); - builder.CreateUnreachable(); - } -} - -void emitBlockExits(CompilerState &base, cfg::CFG &cfg, const IREmitterContext &irctx) { - llvm::IRBuilder<> builder(base); - - for (auto rubyRegionId = 0; rubyRegionId <= cfg.maxRubyRegionId; ++rubyRegionId) { - auto cs = base.withFunctionEntry(irctx.functionInitializersByFunction[rubyRegionId]); - - builder.SetInsertPoint(irctx.blockExitMapping[rubyRegionId]); - - switch (irctx.rubyBlockType[rubyRegionId]) { - case FunctionType::Method: - case FunctionType::StaticInitFile: - case FunctionType::StaticInitModule: - builder.CreateUnreachable(); - break; - - case FunctionType::Block: - case FunctionType::ExceptionBegin: - case FunctionType::Rescue: - case FunctionType::Ensure: - case FunctionType::Unused: - // for non-top-level functions, we return `Qundef` to indicate that this value isn't used for anything. - IREmitterHelpers::emitReturn(cs, builder, irctx, rubyRegionId, Payload::rubyUndef(cs, builder)); - break; - } - } -} - -void emitPostProcess(CompilerState &cs, cfg::CFG &cfg, const IREmitterContext &irctx) { - llvm::IRBuilder<> builder(cs); - builder.SetInsertPoint(irctx.postProcessBlock); - - // we're only using the top-level ruby block at this point - auto rubyRegionId = 0; - - auto var = Payload::varGet(cs, returnValue(cfg, cs), builder, irctx, rubyRegionId); - auto *maybeChecked = IREmitterHelpers::maybeCheckReturnValue(cs, cfg, builder, irctx, var); - - IREmitterHelpers::emitUncheckedReturn(cs, builder, irctx, rubyRegionId, maybeChecked); -} - -// Direct wrappers call the wrapped function without checking the receiver's type; the caller is responsible for -// guarding the call to the wrapper with appropriate validation of the receiver. -void emitDirectWrapper(CompilerState &cs, const ast::MethodDef &md, const IREmitterContext &irctx) { - llvm::IRBuilder<> builder(cs); - - auto *wrapper = IREmitterHelpers::getOrCreateDirectWrapper(cs, md.symbol); - auto *cache = wrapper->arg_begin(); - auto *argc = wrapper->arg_begin() + 1; - auto *argv = wrapper->arg_begin() + 2; - auto *self = wrapper->arg_begin() + 3; - - cache->setName("cache"); - argc->setName("argc"); - argv->setName("argv"); - self->setName("self"); - - auto *entry = llvm::BasicBlock::Create(cs, "entry", wrapper); - builder.SetInsertPoint(entry); - - auto *target = irctx.rubyBlocks2Functions[0]; - auto *stackFrame = builder.CreateLoad(Payload::rubyStackFrameVar(cs, builder, irctx, md.symbol)); - auto *res = Payload::callFuncDirect(cs, builder, cache, target, argc, argv, self, stackFrame); - builder.CreateRet(res); -} - -void IREmitter::run(CompilerState &cs, cfg::CFG &cfg, const ast::MethodDef &md) { - Timer timer(cs.gs.tracer(), "IREmitter::run"); - cfg::CFG::UnfreezeCFGLocalVariables unfreezeVars(cfg); - - llvm::Function *func; - - if (IREmitterHelpers::isClassStaticInit(cs, md.symbol)) { - func = IREmitterHelpers::getOrCreateStaticInit(cs, md.symbol, md.declLoc); - } else { - func = IREmitterHelpers::getOrCreateFunction(cs, md.symbol); - } - func = IREmitterHelpers::cleanFunctionBody(cs, func); - { - // setup function argument names - func->arg_begin()->setName("argc"); - (func->arg_begin() + 1)->setName("argArray"); - (func->arg_begin() + 2)->setName("selfRaw"); - (func->arg_begin() + 3)->setName("cfp"); - (func->arg_begin() + 4)->setName("calling"); - (func->arg_begin() + 5)->setName("callData"); - } - func->addFnAttr(llvm::Attribute::AttrKind::StackProtectReq); - func->addFnAttr(llvm::Attribute::AttrKind::NoUnwind); - func->addFnAttr(llvm::Attribute::AttrKind::UWTable); - llvm::IRBuilder<> builder(cs); - - const IREmitterContext irctx = IREmitterContext::getSorbetBlocks2LLVMBlockMapping(cs, cfg, md, func); - - ENFORCE(cs.functionEntryInitializers == nullptr, "modules shouldn't be reused"); - - setupStackFrames(cs, md, irctx); - setupArguments(cs, cfg, md, irctx); - - emitUserBody(cs, cfg, irctx); - emitDeadBlocks(cs, cfg, irctx); - emitBlockExits(cs, cfg, irctx); - emitPostProcess(cs, cfg, irctx); - - if (md.symbol.data(cs)->flags.isFinal) { - emitDirectWrapper(cs, md, irctx); - } - - // Link the function initializer blocks. - for (int funId = 0; funId < irctx.functionInitializersByFunction.size(); funId++) { - llvm::BasicBlock *nextBlock = irctx.argumentSetupBlocksByFunction[funId]; - builder.SetInsertPoint(irctx.functionInitializersByFunction[funId]); - builder.CreateBr(nextBlock); - } - - cs.debug->finalize(); - - /* run verifier */ - if (debug_mode && llvm::verifyFunction(*func, &llvm::errs())) { - fmt::print("failed to verify:\n"); - func->dump(); - ENFORCE(false); - } - cs.runCheapOptimizations(func); - - // If we are ever returning across blocks, we need to wrap the entire execution - // of the function in an unwind-protect region that knows about the return. - if (!irctx.hasReturnAcrossBlock) { - return; - } - - llvm::ValueToValueMapTy vMap; - auto *implementationFunction = llvm::CloneFunction(func, vMap); - - // Completely delete the function body. - func->dropAllReferences(); - - // Turn the original function into a trampoline for this new function. - auto *entryBlock = llvm::BasicBlock::Create(cs, "entry", func); - builder.SetInsertPoint(entryBlock); - - auto *wrapper = cs.getFunction("sorbet_vm_return_from_block_wrapper"); - // Adding the function argument at the end means that we don't have to perform - // any register shuffling. - auto *status = - builder.CreateCall(wrapper, - {func->arg_begin(), func->arg_begin() + 1, func->arg_begin() + 2, func->arg_begin() + 3, - func->arg_begin() + 4, func->arg_begin() + 5, implementationFunction}, - "returnedFromBlock"); - - // TODO(froydnj): LLVM IR is somewhat machine-specific when it comes to calling - // functions returning a structure, like sorbet_vm_return_from_block_wrapper. - // The conventions we have here are correct for x86-64 (Linux and Mac), but the - // LLVM IR generated by clang for returning the same structure on arm64 Linux - // returns a 2 x i64 vector. If the structure was slightly bigger, the function - // would actually take a pointer to the structure as a separate argument. - // A different architecture might take a pointer always, regardless of how the - // structure was laid out. - // - // It's entirely possible that having the Sorbet compiler generate code for a - // non-x86-64 architecture would require reworking more stuff than just this - // bit of code. But this particular bit seems like an easy place to overlook - // and puzzle about why things are going wrong. So try and provide a little - // advance notice to the would-be porter. - if (debug_mode) { - std::string error; - const auto &targetTriple = cs.module->getTargetTriple(); - auto triple = llvm::Triple(targetTriple); - ENFORCE(triple.getArch() == llvm::Triple::x86_64); - } - // Also make sure that the return value is small enough to be returned in - // registers, i.e. that the function is actually returning a value directly. - ENFORCE(!status->getType()->isVoidTy()); - - // If we received this return value via throwing (i.e. return-from-block), we - // didn't typecheck the value when it was thrown, so we need to do it here. - auto *returnValue = builder.CreateExtractValue(status, {0}, "returnedValue"); - auto *wasThrown = builder.CreateExtractValue(status, {1}, "wasThrown"); - auto *typecheckBlock = llvm::BasicBlock::Create(cs, "typecheck", func); - auto *exitBlock = llvm::BasicBlock::Create(cs, "exit", func); - builder.CreateCondBr(builder.CreateICmpEQ(wasThrown, builder.getInt8(1)), typecheckBlock, exitBlock); - - builder.SetInsertPoint(typecheckBlock); - auto *checkedValue = IREmitterHelpers::maybeCheckReturnValue(cs, cfg, builder, irctx, returnValue); - typecheckBlock = builder.GetInsertBlock(); - builder.CreateBr(exitBlock); - - builder.SetInsertPoint(exitBlock); - auto *returnValuePhi = builder.CreatePHI(returnValue->getType(), 2, "value"); - returnValuePhi->addIncoming(returnValue, entryBlock); - returnValuePhi->addIncoming(checkedValue, typecheckBlock); - builder.CreateRet(returnValuePhi); - - // Redo verifier on our new function. - if (debug_mode && llvm::verifyFunction(*func, &llvm::errs())) { - fmt::print("failed to verify:\n"); - func->dump(); - ENFORCE(false); - } - cs.runCheapOptimizations(func); -} - -void IREmitter::buildInitFor(CompilerState &cs, const core::MethodRef &sym, string_view objectName) { - llvm::IRBuilder<> builder(cs); - - auto owner = sym.data(cs)->owner; - auto isRoot = IREmitterHelpers::isRootishSymbol(cs, owner); - llvm::Function *entryFunc; - - if (IREmitterHelpers::isFileOrClassStaticInit(cs, sym)) { - if (!isRoot) { - return; - } - - // for a path like `foo/bar/baz.rb`, we want the baseName to be just `baz`, as ruby will be looking for an init - // function called `Init_baz` - auto baseName = objectName.substr(0, objectName.rfind(".rb")); - auto slash = baseName.rfind('/'); - if (slash != string_view::npos) { - baseName.remove_prefix(slash + 1); - } - - auto linkageType = llvm::Function::ExternalLinkage; - std::vector NoArgs(0, llvm::Type::getVoidTy(cs)); - auto ft = llvm::FunctionType::get(llvm::Type::getVoidTy(cs), NoArgs, false); - entryFunc = llvm::Function::Create(ft, linkageType, "Init_" + string(baseName), *cs.module); - } else { - entryFunc = IREmitterHelpers::getInitFunction(cs, sym); - } - - auto bb = llvm::BasicBlock::Create(cs, "entry", entryFunc); - builder.SetInsertPoint(bb); - - if (IREmitterHelpers::isFileOrClassStaticInit(cs, sym)) { - // We include sorbet_version.c when compiling the Sorbet Compiler itself to get the expected version. - // The actual version will be linked into libruby.so and compared against at runtime. - auto compileTimeBuildSCMRevision = sorbet_getBuildSCMRevision(); - auto compileTimeIsReleaseBuild = sorbet_getIsReleaseBuild(); - builder.CreateCall(cs.getFunction("sorbet_ensureSorbetRuby"), - { - llvm::ConstantInt::get(cs, llvm::APInt(32, compileTimeIsReleaseBuild, true)), - Payload::toCString(cs, compileTimeBuildSCMRevision, builder), - }); - - auto realpath = builder.CreateCall(cs.getFunction("sorbet_readRealpath"), {}); - realpath->setName("realpath"); - - builder.CreateCall(cs.getFunction("sorbet_globalConstructors"), {realpath}); - - core::MethodRef staticInit = cs.gs.lookupStaticInitForFile(sym.data(cs)->loc().file()); - - // Call the LLVM method that was made by run() from this Init_ method - auto staticInitName = IREmitterHelpers::getFunctionName(cs, staticInit); - auto staticInitFunc = cs.getFunction(staticInitName); - ENFORCE(staticInitFunc, "{} does not exist", staticInitName); - builder.CreateCall(staticInitFunc, - { - llvm::ConstantInt::get(cs, llvm::APInt(32, 0, true)), - llvm::ConstantPointerNull::get(llvm::Type::getInt64PtrTy(cs)), - Payload::rubyTopSelf(cs, builder), - builder.CreateCall(cs.getFunction("sorbet_getCFP"), {}, "cfpTop"), - llvm::ConstantPointerNull::get(llvm::Type::getInt8PtrTy(cs)), - llvm::ConstantPointerNull::get(llvm::Type::getInt8PtrTy(cs)), - }, - staticInitName); - } - - builder.CreateRetVoid(); - - if (debug_mode && llvm::verifyFunction(*entryFunc, &llvm::errs())) { - fmt::print("failed to verify:\n"); - entryFunc->dump(); - ENFORCE(false); - } - cs.runCheapOptimizations(entryFunc); -} - -} // namespace sorbet::compiler diff --git a/compiler/IREmitter/IREmitter.h b/compiler/IREmitter/IREmitter.h deleted file mode 100644 index 9b625eca50..0000000000 --- a/compiler/IREmitter/IREmitter.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef SORBET_COMPILER_LLVM_IR_EMITTER_H -#define SORBET_COMPILER_LLVM_IR_EMITTER_H -#include "compiler/Core/ForwardDeclarations.h" -#include - -namespace sorbet::compiler { -class CompilerState; -class IREmitter { -public: - static void run(CompilerState &, cfg::CFG &cfg, const ast::MethodDef &md); - static void buildInitFor(CompilerState &gs, const core::MethodRef &sym, std::string_view objectName); -}; -} // namespace sorbet::compiler -#endif diff --git a/compiler/IREmitter/IREmitterContext.cc b/compiler/IREmitter/IREmitterContext.cc deleted file mode 100644 index 5d3ed9a379..0000000000 --- a/compiler/IREmitter/IREmitterContext.cc +++ /dev/null @@ -1,1164 +0,0 @@ -// These violate our poisons so have to happen first -#include "llvm/IR/DIBuilder.h" -#include "llvm/IR/DerivedTypes.h" // FunctionType -#include "llvm/IR/IRBuilder.h" - -#include "Payload.h" -#include "absl/base/casts.h" -#include "absl/strings/match.h" -#include "absl/strings/str_replace.h" -#include "ast/Helpers.h" -#include "ast/ast.h" -#include "cfg/CFG.h" -#include "common/sort/sort.h" -#include "compiler/Core/CompilerState.h" -#include "compiler/IREmitter/CFGHelpers.h" -#include "compiler/IREmitter/IREmitterContext.h" -#include "compiler/IREmitter/IREmitterHelpers.h" -#include "compiler/IREmitter/MethodCallContext.h" - -using namespace std; -namespace sorbet::compiler { - -namespace { - -optional isSymbol(const core::GlobalState &gs, const cfg::InstructionPtr &insn) { - auto *liti = cfg::cast_instruction(insn); - if (liti == nullptr) { - return std::nullopt; - } - - if (!core::isa_type(liti->value)) { - return std::nullopt; - } - - const auto &lit = core::cast_type_nonnull(liti->value); - if (lit.literalKind != core::NamedLiteralType::LiteralTypeKind::Symbol) { - return std::nullopt; - } - - return lit.asName().shortName(gs); -} - -struct AliasesAndKeywords { - UnorderedMap aliases; - UnorderedMap symbols; -}; - -// Iterate over all instructions in the CFG, populating the alias map. -AliasesAndKeywords setupAliasesAndKeywords(CompilerState &cs, const cfg::CFG &cfg) { - AliasesAndKeywords res; - - for (auto &bb : cfg.basicBlocks) { - for (auto &bind : bb->exprs) { - if (auto *i = cfg::cast_instruction(bind.value)) { - ENFORCE(res.aliases.find(bind.bind.variable) == res.aliases.end(), - "Overwriting an entry in the aliases map"); - - if (i->what == core::Symbols::Magic_undeclaredFieldStub()) { - // When `i->what` is undeclaredFieldStub, `i->name` is populated - auto name = i->name.shortName(cs); - if (name.size() > 2 && name[0] == '@' && name[1] == '@') { - res.aliases[bind.bind.variable] = Alias::forClassField(i->name); - } else if (name.size() > 1 && name[0] == '@') { - res.aliases[bind.bind.variable] = Alias::forInstanceField(i->name); - } else if (name.size() > 1 && name[0] == '$') { - res.aliases[bind.bind.variable] = Alias::forGlobalField(i->name); - } else { - ENFORCE(stoi(string(name)) > 0, "'{}' is not a valid global name", name); - res.aliases[bind.bind.variable] = Alias::forGlobalField(i->name); - } - } else { - // It's currently impossible in Sorbet to declare a global field with a T.let - // (they will all be Magic_undeclaredFieldStub) - auto name = i->what.name(cs); - auto shortName = name.shortName(cs); - ENFORCE(!(shortName.size() > 0 && shortName[0] == '$')); - - if (i->what.isField(cs)) { - res.aliases[bind.bind.variable] = Alias::forInstanceField(name); - } else if (i->what.isStaticField(cs)) { - if (shortName.size() > 2 && shortName[0] == '@' && shortName[1] == '@') { - res.aliases[bind.bind.variable] = Alias::forClassField(name); - } else { - res.aliases[bind.bind.variable] = Alias::forConstant(i->what); - } - } else { - res.aliases[bind.bind.variable] = Alias::forConstant(i->what); - } - } - } else if (auto sym = isSymbol(cs, bind.value)) { - res.symbols[bind.bind.variable] = sym.value(); - } - } - } - - return res; -} - -// Iterate over all instructions in the CFG, determining which ruby blocks use `break` -vector blocksThatUseBreak(CompilerState &cs, const cfg::CFG &cfg) { - vector res(cfg.maxRubyRegionId + 1, false); - - for (auto &bb : cfg.basicBlocks) { - for (auto &bind : bb->exprs) { - if (bind.bind.variable.data(cfg)._name == core::Names::blockBreak()) { - res[bb->rubyRegionId] = true; - break; - } - } - } - - return res; -} - -std::tuple, UnorderedMap> -setupLocalVariables(CompilerState &cs, cfg::CFG &cfg, const UnorderedMap &variablesPrivateToBlocks, - const IREmitterContext &irctx) { - UnorderedMap llvmVariables; - llvm::IRBuilder<> builder(cs); - { - // nill out block local variables. - auto valueType = cs.getValueType(); - vector> variablesPrivateToBlocksSorted; - - for (const auto &entry : variablesPrivateToBlocks) { - variablesPrivateToBlocksSorted.emplace_back(entry); - } - fast_sort(variablesPrivateToBlocksSorted, - [](const auto &left, const auto &right) -> bool { return left.first.id() < right.first.id(); }); - for (const auto &entry : variablesPrivateToBlocksSorted) { - auto var = entry.first; - auto svName = var.data(cfg)._name.shortName(cs); - builder.SetInsertPoint(irctx.functionInitializersByFunction[entry.second]); - auto alloca = llvmVariables[var] = - builder.CreateAlloca(valueType, nullptr, llvm::StringRef(svName.data(), svName.length())); - auto nilValueRaw = Payload::rubyNil(cs, builder); - Payload::boxRawValue(cs, builder, alloca, nilValueRaw); - } - } - - // reserve self in all basic blocks. We rely on LLVM to delete any allocas - // we wind up not actually needing. - UnorderedMap selfVariables; - for (int i = 0; i <= cfg.maxRubyRegionId; ++i) { - builder.SetInsertPoint(irctx.functionInitializersByFunction[i]); - auto var = cfg::LocalRef::selfVariable(); - auto nameStr = var.toString(cs, cfg); - selfVariables[i] = - builder.CreateAlloca(cs.getValueType(), nullptr, llvm::StringRef(nameStr.data(), nameStr.length())); - } - - { - // reserve the magical return value - builder.SetInsertPoint(irctx.functionInitializersByFunction[0]); - auto name = core::Names::returnValue(); - auto var = cfg.enterLocal(core::LocalVariable{name, 1}); - auto nameStr = name.toString(cs); - llvmVariables[var] = - builder.CreateAlloca(cs.getValueType(), nullptr, llvm::StringRef(nameStr.data(), nameStr.length())); - } - - return {std::move(llvmVariables), std::move(selfVariables)}; -} - -struct CapturedVariables { - // LocalRefs that are only referenced from a single block. Maps from variables - // to the block in which they are referenced. - UnorderedMap privateVariables; - - // LocalRefs referenced from multiple blocks. Maps from variables to their index - // in the local variable area on the Ruby stack. - UnorderedMap escapedVariableIndexes; - - // Whether the ruby method uses a block argument. - BlockArgUsage usesBlockArgs; -}; - -class CaptureContext { -public: - enum class Kind { - MethodArgument, - Receiver, - SendArgument, - General, - }; - -private: - CaptureContext(Kind kind) : kind(kind) {} - CaptureContext(Kind kind, cfg::Send *send) : kind(kind), send(send) {} - -public: - const Kind kind; - const cfg::Send *send = nullptr; - - static CaptureContext methodArg() { - return CaptureContext(Kind::MethodArgument); - } - static CaptureContext receiver(cfg::Send *send) { - return CaptureContext(Kind::Receiver, send); - } - static CaptureContext sendArg(cfg::Send *send) { - return CaptureContext(Kind::SendArgument, send); - } - static CaptureContext general() { - return CaptureContext(Kind::General); - } -}; - -// Bundle up a bunch of state used for capture tracking to simplify the interface in findCaptures below. -class TrackCaptures final { - BlockArgUsage determineUsage(cfg::BasicBlock *bb, cfg::LocalRef lv, LocalUsedHow use, CaptureContext context) { - // Once captured, always captured. - if (blockArgUsage == BlockArgUsage::Captured) { - return BlockArgUsage::Captured; - } - - // Writes to blocks would be unusual, so don't do anything fancy. - if (use == LocalUsedHow::WrittenTo) { - return BlockArgUsage::Captured; - } - ENFORCE(use == LocalUsedHow::ReadOnly); - - if (context.kind == CaptureContext::Kind::General) { - return BlockArgUsage::Captured; - } - - if (context.kind == CaptureContext::Kind::MethodArgument) { - ENFORCE(bb->rubyRegionId == 0); - ENFORCE(blockArgUsage == BlockArgUsage::None); - return BlockArgUsage::SameFrameAsTopLevel; - } - - ENFORCE(blockArgUsage == BlockArgUsage::SameFrameAsTopLevel); - ENFORCE(context.kind == CaptureContext::Kind::Receiver || context.kind == CaptureContext::Kind::SendArgument); - - // If we're in a block that wouldn't have the same frame as the toplevel, - // the block is captured. - // - // TODO: this needs to be move sophisticated in the case of blocks taking - // blocks as arguments. - if (blockLevels[bb->rubyRegionId] != 0) { - return BlockArgUsage::Captured; - } - - // Sending `call` to a block is how we represent `yield`, and does not capture - // the block. - if (context.kind == CaptureContext::Kind::Receiver && context.send->fun == core::Names::call()) { - // We should have called this via call-with-block. - ENFORCE(context.send->link == nullptr); - return BlockArgUsage::SameFrameAsTopLevel; - } - - // If we are the distinguished block argument to ., - // that does not capture the block. - // - // TODO: handle call-with-splat-and-block. - if (context.kind == CaptureContext::Kind::SendArgument && context.send->fun == core::Names::callWithBlock() && - context.send->args[2].variable == lv && context.send->args[0].variable != lv) { - return BlockArgUsage::SameFrameAsTopLevel; - } - - // Anything else captures the block. - return BlockArgUsage::Captured; - } - - struct PrivateUse { - optional rubyRegionId; - LocalUsedHow used; - core::TypePtr type; - }; - - void trackBlockUsage(cfg::BasicBlock *bb, cfg::LocalRef lv, const core::TypePtr &type, LocalUsedHow use, - CaptureContext context) { - if (lv == cfg::LocalRef::selfVariable()) { - return; - } - // Blocks are special, because they have specific support in the Ruby VM that - // we want to re-use as much as possible: converting the blocks into an - // explicit Ruby value (e.g. rb_block_proc()) is expensive. - // - // Thus, this separate tracking for block arguments. - if (lv == blkArg) { - blockArgUsage = determineUsage(bb, lv, use, context); - return; - } - auto fnd = privateUsages.find(lv); - if (fnd != privateUsages.end()) { - auto &store = fnd->second; - if (store.rubyRegionId.has_value()) { - if (store.rubyRegionId.value() != bb->rubyRegionId) { - store.rubyRegionId = nullopt; - LocalUsedHow how = use == LocalUsedHow::ReadOnly ? store.used : LocalUsedHow::WrittenTo; - escapedIndexes[lv] = EscapedUse{escapedIndexCounter, how, store.type}; - escapedIndexCounter += 1; - } else if (use == LocalUsedHow::WrittenTo) { - store.used = LocalUsedHow::WrittenTo; - } - } else { - // If this ref exists in privateUsages, but does not have an associated - // rubyRegionId, then it must have escaped, and we need to update its - // status there. - const auto &escaped = escapedIndexes.find(lv); - ENFORCE(escaped != escapedIndexes.end()); - if (use == LocalUsedHow::WrittenTo) { - escaped->second.used = LocalUsedHow::WrittenTo; - } - } - } else { - privateUsages[lv] = PrivateUse{bb->rubyRegionId, use, type}; - } - } - -public: - UnorderedMap privateUsages; - UnorderedMap escapedIndexes; - int escapedIndexCounter = 0; - BlockArgUsage blockArgUsage = BlockArgUsage::None; - cfg::LocalRef blkArg = cfg::LocalRef::noVariable(); - const UnorderedMap &aliases; - const vector &blockLevels; - - TrackCaptures(const UnorderedMap &aliases, const vector &blockLevels) - : aliases(aliases), blockLevels(blockLevels) {} - - void trackBlockRead(cfg::BasicBlock *bb, cfg::LocalRef lv, CaptureContext context = CaptureContext::general()) { - trackBlockUsage(bb, lv, nullptr, LocalUsedHow::ReadOnly, context); - } - - void trackBlockWrite(cfg::BasicBlock *bb, cfg::LocalRef lv, CaptureContext context = CaptureContext::general()) { - trackBlockUsage(bb, lv, nullptr, LocalUsedHow::WrittenTo, context); - } - - void trackBlockArgument(cfg::BasicBlock *bb, cfg::LocalRef lv, const core::TypePtr &type) { - trackBlockUsage(bb, lv, type, LocalUsedHow::ReadOnly, CaptureContext::methodArg()); - } - - CapturedVariables finalize() { - // privateUsages entries that have nullopt values are only interesting to the - // capture analysis process, so remove them - UnorderedMap realPrivateUsages; - for (auto &entry : privateUsages) { - auto &tracker = entry.second; - if (!tracker.rubyRegionId.has_value()) { - continue; - } - - realPrivateUsages[entry.first] = tracker.rubyRegionId.value(); - } - - if (blkArg.exists()) { - // We have been tracking the block argument separately. - ENFORCE(!realPrivateUsages.contains(blkArg)); - ENFORCE(!escapedIndexes.contains(blkArg)); - if (blkArg.exists()) { - ENFORCE(blockArgUsage != BlockArgUsage::None); - } else { - ENFORCE(blockArgUsage == BlockArgUsage::None); - } - - // ...but we still need to note that it is a legitimate local variable - // if it was captured in some way. - if (blockArgUsage == BlockArgUsage::Captured) { - // Assume the worst about how the block is used. - auto how = LocalUsedHow::WrittenTo; - escapedIndexes[blkArg] = EscapedUse{escapedIndexCounter, how, nullptr}; - escapedIndexCounter += 1; - } - } - - return CapturedVariables{std::move(realPrivateUsages), std::move(escapedIndexes), blockArgUsage}; - } -}; - -/* if local variable is only used in block X, it maps the local variable to X, otherwise, it maps local variable to a - * negative number */ -CapturedVariables findCaptures(CompilerState &cs, const ast::MethodDef &mdef, cfg::CFG &cfg, - const UnorderedMap &aliases, - const vector &exceptionHandlingBlockHeaders, const vector &blockLevels) { - TrackCaptures usage(aliases, blockLevels); - - int argId = -1; - auto &methodArguments = cfg.symbol.data(cs)->arguments; - for (auto &arg : mdef.args) { - argId += 1; - ast::Local const *local = nullptr; - if (auto *opt = ast::cast_tree(arg)) { - local = ast::cast_tree(opt->expr); - } else { - local = ast::cast_tree(arg); - } - ENFORCE(local); - auto localRef = cfg.enterLocal(local->localVariable); - auto &argInfo = methodArguments[argId]; - if (cfg.symbol.data(cs)->arguments[argId].flags.isBlock) { - usage.blkArg = localRef; - } - usage.trackBlockArgument(cfg.entry(), localRef, argInfo.type); - } - - for (auto &bb : cfg.basicBlocks) { - for (cfg::Binding &bind : bb->exprs) { - // Despite: - // - // var: $TYPE = LoadArg(var) - // - // looking like a write to var, we don't want to track it as such. We know - // that this (initial) write isn't really the kind of write we care about. - // So we have this to indicate whether we should record the write to - // bind.bind.variable. - bool trackBinding = true; - typecase( - bind.value, [&](cfg::Ident &i) { usage.trackBlockRead(bb.get(), i.what); }, - [&](cfg::Alias &i) { /* nothing */ - }, - [&](cfg::SolveConstraint &i) { /* nothing*/ }, - [&](cfg::Send &i) { - for (auto &arg : i.args) { - usage.trackBlockRead(bb.get(), arg.variable, CaptureContext::sendArg(&i)); - } - usage.trackBlockRead(bb.get(), i.recv.variable, CaptureContext::receiver(&i)); - }, - [&](cfg::GetCurrentException &i) { - // if the current block is an exception header, record a usage of the variable in the else block - // (the body block of the exception handling) to force it to escape. - if (exceptionHandlingBlockHeaders[bb->id] != 0) { - usage.trackBlockRead(bb->bexit.elseb, bind.bind.variable); - } - }, - [&](cfg::Return &i) { usage.trackBlockRead(bb.get(), i.what.variable); }, - [&](cfg::BlockReturn &i) { usage.trackBlockRead(bb.get(), i.what.variable); }, - [&](cfg::LoadSelf &i) { /*nothing*/ /*todo: how does instance exec pass self?*/ }, - [&](cfg::Literal &i) { /* nothing*/ }, [&](cfg::ArgPresent &i) { /*nothing*/ }, - [&](cfg::LoadArg &i) { trackBinding = false; }, [&](cfg::LoadYieldParams &i) { /*nothing*/ }, - [&](cfg::YieldParamPresent &i) { /* nothing */ }, [&](cfg::YieldLoadArg &i) { /* nothing */ }, - [&](cfg::Cast &i) { usage.trackBlockRead(bb.get(), i.value.variable); }, - [&](cfg::TAbsurd &i) { usage.trackBlockRead(bb.get(), i.what.variable); }); - if (trackBinding) { - usage.trackBlockWrite(bb.get(), bind.bind.variable); - } - } - - // no need to track the condition variable if the jump is unconditional - if (bb->bexit.thenb != bb->bexit.elseb) { - usage.trackBlockRead(bb.get(), bb->bexit.cond.variable); - } - } - - return usage.finalize(); -} - -int getMaxSendArgCount(cfg::CFG &cfg) { - int maxSendArgCount = 0; - for (auto &bb : cfg.basicBlocks) { - for (cfg::Binding &bind : bb->exprs) { - if (auto snd = cfg::cast_instruction(bind.value)) { - int numPosArgs = snd->numPosArgs; - int numKwArgs = snd->args.size() - numPosArgs; - - // add one for the receiver when pushing args on the ruby stack - int numArgs = 1 + numPosArgs; - - if (numKwArgs % 2 == 1) { - // Odd keyword args indicate a keyword splat, and in that case we merge all keyword args into a - // single hash as the VM doesn't support mixed kwarg/kwsplat sends. - numArgs += 1; - } else { - // Otherwise the keyword args indicate the number of symbol/value pairs, and since we push only the - // values on the stack the send arg count is increased by the number of keyword args / 2. - numArgs += numKwArgs / 2; - } - - // For backwards compatibility with the fillSendArgArray method of argument passing, the allocated array - // must be large enough to hold all of the inlined keyword arguments when initializing a hash. This - // comes up in cases like `super` that will forward all kwargs as a single hash. - int buildHashArgs = numKwArgs & ~0x1; - - maxSendArgCount = std::max({maxSendArgCount, numArgs, buildHashArgs}); - } - } - } - return maxSendArgCount; -} - -// TODO -llvm::DISubroutineType *getDebugFunctionType(CompilerState &cs, llvm::Function *func) { - vector eltTys; - - auto *valueTy = cs.debug->createBasicType("VALUE", 64, llvm::dwarf::DW_ATE_signed); - eltTys.push_back(valueTy); - - // NOTE: the return type is always the first element in the array - return cs.debug->createSubroutineType(cs.debug->getOrCreateTypeArray(eltTys)); -} - -llvm::DISubprogram *getDebugScope(CompilerState &cs, cfg::CFG &cfg, llvm::DIScope *parent, llvm::Function *func, - int rubyRegionId) { - auto debugFile = cs.debug->createFile(cs.compileUnit->getFilename(), cs.compileUnit->getDirectory()); - auto loc = cfg.symbol.data(cs)->loc(); - - auto owner = cfg.symbol.data(cs)->owner; - std::string diName(owner.data(cs)->name.shortName(cs)); - - if (owner.data(cs)->isSingletonClass(cs)) { - diName += "."; - } else { - diName += "#"; - } - - diName += cfg.symbol.data(cs)->name.shortName(cs); - - // Line number 0 indicates that the compiler knows the entity came from this - // particular file, but doesn't have precise location tracking for it. This - // can happen with e.g. synthesized packages. - auto lineNo = loc.exists() ? loc.position(cs).first.line : 0; - - return cs.debug->createFunction(parent, diName, func->getName(), debugFile, lineNo, getDebugFunctionType(cs, func), - lineNo, llvm::DINode::FlagPrototyped, llvm::DISubprogram::SPFlagDefinition); -} - -void getRubyBlocks2FunctionsMapping(CompilerState &cs, cfg::CFG &cfg, llvm::Function *func, - const vector &blockTypes, vector &funcs, - vector &scopes) { - auto *bt = cs.getRubyBlockFFIType(); - auto *et = cs.getRubyExceptionFFIType(); - for (int i = 0; i <= cfg.maxRubyRegionId; i++) { - switch (blockTypes[i]) { - case FunctionType::Method: - case FunctionType::StaticInitFile: - case FunctionType::StaticInitModule: - funcs[i] = func; - break; - - case FunctionType::Block: { - auto *fp = - llvm::Function::Create(bt, llvm::Function::InternalLinkage, - llvm::Twine{func->getName()} + "$block_" + llvm::Twine(i), *cs.module); - // setup argument names - fp->arg_begin()->setName("firstYieldArgRaw"); - (fp->arg_begin() + 1)->setName("localsOffset"); - (fp->arg_begin() + 2)->setName("argc"); - (fp->arg_begin() + 3)->setName("argArray"); - (fp->arg_begin() + 4)->setName("blockArg"); - funcs[i] = fp; - break; - } - - // NOTE: explicitly treating Unused functions like Exception functions, as they'll be collected by llvm - // anyway. - case FunctionType::ExceptionBegin: - case FunctionType::Rescue: - case FunctionType::Ensure: - case FunctionType::Unused: { - auto *fp = - llvm::Function::Create(et, llvm::Function::InternalLinkage, - llvm::Twine{func->getName()} + "$block_" + llvm::Twine(i), *cs.module); - - // argument names - fp->arg_begin()->setName("pc"); - (fp->arg_begin() + 1)->setName("localsOffset"); - (fp->arg_begin() + 2)->setName("cfp"); - - funcs[i] = fp; - break; - } - } - - auto *parent = i == 0 ? static_cast(cs.compileUnit) : scopes[0]; - auto *scope = getDebugScope(cs, cfg, parent, funcs[i], i); - scopes[i] = scope; - funcs[i]->setSubprogram(scope); - - ENFORCE(scope->describes(funcs[i])); - } -}; - -// Returns the mapping of ruby block id to function type, as well as the mapping from basic block to exception handling -// body block id. -void determineBlockTypes(CompilerState &cs, cfg::CFG &cfg, vector &blockTypes, vector &blockParents, - vector &exceptionHandlingBlockHeaders, vector &basicBlockJumpOverrides) { - // ruby block 0 is always the top-level of the method being compiled - if (IREmitterHelpers::isClassStaticInit(cs, cfg.symbol)) { - // When ruby runs the `Init_` function to initialize the whole object for this function it pushes a c frame for - // that function on the ruby stack, and we update that frame with the iseq that we make for tracking line - // numbers. However when we run our static-init methods for classes and modules we call the c functions - // directly, and want to avoid mutating the ruby stack frame. Thus those functions get marked as StaticInit, - // while the file-level static-init gets marked as Method. - // - // https://github.com/ruby/ruby/blob/a9a48e6a741f048766a2a287592098c4f6c7b7c7/load.c#L1033-L1034 - blockTypes[0] = FunctionType::StaticInitModule; - } else if (IREmitterHelpers::isFileStaticInit(cs, cfg.symbol)) { - blockTypes[0] = FunctionType::StaticInitFile; - } else { - blockTypes[0] = FunctionType::Method; - } - - for (auto &b : cfg.basicBlocks) { - if (b->bexit.cond.variable == cfg::LocalRef::blockCall()) { - blockTypes[b->rubyRegionId] = FunctionType::Block; - - // the else branch always points back to the original owning rubyRegionId of the block call - blockParents[b->rubyRegionId] = b->bexit.elseb->rubyRegionId; - - } else if (b->bexit.cond.variable.data(cfg)._name == core::Names::exceptionValue()) { - auto *bodyBlock = b->bexit.elseb; - auto *handlersBlock = b->bexit.thenb; - - // the relative block ids of blocks that are involved in the translation of an exception handling block. - auto bodyBlockId = bodyBlock->rubyRegionId; - auto handlersBlockId = bodyBlockId + cfg::CFG::HANDLERS_REGION_OFFSET; - auto ensureBlockId = bodyBlockId + cfg::CFG::ENSURE_REGION_OFFSET; - auto elseBlockId = bodyBlockId + cfg::CFG::ELSE_REGION_OFFSET; - - // `b` is the exception handling header block if the two branches from it have the sequential ids we would - // expect for the handler and body blocks. The reason we bail out here if this isn't the case is because - // there are other blocks within the translation that will also jump based on the value of the same - // exception value variable. - if (handlersBlock->rubyRegionId != handlersBlockId) { - continue; - } - - auto *elseBlock = CFGHelpers::findRegionEntry(cfg, elseBlockId); - auto *ensureBlock = CFGHelpers::findRegionEntry(cfg, ensureBlockId); - - // The way the CFG is constructed ensures that there will always be an else block, an ensure block, or both - // present. - ENFORCE(elseBlock || ensureBlock); - - { - // Find the exit block for exception handling so that we can redirect the header to it. - auto *exit = ensureBlock == nullptr ? elseBlock : ensureBlock; - auto exits = CFGHelpers::findRegionExits(cfg, b->rubyRegionId, exit->rubyRegionId); - - // The ensure block should only ever jump to the code that follows the begin/end block. - ENFORCE(exits.size() <= 1); - - if (exits.empty()) { - // When control flow terminates in the block that ends exception handling, else or ensure, that - // block will transition to the dead block. As `findRegionExits` will ignore the dead block to - // simplify the common case of looking for reachable exits, the exits vector being empty indicates - // that a return is present in the exception handling exit, and that the transition will never - // happen. In this case we can explicitly jump from the exception handling entry block directly to - // the dead block. - basicBlockJumpOverrides[handlersBlock->id] = cfg.deadBlock()->id; - basicBlockJumpOverrides[bodyBlock->id] = cfg.deadBlock()->id; - } else { - // Have the entry block jump over all of the exception handling machinery. - basicBlockJumpOverrides[handlersBlock->id] = exits.front()->id; - basicBlockJumpOverrides[bodyBlock->id] = exits.front()->id; - } - } - - exceptionHandlingBlockHeaders[b->id] = bodyBlockId; - - blockTypes[bodyBlockId] = FunctionType::ExceptionBegin; - blockTypes[handlersBlockId] = FunctionType::Rescue; - - if (elseBlock) { - blockTypes[elseBlockId] = FunctionType::ExceptionBegin; - } - - if (ensureBlock) { - blockTypes[ensureBlockId] = FunctionType::Ensure; - } - - // All exception handling blocks are children of `b`, as far as ruby iseq allocation is concerned. - blockParents[bodyBlockId] = b->rubyRegionId; - blockParents[handlersBlockId] = b->rubyRegionId; - blockParents[elseBlockId] = b->rubyRegionId; - blockParents[ensureBlockId] = b->rubyRegionId; - } - } - - return; -} - -bool returnAcrossBlockIsPresent(CompilerState &cs, cfg::CFG &cfg, const vector &blockNestingLevels) { - for (auto &bb : cfg.basicBlocks) { - for (auto &bind : bb->exprs) { - if (cfg::isa_instruction(bind.value)) { - // This will be non-zero if there was a block in any of our parent blocks. - if (blockNestingLevels[bb->rubyRegionId] != 0) { - return true; - } - } - } - } - return false; -} - -// Returns the number of scopes that must be traversed to get back out out to the -// top-level method frame and the number of blocks that must be traversed to get -// back out to the top-level method frame. The latter is important for providing -// accurate location information for block iseqs. -tuple getBlockNesting(vector &blockParents, vector &blockTypes, int rubyRegionId) { - auto level = 0; - auto blockLevel = 0; - - while (true) { - switch (blockTypes[rubyRegionId]) { - case FunctionType::Method: - case FunctionType::StaticInitFile: - case FunctionType::StaticInitModule: - return {level, blockLevel}; - - case FunctionType::Block: - ++blockLevel; - [[fallthrough]]; - case FunctionType::Rescue: - case FunctionType::Ensure: - // Increment the level, as we're crossing through a non-method stack frame to get back to our parent. - ++level; - rubyRegionId = blockParents[rubyRegionId]; - break; - - case FunctionType::ExceptionBegin: - case FunctionType::Unused: - // ExceptionBegin is considered to be part of the containing frame, so there's no block present here, - // and unused functions will never be called, so it's fine for them to have garbage values here. - rubyRegionId = blockParents[rubyRegionId]; - break; - } - } -} - -tuple, vector> getBlockLevels(vector &blockParents, vector &blockTypes) { - vector levels(blockTypes.size(), 0); - vector blockNesting(blockTypes.size(), 0); - - for (auto i = 0; i < blockTypes.size(); ++i) { - auto [level, blockLevel] = getBlockNesting(blockParents, blockTypes, i); - levels[i] = level; - blockNesting[i] = blockLevel; - } - - return {move(levels), move(blockNesting)}; -} - -string locationNameFor(CompilerState &cs, core::MethodRef symbol) { - if (IREmitterHelpers::isClassStaticInit(cs, symbol)) { - auto enclosingClassRef = symbol.enclosingClass(cs); - ENFORCE(enclosingClassRef.exists()); - enclosingClassRef = enclosingClassRef.data(cs)->attachedClass(cs); - ENFORCE(enclosingClassRef.exists()); - const auto &enclosingClass = enclosingClassRef.data(cs); - return fmt::format("<{}:{}>", enclosingClass->isClass() ? "class"sv : "module"sv, enclosingClassRef.show(cs)); - } else if (IREmitterHelpers::isFileStaticInit(cs, symbol)) { - return string(""sv); - } else { - return string(symbol.data(cs)->name.shortName(cs)); - } -} - -// Given a Ruby block, finds the block id of the nearest _proper_ ancestor of that block that allocates an iseq. -int getNearestIseqAllocatorBlock(const vector &blockParents, const vector &blockTypes, - int rubyRegionId) { - do { - rubyRegionId = blockParents[rubyRegionId]; - } while (rubyRegionId > 0 && blockTypes[rubyRegionId] == FunctionType::ExceptionBegin); - - return rubyRegionId; -} - -// Block names in Ruby are built recursively as the file is parsed. We aren't guaranteed -// to process blocks in depth-first order and we don't want to constantly recompute -// parent names. So we build the names for everything up front. -vector> getBlockLocationNames(CompilerState &cs, cfg::CFG &cfg, const vector &blockLevels, - const vector &blockNestingLevels, const vector &blockParents, - const vector &blockTypes) { - struct BlockInfo { - int rubyRegionId; - // blockLevels[this->rubyRegionId]; - int level; - }; - - ENFORCE(blockLevels.size() == blockParents.size()); - ENFORCE(blockLevels.size() == blockTypes.size()); - - vector> blockLocationNames(blockLevels.size()); - // Sort blocks by their depth so that we can process things in a breadth-first order. - vector blocksByDepth; - blocksByDepth.reserve(blockLevels.size()); - for (int i = 0; i <= cfg.maxRubyRegionId; ++i) { - blocksByDepth.emplace_back(BlockInfo{i, blockLevels[i]}); - } - - fast_sort(blocksByDepth, [](const auto &left, const auto &right) -> bool { return left.level < right.level; }); - - const string topLevelLocation = locationNameFor(cs, cfg.symbol); - - for (const auto &info : blocksByDepth) { - optional &iseqName = blockLocationNames[info.rubyRegionId]; - const auto blockType = blockTypes[info.rubyRegionId]; - switch (blockType) { - case FunctionType::Method: - case FunctionType::StaticInitFile: - case FunctionType::StaticInitModule: - iseqName.emplace(topLevelLocation); - break; - - case FunctionType::Block: { - int blockLevel = blockNestingLevels[info.rubyRegionId]; - if (blockLevel == 1) { - iseqName.emplace(fmt::format("block in {}", topLevelLocation)); - } else { - iseqName.emplace(fmt::format("block ({} levels) in {}", blockLevel, topLevelLocation)); - } - break; - } - - case FunctionType::Rescue: - case FunctionType::Ensure: { - int parent = getNearestIseqAllocatorBlock(blockParents, blockTypes, info.rubyRegionId); - const string *parentLocation; - if (parent == 0) { - parentLocation = &topLevelLocation; - } else { - const auto &parentName = blockLocationNames[parent]; - ENFORCE(blockLevels[parent] < blockLevels[info.rubyRegionId]); - ENFORCE(parentName.has_value()); - parentLocation = &*parentName; - } - iseqName.emplace( - fmt::format("{} in {}", blockType == FunctionType::Rescue ? "rescue" : "ensure", *parentLocation)); - break; - } - - case FunctionType::ExceptionBegin: - case FunctionType::Unused: - // These types do not have iseqs allocated for them and therefore have no name. - break; - } - } - - return blockLocationNames; -} - -void collectRubyBlockArgs(const cfg::CFG &cfg, const cfg::BasicBlock *b, vector &blockArgs) { - bool insideThenBlock = false; - - while (true) { - for (auto &expr : b->exprs) { - auto loadArg = cfg::cast_instruction(expr.value); - if (loadArg == nullptr) { - continue; - } - blockArgs[loadArg->argId] = expr.bind.variable; - } - - // When the exit condition for this block is constructed through `argPresent`, continue down the `then` branch - // to collect more block arg definitions. - if (b->bexit.cond.variable.data(cfg)._name == core::Names::argPresent()) { - insideThenBlock = true; - b = b->bexit.thenb; - continue; - } - - // The blocks emitted when optional arguments are present end in an unconditional jump to join with the path - // that would populate the arg with the default value. - if (insideThenBlock) { - insideThenBlock = false; - b = b->bexit.thenb; - continue; - } - - // We've reaced the end of argument handling entry blocks in the ruby block CFG. - return; - } -} - -// Given a block's argument list, compute the minimum and maximum arguments that will be present at runtime. -BlockArity computeBlockArity(const vector &argFlags) { - BlockArity blockArity; - - bool seenKwarg = false; - bool seenKwopt = false; - bool seenSplat = false; - for (auto &arg : argFlags) { - if (arg.isBlock || arg.isShadow) { - break; - } else if (arg.isKeyword) { - // Defaulted and repeated kwargs will both cause the max arg count to go to -1 when no required - // kwargs are present. - if (arg.isDefault || arg.isRepeated) { - seenKwopt = true; - } else { - seenKwarg = true; - } - } else { - if (!arg.isDefault && !arg.isRepeated) { - blockArity.min += 1; - } - - blockArity.max += 1; - - seenSplat = seenSplat || arg.isRepeated; - } - } - - if (seenKwarg) { - blockArity.min += 1; - blockArity.max += 1; - } else if (seenKwopt) { - blockArity.max = -1; - } - - if (seenSplat) { - blockArity.max = -1; - } - - return blockArity; -} - -} // namespace - -IREmitterContext IREmitterContext::getSorbetBlocks2LLVMBlockMapping(CompilerState &cs, cfg::CFG &cfg, - const ast::MethodDef &md, - llvm::Function *mainFunc) { - vector basicBlockJumpOverrides(cfg.maxBasicBlockId); - vector basicBlockRubyBlockId(cfg.maxBasicBlockId, 0); - llvm::IRBuilder<> builder(cs); - { - for (int i = 0; i < cfg.maxBasicBlockId; i++) { - basicBlockJumpOverrides[i] = i; - } - - for (auto &bb : cfg.basicBlocks) { - basicBlockRubyBlockId[bb->id] = bb->rubyRegionId; - } - } - - vector blockTypes(cfg.maxRubyRegionId + 1, FunctionType::Unused); - vector blockParents(cfg.maxRubyRegionId + 1, 0); - vector exceptionHandlingBlockHeaders(cfg.maxBasicBlockId + 1, 0); - determineBlockTypes(cs, cfg, blockTypes, blockParents, exceptionHandlingBlockHeaders, basicBlockJumpOverrides); - - vector rubyBlock2Function(cfg.maxRubyRegionId + 1, nullptr); - vector blockScopes(cfg.maxRubyRegionId + 1, nullptr); - getRubyBlocks2FunctionsMapping(cs, cfg, mainFunc, blockTypes, rubyBlock2Function, blockScopes); - - auto [blockLevels, blockNestingLevels] = getBlockLevels(blockParents, blockTypes); - auto blockLocationNames = getBlockLocationNames(cs, cfg, blockLevels, blockNestingLevels, blockParents, blockTypes); - - auto [aliases, symbols] = setupAliasesAndKeywords(cs, cfg); - const int maxSendArgCount = getMaxSendArgCount(cfg); - auto [variablesPrivateToBlocks, escapedVariableIndices, blockArgUsage] = - findCaptures(cs, md, cfg, aliases, exceptionHandlingBlockHeaders, blockLevels); - vector functionInitializersByFunction; - vector argumentSetupBlocksByFunction; - vector userEntryBlockByFunction(rubyBlock2Function.size()); - vector sendArgArrayByBlock; - vector lineNumberPtrsByFunction; - bool hasReturnAcrossBlock = false; - UnorderedMap blockControlFramePtrs; - - int i = 0; - auto lineNumberPtrType = llvm::PointerType::getUnqual(llvm::Type::getInt64PtrTy(cs)); - auto *controlFrameStructType = llvm::StructType::getTypeByName(cs, "struct.rb_control_frame_struct"); - ENFORCE(controlFrameStructType != nullptr); - auto *controlFramePtrType = controlFrameStructType->getPointerTo(); - for (auto &fun : rubyBlock2Function) { - auto inits = functionInitializersByFunction.emplace_back(llvm::BasicBlock::Create( - cs, "functionEntryInitializers", - fun)); // we will build a link for this block later, after we finish building expressions into it - builder.SetInsertPoint(inits); - auto sendArgArray = builder.CreateAlloca(llvm::ArrayType::get(llvm::Type::getInt64Ty(cs), maxSendArgCount), - nullptr, "callArgs"); - sendArgArrayByBlock.emplace_back(sendArgArray); - auto lineNumberPtr = builder.CreateAlloca(lineNumberPtrType, nullptr, "lineCountStore"); - lineNumberPtrsByFunction.emplace_back(lineNumberPtr); - // We cache cfp for ordinary ruby blocks; methods and exception begin/else - // blocks receive cfp as an argument. We currently consider caching not - // worth it for ensure/rescue blocks. They are, after all, exceptional, - // and probably don't contain enough code to make it worth caching the - // CFP. - if (blockTypes[i] == FunctionType::Block) { - auto *controlFramePtr = builder.CreateAlloca(controlFramePtrType, nullptr, "controlFrameStore"); - blockControlFramePtrs[i] = controlFramePtr; - } - argumentSetupBlocksByFunction.emplace_back(llvm::BasicBlock::Create(cs, "argumentSetup", fun)); - if (i == 0) { - // If return statements are present inside blocks, we need to codegen - // differently when finalizing the function. - // - // TODO(aprocter): I think this is a little bit more conservative than it needs to be, because it will push - // a tag even if the a return-from-block comes from a lambda, which is not actually necessary. - if (returnAcrossBlockIsPresent(cs, cfg, blockNestingLevels)) { - hasReturnAcrossBlock = true; - } - } - i++; - } - - vector blockExits(cfg.maxRubyRegionId + 1); - for (auto rubyRegionId = 0; rubyRegionId <= cfg.maxRubyRegionId; ++rubyRegionId) { - blockExits[rubyRegionId] = - llvm::BasicBlock::Create(cs, llvm::Twine("blockExit"), rubyBlock2Function[rubyRegionId]); - } - - vector deadBlocks(cfg.maxRubyRegionId + 1); - vector llvmBlocks(cfg.maxBasicBlockId + 1); - for (auto &b : cfg.basicBlocks) { - if (b.get() == cfg.entry()) { - llvmBlocks[b->id] = userEntryBlockByFunction[0] = - llvm::BasicBlock::Create(cs, "userEntry", rubyBlock2Function[0]); - } else if (b.get() == cfg.deadBlock()) { - for (auto rubyRegionId = 0; rubyRegionId <= cfg.maxRubyRegionId; ++rubyRegionId) { - deadBlocks[rubyRegionId] = - llvm::BasicBlock::Create(cs, llvm::Twine("dead"), rubyBlock2Function[rubyRegionId]); - } - - llvmBlocks[b->id] = deadBlocks[0]; - } else { - llvmBlocks[b->id] = llvm::BasicBlock::Create(cs, llvm::Twine("BB") + llvm::Twine(b->id), - rubyBlock2Function[b->rubyRegionId]); - } - } - vector> blockLinks(rubyBlock2Function.size()); - vector> rubyBlockArgs(rubyBlock2Function.size()); - - { - // fill in data about args for main function - for (auto &treeArg : md.args) { - auto *a = ast::MK::arg2Local(treeArg); - rubyBlockArgs[0].emplace_back(cfg.enterLocal(a->localVariable)); - } - } - - vector> argPresentVariables(cfg.maxRubyRegionId + 1); - vector rubyBlockArity(cfg.maxRubyRegionId + 1); - - // The method arguments are initialized here, while the block arguments are initialized when the blockCall header is - // encountered in the loop below. - int numArgs = md.symbol.data(cs)->arguments.size(); - argPresentVariables[0].resize(numArgs, cfg::LocalRef::noVariable()); - - for (auto &b : cfg.basicBlocks) { - if (b->bexit.cond.variable == cfg::LocalRef::blockCall()) { - userEntryBlockByFunction[b->rubyRegionId] = llvmBlocks[b->bexit.thenb->id]; - basicBlockJumpOverrides[b->id] = b->bexit.elseb->id; - auto backId = -1; - for (auto bid = 0; bid < b->backEdges.size(); bid++) { - if (b->backEdges[bid]->rubyRegionId < b->rubyRegionId) { - backId = bid; - break; - }; - } - ENFORCE(backId >= 0); - - cfg::InstructionPtr *expected = nullptr; - for (auto i = b->backEdges[backId]->exprs.rbegin(); i != b->backEdges[backId]->exprs.rend(); ++i) { - if (i->bind.variable.data(cfg)._name == core::Names::blockPreCallTemp()) { - expected = &i->value; - break; - } - } - ENFORCE(expected); - - auto expectedSend = cfg::cast_instruction(*expected); - ENFORCE(expectedSend); - ENFORCE(expectedSend->link); - blockLinks[b->rubyRegionId] = expectedSend->link; - - auto &argFlags = expectedSend->link->argFlags; - auto numBlockArgs = argFlags.size(); - auto &blockArgs = rubyBlockArgs[b->rubyRegionId]; - blockArgs.resize(numBlockArgs, cfg::LocalRef::noVariable()); - collectRubyBlockArgs(cfg, b->bexit.thenb, blockArgs); - - auto &argsPresent = argPresentVariables[b->bexit.thenb->rubyRegionId]; - argsPresent.resize(numBlockArgs, cfg::LocalRef::noVariable()); - - rubyBlockArity[b->rubyRegionId] = computeBlockArity(argFlags); - - } else if (b->bexit.cond.variable.data(cfg)._name == core::Names::exceptionValue()) { - if (exceptionHandlingBlockHeaders[b->id] == 0) { - continue; - } - - auto *bodyBlock = b->bexit.elseb; - auto *handlersBlock = b->bexit.thenb; - - // the relative block ids of blocks that are involved in the translation of an exception handling block. - auto bodyBlockId = bodyBlock->rubyRegionId; - auto handlersBlockId = bodyBlockId + cfg::CFG::HANDLERS_REGION_OFFSET; - auto ensureBlockId = bodyBlockId + cfg::CFG::ENSURE_REGION_OFFSET; - auto elseBlockId = bodyBlockId + cfg::CFG::ELSE_REGION_OFFSET; - - userEntryBlockByFunction[bodyBlockId] = llvmBlocks[bodyBlock->id]; - userEntryBlockByFunction[handlersBlockId] = llvmBlocks[handlersBlock->id]; - - if (auto *elseBlock = CFGHelpers::findRegionEntry(cfg, elseBlockId)) { - userEntryBlockByFunction[elseBlockId] = llvmBlocks[elseBlock->id]; - } - - if (auto *ensureBlock = CFGHelpers::findRegionEntry(cfg, ensureBlockId)) { - userEntryBlockByFunction[ensureBlockId] = llvmBlocks[ensureBlock->id]; - } - } else if (b->bexit.cond.variable.data(cfg)._name == core::Names::argPresent()) { - // the ArgPresent instruction is always the last one generated in the block - int argId = -1; - auto &bind = b->exprs.back(); - typecase( - bind.value, - [&](cfg::ArgPresent &i) { - ENFORCE(bind.bind.variable == b->bexit.cond.variable); - argId = i.argId; - }, - [&](cfg::YieldParamPresent &i) { - ENFORCE(bind.bind.variable == b->bexit.cond.variable); - argId = i.argId; - }); - ENFORCE(argId >= 0, "Missing an index for argPresent condition variable"); - - argPresentVariables[b->rubyRegionId][argId] = b->bexit.cond.variable; - } - } - - auto blockUsesBreak = blocksThatUseBreak(cs, cfg); - llvm::BasicBlock *postProcessBlock = llvm::BasicBlock::Create(cs, "postProcess", mainFunc); - - IREmitterContext approximation{ - cfg, - aliases, - symbols, - functionInitializersByFunction, - argumentSetupBlocksByFunction, - userEntryBlockByFunction, - llvmBlocks, - move(basicBlockJumpOverrides), - move(basicBlockRubyBlockId), - maxSendArgCount, - move(sendArgArrayByBlock), - std::move(escapedVariableIndices), - std::move(argPresentVariables), - {}, - {}, - postProcessBlock, - move(blockLinks), - move(rubyBlockArgs), - move(rubyBlock2Function), - move(blockTypes), - move(blockParents), - move(blockLevels), - move(lineNumberPtrsByFunction), - std::move(blockControlFramePtrs), - blockArgUsage, - move(exceptionHandlingBlockHeaders), - move(deadBlocks), - move(blockExits), - move(blockScopes), - move(blockUsesBreak), - hasReturnAcrossBlock, - move(blockLocationNames), - move(rubyBlockArity), - }; - - auto [llvmVariables, selfVariables] = setupLocalVariables(cs, cfg, variablesPrivateToBlocks, approximation); - approximation.llvmVariables = std::move(llvmVariables); - approximation.selfVariables = std::move(selfVariables); - - return approximation; -} - -} // namespace sorbet::compiler diff --git a/compiler/IREmitter/IREmitterContext.h b/compiler/IREmitter/IREmitterContext.h deleted file mode 100644 index 81fb9b5e5c..0000000000 --- a/compiler/IREmitter/IREmitterContext.h +++ /dev/null @@ -1,319 +0,0 @@ -#ifndef SORBET_COMPILER_IREMITTER_IREMITTERCONTEXT_H -#define SORBET_COMPILER_IREMITTER_IREMITTERCONTEXT_H - -#include "compiler/Core/ForwardDeclarations.h" -#include "core/core.h" - -namespace llvm { -class IRBuilderBase; -} // namespace llvm - -namespace sorbet::compiler { - -class CompilerState; - -enum class FunctionType { - // A normal method - Method, - - // A file-scope static-init method - StaticInitFile, - - // A module/class-scope static-init method - StaticInitModule, - - // Blocks used within a method - Block, - - // Functions extracted for rescue handlers - Rescue, - - // Functions extracted for ensure handlers - Ensure, - - // Functions extracted for the body of an exception - ExceptionBegin, - - // The type of blocks that aren't reference, and should be skipped during code generation - Unused, -}; - -enum class BlockArgUsage { - // This function does not have a block arg. - None, - - // The block argument to the function is only used directly within the - // top-level ruby region (rubyRegionId == 0) or in ruby regions that have the - // same frame as the top-level ruby region (e.g. ExceptionBegin regions). - // - // "used" here means that either it is only ever the receiver of .call - // (i.e. it is yield'd to) or it is passed along as a block argument itself. - SameFrameAsTopLevel, - - // The block argument is captured in some way (e.g. being called by a - // `rescue` block or it is passed as a non-block argument to some method). - Captured, -}; - -// We only track if a local variable is ever written to. A more sophisticated -// analysis might take into account where it is written to: writes only happen -// after all escaped reads, all writes happen before any escaped reads, etc. -enum class LocalUsedHow { - ReadOnly, - WrittenTo, -}; - -constexpr bool functionTypePushesFrame(FunctionType ty) { - switch (ty) { - case FunctionType::Method: - return false; - case FunctionType::StaticInitModule: - return false; - case FunctionType::StaticInitFile: - return false; - case FunctionType::Block: - return false; - case FunctionType::Rescue: - return true; - case FunctionType::Ensure: - return true; - case FunctionType::ExceptionBegin: - return false; - case FunctionType::Unused: - return false; - } -} - -constexpr bool functionTypeNeedsPostprocessing(FunctionType ty) { - switch (ty) { - case FunctionType::Method: - return true; - case FunctionType::StaticInitModule: - return true; - case FunctionType::StaticInitFile: - return true; - case FunctionType::Block: - return false; - case FunctionType::Rescue: - return false; - case FunctionType::Ensure: - return false; - case FunctionType::ExceptionBegin: - return false; - case FunctionType::Unused: - return false; - } -} - -struct Alias; - -struct EscapedUse { - // Index into the local variables array from the environment. - int localIndex; - LocalUsedHow used; - // If this escaped variable comes from a method argument, this is the declared - // type of that argument. Otherwise, this is nullptr. - core::TypePtr type; -}; - -struct BlockArity { - int min = 0; - int max = 0; -}; - -// Contains a bunch of state that gets populated and accessed while emitting IR for a single Ruby method. -// -// Nearly every vector here behaves as a lookup map keyed on cfg::BasicBlock::rubyRegionId (i.e., an ID -// for each Ruby block like `do ... end` inside a Ruby method, with 0 meaning "outside a Ruby block"). -// It might make sense to newtype this someday. -struct IREmitterContext { - cfg::CFG &cfg; - - // Sorbet uses cfg::Alias to link a local variable to a global construct, like an instance variable or a constant. - // - // This mapping is essentially, "if you were about to access a local variable corresponding to an alias, - // this is the thing you should access instead" - UnorderedMap aliases; - - // This is a mapping from local ref to the string that represents its symbol value. The mapping is used when - // building up the static data necessary for keyword arg sends that use the ruby value stack. - // - // key: local ref for the symbol keyword arg - // value: string_view of that keyword arg's contents - UnorderedMap symbols; - - // Contains llvm::BasicBlocks (insertion points) to hold code for a Ruby method or block that runs first, - // before starting to execute the user-written body. - // - // idx: cfg::BasicBlock::rubyRegionId - std::vector functionInitializersByFunction; - - // The insertion points for code that loads and validates arguments, one for each Ruby method or Ruby block. - // - // These insertions points hold that converts from arguments given to us via Ruby's C extension API interface - // into the LLVM IR that lets our compiled code interact with them. - // - // For simplicitly, all our compiled functions advertise themselves as variadic Ruby methods even when - // the original Ruby method had a fixed arity, so there ends up being a fair deal of argument checking logic. - // In particular, there are many llvm::BasicBlocks involved in arguments--this map holds just the starting point. - // - // idx: cfg::BasicBlock::rubyRegionId - std::vector argumentSetupBlocksByFunction; - - // The insertion points for the body code a user wrote inside a Ruby method or Ruby block. - // - // idx: cfg::BasicBlock::rubyRegionId - std::vector userEntryBlockByFunction; - - // A mapping from cfg::BasicBlock -> llvm::BasicBlock - // - // idx: cfg::BasicBlock::id - std::vector llvmBlocksBySorbetBlocks; - - // Sorbet sometimes generates extra cfg::BasicBlocks that make typechecking easier (for example, to model - // the fact that a block might never run, or might run once, or might run many times). Those made up blocks - // should never be branched to so SorbetLLVM needs to maintain a mapping which blocks to essentially skip over. - // - // idx: cfg::BasicBlock::id - // val: cfg::BasicBlock::id - std::vector basicBlockJumpOverrides; - - // Sorbet will simplify and compact the basicBlocks vector inside the CFG. As a result, the mapping from basic block - // id back to basic block is broken. Currently the only thing we need this mapping for is to identify the ruby block - // that a jump override corresponds to, so this data can just exist along-side the jump overrides. - // - // idx: cfg::BasicBlock::id - // val: cfg::BasicBlock::rubyRegionId - std::vector basicBlockRubyBlockId; - - // This is the maximum number of argument seen in a given ruby method. - // TODO(trevor) this could be a vector, allowing for more specific information to be present when checking for stack - // overflow. - int maxSendArgCount; - - // For simplicity, all our compiled functions advertise themselves as variadic Ruby methods. - // One reason why this is simpler is because we can pre-allocate a stack array big enough to - // hold the argv of the Ruby method call taking the most arguments within this Ruby method or - // Ruby block, and reuse that stack space when making each Ruby method call. - // - // This is a mapping from each Ruby method or region to that AllocaInst. - // - // idx: cfg::BasicBlock::rubyRegionId; - std::vector sendArgArrayByBlock; - - // Local variables that escape, i.e. are referenced from multiple Ruby blocks, are - // stored in the Ruby frame's environment. That storage may live on the Ruby stack - // or in heap-allocated memory. Either way, this maps from escaped local variables - // to details on their use. - UnorderedMap escapedVariableIndices; - - // When arguments have defaults, the use of the default is guarded by a call to ArgPresent. The ArgPresent variables - // are initialized during setupArguments, but need to be available by argument index. - // - // outer idx: the ruby region id that defines these arguments - // idx: Argument index into the method arguments - // val: The local ref that holds the result of the ArgPresent instruction, or cfg::LocalRef::noVariable if the - // argument does not have a default value. - std::vector> argPresentVariables; - - // Every local variable (including arguments) shows up as either an index into the closure (escapedVariableIndices) - // or something that was explicitly stack allocated. - // - // This mapping holds the latter: locals that don't come from the closure. - UnorderedMap llvmVariables; - - // `self` is retrieved at the entry point of each Ruby block and stashed here. - // - // idx: cfg::BasicBlock::rubyRegionId - // val: alloca for `self` - UnorderedMap selfVariables; - - // Insertion point for code that runs at the end of a Ruby method (i.e., where returns go) - // This handles return type checking, among other things. - llvm::BasicBlock *postProcessBlock; - - // idx: cfg::BasicBlock::rubyRegionId - // val: The SendAndBlockLink for that region (each Ruby block correspondes to one cfg::Send) - std::vector> blockLinks; - - // idx: cfg::BasicBlock::rubyRegionId - // val: The arguments for that Ruby method or region. - std::vector> rubyBlockArgs; - - // Each Ruby method and Ruby region gets compiled to an llvm::Function so that it can be called - // directly like any other C function. - // - // idx: cfg::BasicBlock::rubyRegionId - std::vector rubyBlocks2Functions; - - // The type of each function that we're going to generate. - // - // idx: cfg::BasicBlock::rubyRegionId - std::vector rubyBlockType; - - // The parent region id of each function being generated. Used for constructing the parent relationship when - // allocating iseq values for block/exception functions. - // - // idx: cfg::BasicBlock::rubyRegionId - // val: cfg::BasicBlock::rubyRegionId - std::vector rubyBlockParent; - - // The level of a ruby region corresponds to the nesting depth of non-method stack frames when it's called. So for a - // region at the top-level of the method, the value would be 1. - // - // idx: cfg::BasicBlock::rubyRegionId - // val: the number of scopes traversed to get back to the top-level method frame at runtime - std::vector rubyBlockLevel; - - // TODO(jez) document lineNumberPtrsByFunction - std::vector lineNumberPtrsByFunction; - - // Stores the control frame (GET_EC()->cfp) for ordinary ruby blocks. - // - // idx: cfg::BasicBlock::rubyRegionId - UnorderedMap blockControlFramePtrs; - - // How this function uses its block arg, if any. - BlockArgUsage blockArgUsage; - - // Non-zero for regions that originally jumped to exception-handling code. - // idx: block id - // val: the rubyRegionId for the body of the exception block - std::vector exceptionBlockHeader; - - // Mapping from ruby region id to llvm dead block. - std::vector deadBlockMapping; - - // Mapping from ruby region id to llvm block exit blocks. These blocks are used for the case where a transition - // between a basic block in a ruby region exists, and transitions to a node in a different ruby region (that isn't - // the dead block). - std::vector blockExitMapping; - - // Mappinf from ruby region id to debug info scope. - std::vector blockScopes; - - // Records which ruby regions use `break`, as that will forbid us from emitting type assertions on intrinsics that - // they are used with. - // - // idx: cfg::BasicBlock::rubyRegionId - // val: true when the region uses `break` - std::vector blockUsesBreak; - - // Whether an explicit return statement ever appears in a context that has a - // Ruby block as an ancestor. - bool hasReturnAcrossBlock; - - // Mapping from ruby region id to the name for the block's iseq. - std::vector> rubyBlockLocationNames; - - // Mapping from ruby region id to min/max arguments that region takes. As this is only used for block allocations, - // it's {0, 0} for every ruby region id that is not a FunctionType::Block. - std::vector rubyBlockArity; - - static IREmitterContext getSorbetBlocks2LLVMBlockMapping(CompilerState &cs, cfg::CFG &cfg, const ast::MethodDef &md, - llvm::Function *mainFunc); -}; - -} // namespace sorbet::compiler - -#endif diff --git a/compiler/IREmitter/IREmitterHelpers.cc b/compiler/IREmitter/IREmitterHelpers.cc deleted file mode 100644 index ecbfe69cfa..0000000000 --- a/compiler/IREmitter/IREmitterHelpers.cc +++ /dev/null @@ -1,555 +0,0 @@ -// These violate our poisons so have to happen first -#include "llvm/IR/DIBuilder.h" -#include "llvm/IR/DerivedTypes.h" // FunctionType -#include "llvm/IR/IRBuilder.h" - -#include "Payload.h" -#include "absl/base/casts.h" -#include "absl/strings/match.h" -#include "absl/strings/str_replace.h" -#include "absl/strings/str_split.h" -#include "ast/Helpers.h" -#include "ast/ast.h" -#include "cfg/CFG.h" -#include "common/sort/sort.h" -#include "compiler/Core/CompilerState.h" -#include "compiler/IREmitter/IREmitterContext.h" -#include "compiler/IREmitter/IREmitterHelpers.h" -#include "compiler/IREmitter/MethodCallContext.h" - -using namespace std; -namespace sorbet::compiler { - -namespace { -string getFunctionNamePrefix(CompilerState &cs, core::ClassOrModuleRef sym) { - auto maybeAttached = sym.data(cs)->attachedClass(cs); - if (maybeAttached.exists()) { - return getFunctionNamePrefix(cs, maybeAttached) + ".singleton_class"; - } - string suffix; - auto name = sym.data(cs)->name; - if (name.kind() == core::NameKind::CONSTANT && name.dataCnst(cs)->original.kind() == core::NameKind::UTF8) { - suffix = string(name.shortName(cs)); - } else { - suffix = name.toString(cs); - } - string prefix = IREmitterHelpers::isRootishSymbol(cs, sym.data(cs)->owner) - ? "" - : getFunctionNamePrefix(cs, sym.data(cs)->owner) + "::"; - - return prefix + suffix; -} -} // namespace - -string IREmitterHelpers::getFunctionName(CompilerState &cs, core::MethodRef sym) { - auto owner = sym.data(cs)->owner; - auto maybeAttachedOwner = owner.data(cs)->attachedClass(cs); - string prefix = "func_"; - if (maybeAttachedOwner.exists()) { - prefix = prefix + getFunctionNamePrefix(cs, maybeAttachedOwner) + "."; - } else { - prefix = prefix + getFunctionNamePrefix(cs, owner) + "#"; - } - - auto name = sym.data(cs)->name; - string suffix; - if (name.kind() == core::NameKind::UTF8) { - suffix = string(name.shortName(cs)); - } else if (name.kind() == core::NameKind::UNIQUE && name.dataUnique(cs)->original == core::Names::staticInit()) { - // For file-level static init, the unique name generated depends on the - // number of files Sorbet is processing (especially, for tests, the number - // of files in the payload), which makes the output of just name.toString(cs) - // less deterministic than we would like. For class-level static-init, - // the function name will be prefixed with the class name anyway, so we - // don't need the unique integer appended to it. - suffix = name.dataUnique(cs)->original.toString(cs); - } else { - suffix = name.toString(cs); - } - - // '@' in symbol names means special things to the dynamic linker with regards - // to symbol versioning. Since we're not using symbol versioning, we need to - // mangle function names to avoid '@'. - string mangled; - bool isFirst = true; - for (auto part : absl::StrSplit(suffix, '@')) { - if (!isFirst) { - mangled += "at"; - } else { - isFirst = false; - } - mangled += to_string(part.size()); - mangled += part; - } - - return prefix + mangled; -} - -bool IREmitterHelpers::isFileStaticInit(const core::GlobalState &gs, core::MethodRef sym) { - auto name = sym.data(gs)->name; - if (name.kind() != core::NameKind::UNIQUE) { - return false; - } - return name.dataUnique(gs)->original == core::Names::staticInit(); -} - -bool IREmitterHelpers::isClassStaticInit(const core::GlobalState &gs, core::MethodRef sym) { - return sym.data(gs)->name == core::Names::staticInit(); -} - -bool IREmitterHelpers::isFileOrClassStaticInit(const core::GlobalState &gs, core::MethodRef sym) { - return isFileStaticInit(gs, sym) || isClassStaticInit(gs, sym); -} - -namespace { - -llvm::Function * -getOrCreateFunctionWithName(CompilerState &cs, std::string name, llvm::FunctionType *ft, - llvm::GlobalValue::LinkageTypes linkageType = llvm::Function::InternalLinkage, - bool overrideLinkage = false) { - auto func = cs.module->getFunction(name); - if (func) { - if (overrideLinkage) { - func->setLinkage(linkageType); - } - return func; - } - return llvm::Function::Create(ft, linkageType, name, *cs.module); -} - -}; // namespace - -llvm::Function *IREmitterHelpers::lookupFunction(CompilerState &cs, core::MethodRef sym) { - ENFORCE(!isClassStaticInit(cs, sym), "use special helper instead"); - auto *func = cs.module->getFunction(IREmitterHelpers::getFunctionName(cs, sym)); - return func; -} - -llvm::Function *IREmitterHelpers::getOrCreateDirectWrapper(CompilerState &cs, core::MethodRef sym) { - auto name = "direct_" + IREmitterHelpers::getFunctionName(cs, sym); - return getOrCreateFunctionWithName(cs, name, cs.getDirectWrapperFunctionType(), llvm::Function::ExternalLinkage, - true); -} - -llvm::Function *IREmitterHelpers::getOrCreateFunction(CompilerState &cs, core::MethodRef sym) { - ENFORCE(!isClassStaticInit(cs, sym), "use special helper instead"); - return getOrCreateFunctionWithName(cs, IREmitterHelpers::getFunctionName(cs, sym), cs.getRubyFFIType(), - llvm::Function::InternalLinkage, true); -} - -llvm::Function *IREmitterHelpers::getOrCreateStaticInit(CompilerState &cs, core::MethodRef sym, core::LocOffsets loc) { - ENFORCE(isClassStaticInit(cs, sym), "use general helper instead"); - auto name = IREmitterHelpers::getFunctionName(cs, sym) + "L" + to_string(loc.beginPos()); - return getOrCreateFunctionWithName(cs, name, cs.getRubyFFIType(), llvm::Function::InternalLinkage, true); -} -llvm::Function *IREmitterHelpers::getInitFunction(CompilerState &cs, core::MethodRef sym) { - std::vector NoArgs(0, llvm::Type::getVoidTy(cs)); - auto linkageType = llvm::Function::InternalLinkage; - auto baseName = IREmitterHelpers::getFunctionName(cs, sym); - - auto ft = llvm::FunctionType::get(llvm::Type::getVoidTy(cs), NoArgs, false); - return getOrCreateFunctionWithName(cs, "Init_" + baseName, ft, linkageType); -} - -// TODO(froydnj): LLVM datatypes don't really have the concept of signedness, only -// LLVM operations. Does that mean we should just be using IRBuilder::getInt32 etc.? -llvm::Value *IREmitterHelpers::buildU4(CompilerState &cs, uint32_t i) { - return llvm::ConstantInt::get(cs, llvm::APInt(32, i)); -} - -llvm::Value *IREmitterHelpers::buildS4(CompilerState &cs, int i) { - return llvm::ConstantInt::get(cs, llvm::APInt(32, i, /*signed=*/true)); -} - -llvm::Function *IREmitterHelpers::cleanFunctionBody(CompilerState &cs, llvm::Function *func) { - for (auto &bb : *func) { - bb.dropAllReferences(); - } - - // Delete all basic blocks. They are now unused, except possibly by - // blockaddresses, but BasicBlock's destructor takes care of those. - while (!func->empty()) { - func->begin()->eraseFromParent(); - } - return func; -} - -void IREmitterHelpers::emitDebugLoc(CompilerState &cs, llvm::IRBuilderBase &builder, const IREmitterContext &irctx, - int rubyRegionId, core::Loc loc) { - auto *scope = irctx.blockScopes[rubyRegionId]; - unsigned line, column; - - if (!loc.exists()) { - // This location seems less useful than no debug location, but it ensures - // that we have a "real" debug location to set for LLVM. LLVM verification - // will assert if we have calls in debug-info-laden functions that can be - // inlined and are not tagged with proper debug information. - line = 0; - column = 0; - } else { - auto start = loc.position(cs).first; - line = start.line; - column = start.column; - } - - builder.SetCurrentDebugLocation(llvm::DILocation::get(cs, line, column, scope)); -} - -void IREmitterHelpers::emitUncheckedReturn(CompilerState &cs, llvm::IRBuilderBase &builder, - const IREmitterContext &irctx, int rubyRegionId, llvm::Value *retVal) { - if (functionTypePushesFrame(irctx.rubyBlockType[rubyRegionId])) { - builder.CreateCall(cs.getFunction("sorbet_popFrame"), {}); - } - builder.CreateRet(retVal); -} - -void IREmitterHelpers::emitReturnAcrossBlock(CompilerState &cs, cfg::CFG &cfg, llvm::IRBuilderBase &builder, - const IREmitterContext &irctx, int rubyRegionId, llvm::Value *retVal) { - auto *ec = builder.CreateCall(cs.getFunction("sorbet_getEC"), {}, "ec"); - builder.CreateCall(cs.getFunction("sorbet_throwReturn"), {ec, retVal}); - builder.CreateUnreachable(); -} - -void IREmitterHelpers::emitReturn(CompilerState &cs, llvm::IRBuilderBase &builder, const IREmitterContext &irctx, - int rubyRegionId, llvm::Value *retVal) { - if (functionTypeNeedsPostprocessing(irctx.rubyBlockType[rubyRegionId])) { - auto returnValue = irctx.cfg.enterLocal({core::Names::returnValue(), 1}); - Payload::varSet(cs, returnValue, retVal, builder, irctx, rubyRegionId); - builder.CreateBr(irctx.postProcessBlock); - } else { - emitUncheckedReturn(cs, builder, irctx, rubyRegionId, retVal); - } -} - -llvm::Value *IREmitterHelpers::maybeCheckReturnValue(CompilerState &cs, cfg::CFG &cfg, llvm::IRBuilderBase &builder, - const IREmitterContext &irctx, llvm::Value *returnValue) { - auto expectedType = cfg.symbol.data(cs)->resultType; - if (expectedType == nullptr) { - return returnValue; - } - - if (core::isa_type(expectedType) && - core::cast_type_nonnull(expectedType).symbol == core::Symbols::void_()) { - return Payload::voidSingleton(cs, builder, irctx); - } - - // sorbet-runtime doesn't check this type for abstract methods, so we won't either. - // TODO(froydnj): we should check this type. - if (!cfg.symbol.data(cs)->flags.isAbstract) { - IREmitterHelpers::emitTypeTest(cs, builder, returnValue, expectedType, "Return value"); - } - - return returnValue; -} - -namespace { -void buildTypeTestPassFailBlocks(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *value, - llvm::Value *testResult, const core::TypePtr &expectedType, - std::string_view description) { - auto successBlock = llvm::BasicBlock::Create(cs, "typeTestSuccess", builder.GetInsertBlock()->getParent()); - - auto failBlock = llvm::BasicBlock::Create(cs, "typeTestFail", builder.GetInsertBlock()->getParent()); - - auto expected = Payload::setExpectedBool(cs, builder, testResult, true); - builder.CreateCondBr(expected, successBlock, failBlock); - builder.SetInsertPoint(failBlock); - // this will throw exception - builder.CreateCall(cs.getFunction("sorbet_cast_failure"), {value, Payload::toCString(cs, description, builder), - Payload::toCString(cs, expectedType.show(cs), builder)}); - builder.CreateUnreachable(); - builder.SetInsertPoint(successBlock); -} -} // namespace - -void IREmitterHelpers::emitTypeTest(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *value, - const core::TypePtr &expectedType, std::string_view description) { - auto *typeTest = Payload::typeTest(cs, builder, value, expectedType); - buildTypeTestPassFailBlocks(cs, builder, value, typeTest, expectedType, description); -} - -void IREmitterHelpers::emitTypeTestForRestArg(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *value, - const core::TypePtr &expectedType, std::string_view description) { - auto *fun = builder.GetInsertBlock()->getParent(); - auto *initBlock = llvm::BasicBlock::Create(cs, "restTypeTestInit", fun); - auto *headerBlock = llvm::BasicBlock::Create(cs, "restTypeTestHeader", fun); - auto *bodyBlock = llvm::BasicBlock::Create(cs, "restTypeTestBody", fun); - auto *continuationBlock = llvm::BasicBlock::Create(cs, "restTypeTestFinished", fun); - - builder.CreateBr(initBlock); - builder.SetInsertPoint(initBlock); - auto *loopIndex = buildS4(cs, 0); - auto *loopEnd = builder.CreateCall(cs.getFunction("sorbet_rubyArrayLen"), {value}, "restArgLength"); - builder.CreateBr(headerBlock); - - builder.SetInsertPoint(headerBlock); - auto *indexPhi = builder.CreatePHI(builder.getInt32Ty(), 2, "loopIndexPhi"); - auto *moreToGo = builder.CreateICmpSLT(indexPhi, loopEnd, "moreToGo"); - builder.CreateCondBr(moreToGo, bodyBlock, continuationBlock); - - builder.SetInsertPoint(bodyBlock); - auto *element = builder.CreateCall(cs.getFunction("sorbet_rubyArrayArefUnchecked"), {value, indexPhi}, "element"); - auto *typeTest = Payload::typeTest(cs, builder, element, expectedType); - buildTypeTestPassFailBlocks(cs, builder, element, typeTest, expectedType, description); - auto *incrementedIndex = builder.CreateAdd(indexPhi, buildS4(cs, 1)); - indexPhi->addIncoming(loopIndex, initBlock); - indexPhi->addIncoming(incrementedIndex, builder.GetInsertBlock()); - builder.CreateBr(headerBlock); - - builder.SetInsertPoint(continuationBlock); -} - -bool IREmitterHelpers::isAliasToSingleton(const core::GlobalState &gs, const IREmitterContext &irctx, cfg::LocalRef var, - core::ClassOrModuleRef klass) { - auto aliasit = irctx.aliases.find(var); - if (aliasit == irctx.aliases.end()) { - return false; - } - - if (aliasit->second.kind != Alias::AliasKind::Constant) { - return false; - } - - ENFORCE(klass.data(gs)->isSingletonClass(gs)); - auto attachedClass = klass.data(gs)->attachedClass(gs); - return aliasit->second.constantSym == attachedClass; -} - -llvm::Value *IREmitterHelpers::emitLiteralish(CompilerState &cs, llvm::IRBuilderBase &builder, - const core::TypePtr &lit) { - if (lit.derivesFrom(cs, core::Symbols::FalseClass())) { - return Payload::rubyFalse(cs, builder); - } - if (lit.derivesFrom(cs, core::Symbols::TrueClass())) { - return Payload::rubyTrue(cs, builder); - } - if (lit.derivesFrom(cs, core::Symbols::NilClass())) { - return Payload::rubyNil(cs, builder); - } - if (core::isa_type(lit)) { - const auto &litInt = core::cast_type_nonnull(lit); - auto *value = Payload::longToRubyValue(cs, builder, litInt.value); - Payload::assumeType(cs, builder, value, core::Symbols::Integer()); - return value; - } - if (core::isa_type(lit)) { - const auto &litFloat = core::cast_type_nonnull(lit); - auto *value = Payload::doubleToRubyValue(cs, builder, litFloat.value); - Payload::assumeType(cs, builder, value, core::Symbols::Float()); - return value; - } - - auto litType = core::cast_type_nonnull(lit); - switch (litType.literalKind) { - case core::NamedLiteralType::LiteralTypeKind::Symbol: { - auto str = litType.asName().shortName(cs); - auto rawId = Payload::idIntern(cs, builder, str); - auto *value = builder.CreateCall(cs.getFunction("rb_id2sym"), {rawId}, "rawSym"); - Payload::assumeType(cs, builder, value, core::Symbols::Symbol()); - return value; - } - case core::NamedLiteralType::LiteralTypeKind::String: { - auto str = litType.asName().shortName(cs); - auto *value = Payload::cPtrToRubyString(cs, builder, str, true); - Payload::assumeType(cs, builder, value, core::Symbols::String()); - return value; - } - } -} - -bool IREmitterHelpers::hasBlockArgument(CompilerState &cs, int blockId, core::MethodRef method, - const IREmitterContext &irctx) { - auto ty = irctx.rubyBlockType[blockId]; - if (!(ty == FunctionType::Block || ty == FunctionType::Method || ty == FunctionType::StaticInitFile || - ty == FunctionType::StaticInitModule)) { - return false; - } - - if (ty == FunctionType::Block) { - auto blockLink = irctx.blockLinks[blockId]; - if (blockLink->argFlags.empty()) { - return false; - } - - return blockLink->argFlags.back().isBlock; - } - - auto &args = method.data(cs)->arguments; - if (args.empty()) { - return false; - } - - return args.back().flags.isBlock; -} - -core::SymbolRef IREmitterHelpers::fixupOwningSymbol(const core::GlobalState &gs, core::SymbolRef sym) { - if (isRootishSymbol(gs, sym)) { - // Root methods end up going on Object. - return core::Symbols::Object(); - } - - return sym; -} - -std::string IREmitterHelpers::showClassNameWithoutOwner(const core::GlobalState &gs, core::SymbolRef sym) { - std::string withoutOwnerStr; - - auto name = sym.name(gs); - if (name.kind() == core::NameKind::UNIQUE) { - withoutOwnerStr = name.dataUnique(gs)->original.show(gs); - } else { - withoutOwnerStr = name.show(gs); - }; - - return withoutOwnerStr; -} - -bool IREmitterHelpers::isRootishSymbol(const core::GlobalState &gs, core::SymbolRef sym) { - if (!sym.exists()) { - return false; - } - - // These are the obvious cases. - if (sym == core::Symbols::root() || sym == core::Symbols::rootSingleton()) { - return true; - } - - return false; -} - -std::optional -IREmitterHelpers::isFinalMethod(const core::GlobalState &gs, core::TypePtr recvType, core::NameRef fun) { - core::ClassOrModuleRef recvSym; - if (core::isa_type(recvType)) { - recvSym = core::cast_type_nonnull(recvType).symbol; - } else if (auto *app = core::cast_type(recvType)) { - recvSym = app->klass; - } - - if (!recvSym.exists()) { - return std::nullopt; - } - - auto funSym = recvSym.data(gs)->findMethod(gs, fun); - if (!funSym.exists()) { - return std::nullopt; - } - - if (!funSym.data(gs)->flags.isFinal) { - return std::nullopt; - } - - auto file = funSym.data(gs)->loc().file(); - if (file.data(gs).compiledLevel != core::CompiledLevel::True) { - return std::nullopt; - } - - return IREmitterHelpers::FinalMethodInfo{recvSym, funSym, file}; -} - -llvm::Value *KnownFunction::getFunction(CompilerState &cs, llvm::IRBuilderBase &builder) const { - auto *fun = cs.getFunction(this->name); - auto *type = cs.getAnyRubyCApiFunctionType()->getPointerTo(); - - switch (this->type) { - case KnownFunction::Type::Symbol: - return builder.CreatePointerCast(fun, type, "fnPtrCast"); - - case KnownFunction::Type::CachedSymbol: { - // allocate a global to store the return value of the forwarding function - string cacheName = "symbolCache_" + this->name; - - auto *cache = static_cast( - cs.module->getOrInsertGlobal(cacheName, type, [fun, type, &cs, &cacheName] { - auto *null = llvm::ConstantPointerNull::get(type); - auto *ret = new llvm::GlobalVariable(*cs.module, type, false, llvm::GlobalVariable::InternalLinkage, - null, cacheName); - - ret->setAlignment(llvm::MaybeAlign(8)); - - auto globalInitBuilder = llvm::IRBuilder<>(cs); - globalInitBuilder.SetInsertPoint(cs.globalConstructorsEntry); - - auto *res = globalInitBuilder.CreateCall(fun, {}, "init_" + cacheName); - globalInitBuilder.CreateStore(globalInitBuilder.CreatePointerCast(res, type, "fnPtrCast"), ret); - - return ret; - })); - - return builder.CreateLoad(cache); - } - } -} - -llvm::Value *IREmitterHelpers::receiverFastPathTestWithCache(MethodCallContext &mcctx, - const vector &expectedRubyCFuncs, - const string &methodNameForDebug) { - auto &cs = mcctx.cs; - auto &builder = mcctx.builder; - auto *cache = mcctx.getInlineCache(); - auto *recv = mcctx.varGetRecv(); - mcctx.emitMethodSearch(); - - // We could initialize result with the first result (because expectedRubyCFunc is - // non-empty), but this makes the code slightly cleaner, and LLVM will optimize. - llvm::Value *result = builder.getInt1(false); - for (const auto &knownFunc : expectedRubyCFuncs) { - auto *fnPtrAsAnyFn = knownFunc.getFunction(cs, builder); - auto current = - builder.CreateCall(cs.getFunction("sorbet_isCachedMethod"), {cache, fnPtrAsAnyFn, recv}, "isCached"); - result = builder.CreateOr(result, current); - } - - return result; -} - -llvm::Value *CallCacheFlags::build(CompilerState &cs, llvm::IRBuilderBase &builder) { - static struct { - bool CallCacheFlags::*field; - string_view variableName; - llvm::StringRef flagName; - } flags[] = { - {&CallCacheFlags::args_simple, "sorbet_vmCallArgsSimple", "VM_CALL_ARGS_SIMPLE"}, - {&CallCacheFlags::args_splat, "sorbet_vmCallArgsSplat", "VM_CALL_ARGS_SPLAT"}, - {&CallCacheFlags::kwarg, "sorbet_vmCallKwarg", "VM_CALL_KWARG"}, - {&CallCacheFlags::kw_splat, "sorbet_vmCallKwSplat", "VM_CALL_KW_SPLAT"}, - {&CallCacheFlags::fcall, "sorbet_vmCallFCall", "VM_CALL_FCALL"}, - {&CallCacheFlags::blockarg, "sorbet_vmCallArgsBlockarg", "VM_CALL_ARGS_BLOCKARG"}, - }; - - llvm::Value *acc = llvm::ConstantInt::get(cs, llvm::APInt(32, 0, false)); - for (auto &flag : flags) { - if (this->*flag.field) { - const bool allowInternal = true; - auto *global = cs.module->getGlobalVariable(flag.variableName, allowInternal); - ENFORCE(global != nullptr); - ENFORCE(global->hasInitializer()); - auto *flagVal = global->getInitializer(); - acc = builder.CreateBinOp(llvm::Instruction::Or, acc, flagVal); - } - } - - return acc; -} - -bool IREmitterHelpers::canPassThroughBlockViaRubyVM(MethodCallContext &mcctx, cfg::LocalRef blkVar) { - auto &irctx = mcctx.irctx; - auto rubyRegionId = mcctx.rubyRegionId; - - // TODO: all of this logic needs to be modified for blocks that take blocks. - if (irctx.blockArgUsage != BlockArgUsage::SameFrameAsTopLevel) { - return false; - } - - ENFORCE(IREmitterHelpers::hasBlockArgument(mcctx.cs, 0, irctx.cfg.symbol, irctx)); - - // The block for the send is not at the same level as the toplevel. - if (irctx.rubyBlockLevel[rubyRegionId] != 0) { - return false; - } - - ENFORCE(!irctx.rubyBlockArgs.empty()); - - return blkVar == irctx.rubyBlockArgs[0].back(); -} - -} // namespace sorbet::compiler diff --git a/compiler/IREmitter/IREmitterHelpers.h b/compiler/IREmitter/IREmitterHelpers.h deleted file mode 100644 index 20738c7d82..0000000000 --- a/compiler/IREmitter/IREmitterHelpers.h +++ /dev/null @@ -1,193 +0,0 @@ -#ifndef SORBET_COMPILER_IREMITTER_IREMITTERHELPERS_H -#define SORBET_COMPILER_IREMITTER_IREMITTERHELPERS_H -#include "IREmitter.h" -#include "cfg/CFG.h" -#include "compiler/Core/ForwardDeclarations.h" -#include "compiler/IREmitter/CallCacheFlags.h" -#include "compiler/IREmitter/RubyStackArgs.h" -#include "core/core.h" -#include -#include - -namespace sorbet::compiler { - -struct IREmitterContext; -class MethodCallContext; -struct VMFlag; - -// TODO(jez) This shouldn't be at the top-level (sorbet::compiler). It should probably be nested in something. -// (Confusing to see bare `Alias` when there is also `cfg::Alias`) -struct Alias { - enum class AliasKind { Constant, InstanceField, ClassField, GlobalField }; - AliasKind kind; - core::SymbolRef constantSym; - core::NameRef instanceField; - core::NameRef classField; - core::NameRef globalField; - static Alias forConstant(core::SymbolRef sym) { - Alias ret; - ret.kind = AliasKind::Constant; - ret.constantSym = sym; - return ret; - } - static Alias forClassField(core::NameRef name) { - Alias ret; - ret.kind = AliasKind::ClassField; - ret.classField = name; - return ret; - } - static Alias forInstanceField(core::NameRef name) { - Alias ret; - ret.kind = AliasKind::InstanceField; - ret.instanceField = name; - return ret; - } - static Alias forGlobalField(core::NameRef name) { - Alias ret; - ret.kind = AliasKind::GlobalField; - ret.globalField = name; - return ret; - } -}; - -class Intrinsics { -public: - enum class HandleBlock : uint8_t { - Handled = 1, - Unhandled = 2, - }; -}; - -struct KnownFunction { - enum class Type { - // Symbols available for direct comparison - Symbol, - - // Symbols that are computed by calling another function, and then cached - CachedSymbol, - }; - - Type type; - std::string name; - - // Implicit constructor from a string for backwards compability - KnownFunction(std::string name) : KnownFunction(Type::Symbol, std::move(name)) {} - - static KnownFunction cached(std::string name) { - return KnownFunction{Type::CachedSymbol, std::move(name)}; - } - - // Fetch the function referenced from the payload. - llvm::Value *getFunction(CompilerState &cs, llvm::IRBuilderBase &builder) const; - -private: - KnownFunction(Type type, std::string name) : type{type}, name{std::move(name)} {} -}; - -class IREmitterHelpers { -public: - static bool isClassStaticInit(const core::GlobalState &gs, core::MethodRef sym); - static bool isFileStaticInit(const core::GlobalState &gs, core::MethodRef sym); - static bool isFileOrClassStaticInit(const core::GlobalState &gs, core::MethodRef sym); - - static std::string getFunctionName(CompilerState &cs, core::MethodRef sym); - static llvm::Function *lookupFunction(CompilerState &cs, core::MethodRef sym); - static llvm::Function *cleanFunctionBody(CompilerState &cs, llvm::Function *func); - static llvm::Function *getOrCreateStaticInit(CompilerState &cs, core::MethodRef sym, core::LocOffsets loc); - static llvm::Function *getOrCreateFunction(CompilerState &cs, core::MethodRef sym); - static llvm::Function *getOrCreateDirectWrapper(CompilerState &cs, core::MethodRef sym); - - static llvm::Function *getInitFunction(CompilerState &cs, core::MethodRef sym); - - static std::size_t sendArgCount(cfg::Send *send); - - struct SendArgInfo { - SendArgInfo(llvm::Value *argc, llvm::Value *argv, llvm::Value *kw_splat, std::vector argValues); - - llvm::Value *argc; - llvm::Value *argv; - llvm::Value *kw_splat; - - std::vector argValues; - }; - - static SendArgInfo fillSendArgArray(MethodCallContext &mcctx, const std::size_t offset); - static SendArgInfo fillSendArgArray(MethodCallContext &mcctx); - - static llvm::Value *buildU4(CompilerState &cs, uint32_t i); - static llvm::Value *buildS4(CompilerState &cs, int i); - - static llvm::Value *emitMethodCall(MethodCallContext &mcctx); - - static RubyStackArgs buildSendArgs(MethodCallContext &mcctx, cfg::LocalRef recv, const std::size_t offset); - - static llvm::Value *makeInlineCache(CompilerState &cs, llvm::IRBuilderBase &build, std::string methodName, - CallCacheFlags flags, int argc, const std::vector &keywords); - - static llvm::Value *emitMethodCallViaRubyVM(MethodCallContext &mcctx); - - static void emitExceptionHandlers(CompilerState &gs, llvm::IRBuilderBase &builder, const IREmitterContext &irctx, - int rubyRegionId, int bodyRubyRegionId, cfg::LocalRef exceptionValue); - - static void emitDebugLoc(CompilerState &gs, llvm::IRBuilderBase &builder, const IREmitterContext &irctx, - int rubyRegionId, core::Loc loc); - - static void emitUncheckedReturn(CompilerState &gs, llvm::IRBuilderBase &builder, const IREmitterContext &irctx, - int rubyRegionId, llvm::Value *retVal); - static void emitReturn(CompilerState &gs, llvm::IRBuilderBase &builder, const IREmitterContext &irctx, - int rubyRegionId, llvm::Value *retVal); - static void emitReturnAcrossBlock(CompilerState &gs, cfg::CFG &cfg, llvm::IRBuilderBase &builder, - const IREmitterContext &irctx, int rubyRegionId, llvm::Value *retVal); - // Typecheck returnValue as the return value of cfg, if necessary. Returns the actual - // value to be returned, which may be different than returnValue e.g. in the case of a - // void-returning method. - static llvm::Value *maybeCheckReturnValue(CompilerState &cs, cfg::CFG &cfg, llvm::IRBuilderBase &build, - const IREmitterContext &irctx, llvm::Value *returnValue); - - // Emit a type test. The insertion point of the builder is set to the start of - // the block following a successful test. - static void emitTypeTest(CompilerState &gs, llvm::IRBuilderBase &builder, llvm::Value *value, - const core::TypePtr &expectedType, std::string_view description); - static void emitTypeTestForRestArg(CompilerState &gs, llvm::IRBuilderBase &builder, llvm::Value *value, - const core::TypePtr &expectedType, std::string_view description); - - // Return a value representing the literalish thing, which is either a NamedLiteralType - // or a type representing nil, false, or true. - static llvm::Value *emitLiteralish(CompilerState &gs, llvm::IRBuilderBase &builder, - const core::TypePtr &literalish); - - // Return true if the given blockId has a block argument. - static bool hasBlockArgument(CompilerState &gs, int blockId, core::MethodRef method, const IREmitterContext &irctx); - - // Given an owner as the Sorbet-visible symbol, return the parent symbol - // as seen by the Ruby VM. - static core::SymbolRef fixupOwningSymbol(const core::GlobalState &gs, core::SymbolRef sym); - - static std::string showClassNameWithoutOwner(const core::GlobalState &gs, core::SymbolRef sym); - - // Return true if the given symbol is a "root" symbol. - static bool isRootishSymbol(const core::GlobalState &gs, core::SymbolRef sym); - - struct FinalMethodInfo { - core::ClassOrModuleRef recv; - core::MethodRef method; - core::FileRef file; - }; - - // Return true when the symbol is a final method - static std::optional isFinalMethod(const core::GlobalState &gs, core::TypePtr recvType, - core::NameRef fun); - - static llvm::Value *receiverFastPathTestWithCache(MethodCallContext &mcctx, - const std::vector &expectedRubyCFuncs, - const std::string &methodNameForDebug); - - static bool isAliasToSingleton(const core::GlobalState &gs, const IREmitterContext &irctx, cfg::LocalRef var, - core::ClassOrModuleRef klass); - - // Given a method call context representing a send, determine whether a variable - // representing a block can be fetched out of the Ruby VM state. - static bool canPassThroughBlockViaRubyVM(MethodCallContext &mcctx, cfg::LocalRef var); -}; -} // namespace sorbet::compiler -#endif diff --git a/compiler/IREmitter/Intrinsics/Makefile b/compiler/IREmitter/Intrinsics/Makefile deleted file mode 100644 index fac842196c..0000000000 --- a/compiler/IREmitter/Intrinsics/Makefile +++ /dev/null @@ -1,27 +0,0 @@ -TOPDIR := ../../.. -BAZEL := $(TOPDIR)/bazel - -all: intrinsics-report.md ../WrappedIntrinsics.h ../Payload/PayloadIntrinsics.c - -../WrappedIntrinsics.h: WrappedIntrinsics.formatted.h - cp $< $@ - -../Payload/PayloadIntrinsics.c: PayloadIntrinsics.formatted.c - cp $< $@ - -%.formatted.c: %.c - $(BAZEL) run @com_stripe_ruby_typer//tools:clang-format "$(PWD)/$<" > "$(PWD)/$@" -%.formatted.h: %.h - $(BAZEL) run @com_stripe_ruby_typer//tools:clang-format "$(PWD)/$<" > "$(PWD)/$@" - -ensure-ruby: - $(BAZEL) build @sorbet_ruby_2_7_unpatched//:ruby - -write-files: wrap-intrinsics.rb ensure-ruby - $(TOPDIR)/bazel-bin/external/sorbet_ruby_2_7_unpatched/toolchain/bin/ruby wrap-intrinsics.rb - -intrinsics-report.md WrappedIntrinsics.h PayloadIntrinsics.c: write-files - -clean: - $(RM) WrappedIntrinsics.h WrappedIntrinsics.formatted.h - $(RM) PayloadIntrinsics.c PayloadIntrinsics.formatted.c diff --git a/compiler/IREmitter/Intrinsics/README.md b/compiler/IREmitter/Intrinsics/README.md deleted file mode 100644 index 97b3ae929f..0000000000 --- a/compiler/IREmitter/Intrinsics/README.md +++ /dev/null @@ -1,38 +0,0 @@ -# Ruby intrinsic analyzer - -## Adding more intrinsics - -To add more intrinsics, edit the whitelists at the top of wrap-intrinsics.rb. -You will need: - -- To know the name of the underlying Ruby class & method -- To know that Sorbet has a SymbolRef with the same name as the Ruby class name - -## Running - -To re-generate the whitelisted intrinsics: - -``` -(cd compiler/IREmitter/Intrinsics/ && make) -``` - -To regenate all intrinsics: - -``` -# Edit the method_whitelisted? method in wrap-intrinsics.rb to return `true` - -# then: -(cd compiler/IREmitter/Intrinsics/ && make) -``` - -## Debugging - -- Look at `intrinsics-report.md`, which serializes the entirety of the - information the script was working from. - -- Half of the script is powered by running `nm` on an object to list it's - symbols. Sometimes that output can have changed unexpectedly. - -- Sometimes the generated diffs can be hard to read. They're usually better when - passing the `--patience` flag. - diff --git a/compiler/IREmitter/Intrinsics/intrinsic-report.md b/compiler/IREmitter/Intrinsics/intrinsic-report.md deleted file mode 100644 index 6b123be984..0000000000 --- a/compiler/IREmitter/Intrinsics/intrinsic-report.md +++ /dev/null @@ -1,1567 +0,0 @@ -# Intrinsics by file - -## `array.c` -* [ ] `rb_ary_initialize` (`Array#initialize`) -* [✔ ] `rb_ary_replace` (`Array#initialize_copy`) -* [ ] `rb_ary_inspect` (`Array#inspect`) -* [ ] `rb_ary_to_a` (`Array#to_a`) -* [ ] `rb_ary_to_h` (`Array#to_h`) -* [ ] `rb_ary_to_ary_m` (`Array#to_ary`) -* [ ] `rb_ary_equal` (`Array#==`) -* [ ] `rb_ary_eql` (`Array#eql?`) -* [ ] `rb_ary_hash` (`Array#hash`) -* [✔ ] `rb_ary_aref` (`Array#[]`) -* [ ] `rb_ary_aset` (`Array#[]=`) -* [✔ ] `rb_ary_at` (`Array#at`) -* [ ] `rb_ary_fetch` (`Array#fetch`) -* [ ✔] `rb_ary_first` (`Array#first`) -* [✔ ] `rb_ary_last` (`Array#last`) -* [ ✔] `rb_ary_concat_multi` (`Array#concat`) -* [ ] `rb_ary_union_multi` (`Array#union`) -* [ ] `rb_ary_difference_multi` (`Array#difference`) -* [ ] `rb_ary_intersection_multi` (`Array#intersection`) -* [✔ ] `rb_ary_push` (`Array#<<`) -* [ ✔] `rb_ary_push_m` (`Array#push`) -* [ ] `rb_ary_pop_m` (`Array#pop`) -* [ ] `rb_ary_shift_m` (`Array#shift`) -* [ ] `rb_ary_unshift_m` (`Array#unshift`) -* [ ] `rb_ary_insert` (`Array#insert`) -* [✔ ] `rb_ary_each` (`Array#each`) -* [ ] `rb_ary_each_index` (`Array#each_index`) -* [ ] `rb_ary_reverse_each` (`Array#reverse_each`) -* [ ] `rb_ary_length` (`Array#length`) -* [ ] `rb_ary_empty_p` (`Array#empty?`) -* [ ] `rb_ary_index` (`Array#find_index`) -* [ ] `rb_ary_index` (`Array#index`) -* [ ] `rb_ary_rindex` (`Array#rindex`) -* [ ✔] `rb_ary_join_m` (`Array#join`) -* [ ] `rb_ary_reverse_m` (`Array#reverse`) -* [ ] `rb_ary_reverse_bang` (`Array#reverse!`) -* [ ] `rb_ary_rotate_m` (`Array#rotate`) -* [ ] `rb_ary_rotate_bang` (`Array#rotate!`) -* [✔ ] `rb_ary_sort` (`Array#sort`) -* [✔ ] `rb_ary_sort_bang` (`Array#sort!`) -* [ ] `rb_ary_sort_by_bang` (`Array#sort_by!`) -* [ ] `rb_ary_collect` (`Array#collect`) -* [ ] `rb_ary_collect_bang` (`Array#collect!`) -* [ ] `rb_ary_collect` (`Array#map`) -* [ ] `rb_ary_collect_bang` (`Array#map!`) -* [ ] `rb_ary_select` (`Array#select`) -* [ ] `rb_ary_select_bang` (`Array#select!`) -* [ ] `rb_ary_select` (`Array#filter`) -* [ ] `rb_ary_select_bang` (`Array#filter!`) -* [ ] `rb_ary_keep_if` (`Array#keep_if`) -* [ ] `rb_ary_values_at` (`Array#values_at`) -* [✔ ] `rb_ary_delete` (`Array#delete`) -* [ ] `rb_ary_delete_at_m` (`Array#delete_at`) -* [ ] `rb_ary_delete_if` (`Array#delete_if`) -* [ ] `rb_ary_reject` (`Array#reject`) -* [ ] `rb_ary_reject_bang` (`Array#reject!`) -* [ ] `rb_ary_zip` (`Array#zip`) -* [ ] `rb_ary_transpose` (`Array#transpose`) -* [✔ ] `rb_ary_replace` (`Array#replace`) -* [✔ ] `rb_ary_clear` (`Array#clear`) -* [ ] `rb_ary_fill` (`Array#fill`) -* [✔ ] `rb_ary_includes` (`Array#include?`) -* [✔ ] `rb_ary_cmp` (`Array#<=>`) -* [✔ ] `rb_ary_aref` (`Array#slice`) -* [ ] `rb_ary_slice_bang` (`Array#slice!`) -* [✔ ] `rb_ary_assoc` (`Array#assoc`) -* [✔ ] `rb_ary_rassoc` (`Array#rassoc`) -* [✔ ] `rb_ary_plus` (`Array#+`) -* [ ] `rb_ary_times` (`Array#*`) -* [ ✔] `rb_ary_diff` (`Array#-`) -* [ ] `rb_ary_and` (`Array#&`) -* [ ] `rb_ary_or` (`Array#|`) -* [ ] `rb_ary_max` (`Array#max`) -* [ ] `rb_ary_min` (`Array#min`) -* [ ] `rb_ary_minmax` (`Array#minmax`) -* [ ] `rb_ary_uniq` (`Array#uniq`) -* [ ] `rb_ary_uniq_bang` (`Array#uniq!`) -* [ ] `rb_ary_compact` (`Array#compact`) -* [ ] `rb_ary_compact_bang` (`Array#compact!`) -* [ ✔] `rb_ary_flatten` (`Array#flatten`) -* [ ] `rb_ary_flatten_bang` (`Array#flatten!`) -* [ ] `rb_ary_count` (`Array#count`) -* [ ] `rb_ary_shuffle_bang` (`Array#shuffle!`) -* [ ] `rb_ary_shuffle` (`Array#shuffle`) -* [ ] `rb_ary_sample` (`Array#sample`) -* [ ] `rb_ary_cycle` (`Array#cycle`) -* [ ] `rb_ary_permutation` (`Array#permutation`) -* [ ] `rb_ary_combination` (`Array#combination`) -* [ ] `rb_ary_repeated_permutation` (`Array#repeated_permutation`) -* [ ] `rb_ary_repeated_combination` (`Array#repeated_combination`) -* [ ] `rb_ary_product` (`Array#product`) -* [ ] `rb_ary_take` (`Array#take`) -* [ ] `rb_ary_take_while` (`Array#take_while`) -* [ ] `rb_ary_drop` (`Array#drop`) -* [ ] `rb_ary_drop_while` (`Array#drop_while`) -* [ ] `rb_ary_bsearch` (`Array#bsearch`) -* [ ] `rb_ary_bsearch_index` (`Array#bsearch_index`) -* [ ] `rb_ary_any_p` (`Array#any?`) -* [ ] `rb_ary_all_p` (`Array#all?`) -* [ ] `rb_ary_none_p` (`Array#none?`) -* [ ] `rb_ary_one_p` (`Array#one?`) -* [ ] `rb_ary_dig` (`Array#dig`) -* [ ] `rb_ary_sum` (`Array#sum`) -* [ ] `rb_ary_deconstruct` (`Array#deconstruct`) - -## `bignum.c` -* [ ] `rb_int_coerce` (`Integer#coerce`) - -## `compar.c` -* [ ] `cmp_equal` (`Comparable#==`) -* [ ] `cmp_gt` (`Comparable#>`) -* [ ] `cmp_ge` (`Comparable#>=`) -* [ ] `cmp_lt` (`Comparable#<`) -* [ ] `cmp_le` (`Comparable#<=`) -* [ ] `cmp_between` (`Comparable#between?`) -* [ ] `cmp_clamp` (`Comparable#clamp`) - -## `complex.c` -* [✔ ] `rb_complex_real` (`Complex#real`) -* [✔ ] `rb_complex_imag` (`Complex#imaginary`) -* [✔ ] `rb_complex_imag` (`Complex#imag`) -* [✔ ] `rb_complex_uminus` (`Complex#-@`) -* [✔ ] `rb_complex_plus` (`Complex#+`) -* [✔ ] `rb_complex_minus` (`Complex#-`) -* [✔ ] `rb_complex_mul` (`Complex#*`) -* [✔ ] `rb_complex_div` (`Complex#/`) -* [ ] `nucomp_quo` (`Complex#quo`) -* [ ] `nucomp_fdiv` (`Complex#fdiv`) -* [✔ ] `rb_complex_pow` (`Complex#**`) -* [ ] `nucomp_eqeq_p` (`Complex#==`) -* [ ] `nucomp_cmp` (`Complex#<=>`) -* [ ] `nucomp_coerce` (`Complex#coerce`) -* [✔ ] `rb_complex_abs` (`Complex#abs`) -* [✔ ] `rb_complex_abs` (`Complex#magnitude`) -* [ ] `nucomp_abs2` (`Complex#abs2`) -* [✔ ] `rb_complex_arg` (`Complex#arg`) -* [✔ ] `rb_complex_arg` (`Complex#angle`) -* [✔ ] `rb_complex_arg` (`Complex#phase`) -* [ ] `nucomp_rect` (`Complex#rectangular`) -* [ ] `nucomp_rect` (`Complex#rect`) -* [ ] `nucomp_polar` (`Complex#polar`) -* [✔ ] `rb_complex_conjugate` (`Complex#conjugate`) -* [✔ ] `rb_complex_conjugate` (`Complex#conj`) -* [ ] `nucomp_false` (`Complex#real?`) -* [ ] `nucomp_numerator` (`Complex#numerator`) -* [ ] `nucomp_denominator` (`Complex#denominator`) -* [ ] `nucomp_hash` (`Complex#hash`) -* [ ] `nucomp_eql_p` (`Complex#eql?`) -* [ ] `nucomp_to_s` (`Complex#to_s`) -* [ ] `nucomp_inspect` (`Complex#inspect`) -* [ ] `rb_complex_finite_p` (`Complex#finite?`) -* [ ] `rb_complex_infinite_p` (`Complex#infinite?`) -* [ ] `nucomp_to_i` (`Complex#to_i`) -* [ ] `nucomp_to_f` (`Complex#to_f`) -* [ ] `nucomp_to_r` (`Complex#to_r`) -* [ ] `nucomp_rationalize` (`Complex#rationalize`) -* [ ] `nucomp_to_c` (`Complex#to_c`) -* [ ] `nilclass_to_c` (`NilClass#to_c`) -* [ ] `numeric_to_c` (`Numeric#to_c`) -* [ ] `string_to_c` (`String#to_c`) -* [ ] `numeric_real` (`Numeric#real`) -* [ ] `numeric_imag` (`Numeric#imaginary`) -* [ ] `numeric_imag` (`Numeric#imag`) -* [ ] `numeric_abs2` (`Numeric#abs2`) -* [ ] `numeric_arg` (`Numeric#arg`) -* [ ] `numeric_arg` (`Numeric#angle`) -* [ ] `numeric_arg` (`Numeric#phase`) -* [ ] `numeric_rect` (`Numeric#rectangular`) -* [ ] `numeric_rect` (`Numeric#rect`) -* [ ] `numeric_polar` (`Numeric#polar`) -* [ ] `numeric_conj` (`Numeric#conjugate`) -* [ ] `numeric_conj` (`Numeric#conj`) -* [ ] `float_arg` (`Float#arg`) -* [ ] `float_arg` (`Float#angle`) -* [ ] `float_arg` (`Float#phase`) - -## `cont.c` -* [ ] `rb_fiber_initialize` (`Fiber#initialize`) -* [ ] `rb_fiber_m_resume` (`Fiber#resume`) -* [ ] `rb_fiber_raise` (`Fiber#raise`) -* [ ] `fiber_to_s` (`Fiber#to_s`) -* [ ] `rb_fiber_pool_initialize` (`Pool#initialize`) -* [ ] `rb_cont_call` (`Continuation#call`) -* [ ] `rb_cont_call` (`Continuation#[]`) -* [ ] `rb_fiber_m_transfer` (`Fiber#transfer`) -* [✔ ] `rb_fiber_alive_p` (`Fiber#alive?`) - -## `dir.c` -* [ ] `dir_initialize` (`Dir#initialize`) -* [ ] `dir_fileno` (`Dir#fileno`) -* [ ] `dir_path` (`Dir#path`) -* [ ] `dir_path` (`Dir#to_path`) -* [ ] `dir_inspect` (`Dir#inspect`) -* [ ] `dir_read` (`Dir#read`) -* [ ] `dir_each` (`Dir#each`) -* [ ] `dir_each_child_m` (`Dir#each_child`) -* [ ] `dir_collect_children` (`Dir#children`) -* [ ] `dir_rewind` (`Dir#rewind`) -* [ ] `dir_tell` (`Dir#tell`) -* [ ] `dir_seek` (`Dir#seek`) -* [ ] `dir_tell` (`Dir#pos`) -* [ ] `dir_set_pos` (`Dir#pos=`) -* [ ] `dir_close` (`Dir#close`) - -## `encoding.c` -* [ ] `enc_name` (`Encoding#to_s`) -* [ ] `enc_inspect` (`Encoding#inspect`) -* [ ] `enc_name` (`Encoding#name`) -* [ ] `enc_names` (`Encoding#names`) -* [ ] `enc_dummy_p` (`Encoding#dummy?`) -* [ ] `enc_ascii_compatible_p` (`Encoding#ascii_compatible?`) -* [ ] `enc_replicate` (`Encoding#replicate`) -* [ ] `enc_dump` (`Encoding#_dump`) - -## `enum.c` -* [ ] `enum_to_a` (`Enumerable#to_a`) -* [ ] `enum_to_a` (`Enumerable#entries`) -* [ ] `enum_to_h` (`Enumerable#to_h`) -* [ ] `enum_sort` (`Enumerable#sort`) -* [ ] `enum_sort_by` (`Enumerable#sort_by`) -* [ ] `enum_grep` (`Enumerable#grep`) -* [ ] `enum_grep_v` (`Enumerable#grep_v`) -* [ ] `enum_count` (`Enumerable#count`) -* [ ] `enum_find` (`Enumerable#find`) -* [ ] `enum_find` (`Enumerable#detect`) -* [ ] `enum_find_index` (`Enumerable#find_index`) -* [ ] `enum_find_all` (`Enumerable#find_all`) -* [ ] `enum_find_all` (`Enumerable#select`) -* [ ] `enum_find_all` (`Enumerable#filter`) -* [ ] `enum_filter_map` (`Enumerable#filter_map`) -* [ ] `enum_reject` (`Enumerable#reject`) -* [ ] `enum_collect` (`Enumerable#collect`) -* [ ] `enum_collect` (`Enumerable#map`) -* [ ] `enum_flat_map` (`Enumerable#flat_map`) -* [ ] `enum_flat_map` (`Enumerable#collect_concat`) -* [ ] `enum_inject` (`Enumerable#inject`) -* [ ] `enum_inject` (`Enumerable#reduce`) -* [ ] `enum_partition` (`Enumerable#partition`) -* [ ] `enum_group_by` (`Enumerable#group_by`) -* [ ] `enum_tally` (`Enumerable#tally`) -* [ ] `enum_first` (`Enumerable#first`) -* [ ] `enum_all` (`Enumerable#all?`) -* [ ] `enum_any` (`Enumerable#any?`) -* [ ] `enum_one` (`Enumerable#one?`) -* [ ] `enum_none` (`Enumerable#none?`) -* [ ] `enum_min` (`Enumerable#min`) -* [ ] `enum_max` (`Enumerable#max`) -* [ ] `enum_minmax` (`Enumerable#minmax`) -* [ ] `enum_min_by` (`Enumerable#min_by`) -* [ ] `enum_max_by` (`Enumerable#max_by`) -* [ ] `enum_minmax_by` (`Enumerable#minmax_by`) -* [ ] `enum_member` (`Enumerable#member?`) -* [ ] `enum_member` (`Enumerable#include?`) -* [ ] `enum_each_with_index` (`Enumerable#each_with_index`) -* [ ] `enum_reverse_each` (`Enumerable#reverse_each`) -* [ ] `enum_each_entry` (`Enumerable#each_entry`) -* [ ] `enum_each_slice` (`Enumerable#each_slice`) -* [ ] `enum_each_cons` (`Enumerable#each_cons`) -* [ ] `enum_each_with_object` (`Enumerable#each_with_object`) -* [ ] `enum_zip` (`Enumerable#zip`) -* [ ] `enum_take` (`Enumerable#take`) -* [ ] `enum_take_while` (`Enumerable#take_while`) -* [ ] `enum_drop` (`Enumerable#drop`) -* [ ] `enum_drop_while` (`Enumerable#drop_while`) -* [ ] `enum_cycle` (`Enumerable#cycle`) -* [ ] `enum_chunk` (`Enumerable#chunk`) -* [ ] `enum_slice_before` (`Enumerable#slice_before`) -* [ ] `enum_slice_after` (`Enumerable#slice_after`) -* [ ] `enum_slice_when` (`Enumerable#slice_when`) -* [ ] `enum_chunk_while` (`Enumerable#chunk_while`) -* [ ] `enum_sum` (`Enumerable#sum`) -* [ ] `enum_uniq` (`Enumerable#uniq`) - -## `enumerator.c` -* [ ] `obj_to_enum` (`Kernel#to_enum`) -* [ ] `obj_to_enum` (`Kernel#enum_for`) -* [ ] `enumerator_initialize` (`Enumerator#initialize`) -* [ ] `enumerator_init_copy` (`Enumerator#initialize_copy`) -* [ ] `enumerator_each` (`Enumerator#each`) -* [ ] `enumerator_each_with_index` (`Enumerator#each_with_index`) -* [ ] `enumerator_with_object` (`Enumerator#each_with_object`) -* [ ] `enumerator_with_index` (`Enumerator#with_index`) -* [ ] `enumerator_with_object` (`Enumerator#with_object`) -* [ ] `enumerator_next_values` (`Enumerator#next_values`) -* [ ] `enumerator_peek_values_m` (`Enumerator#peek_values`) -* [ ] `enumerator_next` (`Enumerator#next`) -* [ ] `enumerator_peek` (`Enumerator#peek`) -* [ ] `enumerator_feed` (`Enumerator#feed`) -* [ ] `enumerator_rewind` (`Enumerator#rewind`) -* [ ] `enumerator_inspect` (`Enumerator#inspect`) -* [ ] `enumerator_size` (`Enumerator#size`) -* [ ] `enumerator_plus` (`Enumerator#+`) -* [ ] `enum_chain` (`Enumerable#chain`) -* [ ] `enumerable_lazy` (`Enumerable#lazy`) -* [ ] `lazy_initialize` (`Lazy#initialize`) -* [ ] `lazy_to_enum` (`Lazy#to_enum`) -* [ ] `lazy_to_enum` (`Lazy#enum_for`) -* [ ] `lazy_eager` (`Lazy#eager`) -* [ ] `lazy_map` (`Lazy#map`) -* [ ] `lazy_map` (`Lazy#collect`) -* [ ] `lazy_flat_map` (`Lazy#flat_map`) -* [ ] `lazy_flat_map` (`Lazy#collect_concat`) -* [ ] `lazy_select` (`Lazy#select`) -* [ ] `lazy_select` (`Lazy#find_all`) -* [ ] `lazy_select` (`Lazy#filter`) -* [ ] `lazy_filter_map` (`Lazy#filter_map`) -* [ ] `lazy_reject` (`Lazy#reject`) -* [ ] `lazy_grep` (`Lazy#grep`) -* [ ] `lazy_grep_v` (`Lazy#grep_v`) -* [ ] `lazy_zip` (`Lazy#zip`) -* [ ] `lazy_take` (`Lazy#take`) -* [ ] `lazy_take_while` (`Lazy#take_while`) -* [ ] `lazy_drop` (`Lazy#drop`) -* [ ] `lazy_drop_while` (`Lazy#drop_while`) -* [ ] `lazy_lazy` (`Lazy#lazy`) -* [ ] `lazy_super` (`Lazy#chunk`) -* [ ] `lazy_super` (`Lazy#slice_before`) -* [ ] `lazy_super` (`Lazy#slice_after`) -* [ ] `lazy_super` (`Lazy#slice_when`) -* [ ] `lazy_super` (`Lazy#chunk_while`) -* [ ] `lazy_uniq` (`Lazy#uniq`) -* [ ] `lazy_with_index` (`Lazy#with_index`) -* [ ] `lazy_to_a` (`Lazy#to_a`) -* [ ] `lazy_chunk` (`Lazy#chunk`) -* [ ] `lazy_chunk_while` (`Lazy#chunk_while`) -* [ ] `lazy_slice_after` (`Lazy#slice_after`) -* [ ] `lazy_slice_before` (`Lazy#slice_before`) -* [ ] `lazy_slice_when` (`Lazy#slice_when`) -* [ ] `stop_result` (`StopIteration#result`) -* [ ] `generator_initialize` (`Generator#initialize`) -* [ ] `generator_init_copy` (`Generator#initialize_copy`) -* [ ] `generator_each` (`Generator#each`) -* [ ] `yielder_initialize` (`Yielder#initialize`) -* [ ] `yielder_yield` (`Yielder#yield`) -* [ ] `yielder_yield_push` (`Yielder#<<`) -* [ ] `yielder_to_proc` (`Yielder#to_proc`) -* [ ] `producer_each` (`Producer#each`) -* [ ] `enum_chain_initialize` (`Chain#initialize`) -* [ ] `enum_chain_init_copy` (`Chain#initialize_copy`) -* [ ] `enum_chain_each` (`Chain#each`) -* [ ] `enum_chain_size` (`Chain#size`) -* [ ] `enum_chain_rewind` (`Chain#rewind`) -* [ ] `enum_chain_inspect` (`Chain#inspect`) -* [ ] `arith_seq_begin` (`ArithmeticSequence#begin`) -* [ ] `arith_seq_end` (`ArithmeticSequence#end`) -* [ ] `arith_seq_exclude_end` (`ArithmeticSequence#exclude_end?`) -* [ ] `arith_seq_step` (`ArithmeticSequence#step`) -* [ ] `arith_seq_first` (`ArithmeticSequence#first`) -* [ ] `arith_seq_last` (`ArithmeticSequence#last`) -* [ ] `arith_seq_inspect` (`ArithmeticSequence#inspect`) -* [ ] `arith_seq_eq` (`ArithmeticSequence#==`) -* [ ] `arith_seq_eq` (`ArithmeticSequence#===`) -* [ ] `arith_seq_eq` (`ArithmeticSequence#eql?`) -* [ ] `arith_seq_hash` (`ArithmeticSequence#hash`) -* [ ] `arith_seq_each` (`ArithmeticSequence#each`) -* [ ] `arith_seq_size` (`ArithmeticSequence#size`) - -## `error.c` -* [ ] `exc_exception` (`Exception#exception`) -* [ ] `exc_initialize` (`Exception#initialize`) -* [ ] `exc_equal` (`Exception#==`) -* [ ] `exc_to_s` (`Exception#to_s`) -* [ ] `exc_message` (`Exception#message`) -* [ ] `exc_full_message` (`Exception#full_message`) -* [ ] `exc_inspect` (`Exception#inspect`) -* [ ] `exc_backtrace` (`Exception#backtrace`) -* [ ] `exc_backtrace_locations` (`Exception#backtrace_locations`) -* [ ] `exc_set_backtrace` (`Exception#set_backtrace`) -* [ ] `exc_cause` (`Exception#cause`) -* [ ] `exit_initialize` (`SystemExit#initialize`) -* [ ] `exit_status` (`SystemExit#status`) -* [ ] `exit_success_p` (`SystemExit#success?`) -* [ ] `key_err_initialize` (`KeyError#initialize`) -* [ ] `key_err_receiver` (`KeyError#receiver`) -* [ ] `key_err_key` (`KeyError#key`) -* [ ] `syntax_error_initialize` (`SyntaxError#initialize`) -* [ ] `name_err_initialize` (`NameError#initialize`) -* [ ] `name_err_name` (`NameError#name`) -* [ ] `name_err_receiver` (`NameError#receiver`) -* [ ] `name_err_local_variables` (`NameError#local_variables`) -* [ ] `name_err_mesg_equal` (`message#==`) -* [ ] `name_err_mesg_to_str` (`message#to_str`) -* [ ] `name_err_mesg_dump` (`message#_dump`) -* [ ] `nometh_err_initialize` (`NoMethodError#initialize`) -* [ ] `nometh_err_args` (`NoMethodError#args`) -* [ ] `nometh_err_private_call_p` (`NoMethodError#private_call?`) -* [ ] `frozen_err_initialize` (`FrozenError#initialize`) -* [ ] `frozen_err_receiver` (`FrozenError#receiver`) -* [ ] `syserr_initialize` (`SystemCallError#initialize`) -* [ ] `syserr_errno` (`SystemCallError#errno`) -* [ ] `rb_warning_s_warn` (`Warning#warn`) -* [ ] `warning_write` (`buffer#write`) - -## `eval.c` -* [ ] `rb_mod_include` (`Module#include`) -* [ ] `rb_mod_prepend` (`Module#prepend`) -* [ ] `rb_obj_extend` (`Kernel#extend`) - -## `file.c` -* [ ] `rb_io_stat` (`IO#stat`) -* [ ] `rb_file_lstat` (`File#lstat`) -* [ ] `rb_file_atime` (`File#atime`) -* [ ] `rb_file_mtime` (`File#mtime`) -* [ ] `rb_file_ctime` (`File#ctime`) -* [ ] `rb_file_birthtime` (`File#birthtime`) -* [ ] `rb_file_size` (`File#size`) -* [ ] `rb_file_chmod` (`File#chmod`) -* [ ] `rb_file_chown` (`File#chown`) -* [ ] `rb_file_truncate` (`File#truncate`) -* [ ] `rb_file_flock` (`File#flock`) -* [ ] `rb_file_path` (`File#path`) -* [ ] `rb_file_path` (`File#to_path`) -* [ ] `rb_stat_init` (`Stat#initialize`) -* [ ] `rb_stat_init_copy` (`Stat#initialize_copy`) -* [ ] `rb_stat_cmp` (`Stat#<=>`) -* [ ] `rb_stat_dev` (`Stat#dev`) -* [ ] `rb_stat_dev_major` (`Stat#dev_major`) -* [ ] `rb_stat_dev_minor` (`Stat#dev_minor`) -* [ ] `rb_stat_ino` (`Stat#ino`) -* [ ] `rb_stat_mode` (`Stat#mode`) -* [ ] `rb_stat_nlink` (`Stat#nlink`) -* [ ] `rb_stat_uid` (`Stat#uid`) -* [ ] `rb_stat_gid` (`Stat#gid`) -* [ ] `rb_stat_rdev` (`Stat#rdev`) -* [ ] `rb_stat_rdev_major` (`Stat#rdev_major`) -* [ ] `rb_stat_rdev_minor` (`Stat#rdev_minor`) -* [ ] `rb_stat_size` (`Stat#size`) -* [ ] `rb_stat_blksize` (`Stat#blksize`) -* [ ] `rb_stat_blocks` (`Stat#blocks`) -* [ ] `rb_stat_atime` (`Stat#atime`) -* [ ] `rb_stat_mtime` (`Stat#mtime`) -* [ ] `rb_stat_ctime` (`Stat#ctime`) -* [ ] `rb_stat_birthtime` (`Stat#birthtime`) -* [ ] `rb_stat_inspect` (`Stat#inspect`) -* [ ] `rb_stat_ftype` (`Stat#ftype`) -* [ ] `rb_stat_d` (`Stat#directory?`) -* [ ] `rb_stat_r` (`Stat#readable?`) -* [ ] `rb_stat_R` (`Stat#readable_real?`) -* [ ] `rb_stat_wr` (`Stat#world_readable?`) -* [ ] `rb_stat_w` (`Stat#writable?`) -* [ ] `rb_stat_W` (`Stat#writable_real?`) -* [ ] `rb_stat_ww` (`Stat#world_writable?`) -* [ ] `rb_stat_x` (`Stat#executable?`) -* [ ] `rb_stat_X` (`Stat#executable_real?`) -* [ ] `rb_stat_f` (`Stat#file?`) -* [ ] `rb_stat_z` (`Stat#zero?`) -* [ ] `rb_stat_s` (`Stat#size?`) -* [ ] `rb_stat_owned` (`Stat#owned?`) -* [ ] `rb_stat_grpowned` (`Stat#grpowned?`) -* [ ] `rb_stat_p` (`Stat#pipe?`) -* [ ] `rb_stat_l` (`Stat#symlink?`) -* [ ] `rb_stat_S` (`Stat#socket?`) -* [ ] `rb_stat_b` (`Stat#blockdev?`) -* [ ] `rb_stat_c` (`Stat#chardev?`) -* [ ] `rb_stat_suid` (`Stat#setuid?`) -* [ ] `rb_stat_sgid` (`Stat#setgid?`) -* [ ] `rb_stat_sticky` (`Stat#sticky?`) - -## `gc.c` -* [✔ ] `rb_obj_id` (`BasicObject#__id__`) -* [✔ ] `rb_obj_id` (`Kernel#object_id`) -* [ ] `wmap_aset` (`WeakMap#[]=`) -* [ ] `wmap_aref` (`WeakMap#[]`) -* [ ] `wmap_has_key` (`WeakMap#include?`) -* [ ] `wmap_has_key` (`WeakMap#member?`) -* [ ] `wmap_has_key` (`WeakMap#key?`) -* [ ] `wmap_inspect` (`WeakMap#inspect`) -* [ ] `wmap_each` (`WeakMap#each`) -* [ ] `wmap_each` (`WeakMap#each_pair`) -* [ ] `wmap_each_key` (`WeakMap#each_key`) -* [ ] `wmap_each_value` (`WeakMap#each_value`) -* [ ] `wmap_keys` (`WeakMap#keys`) -* [ ] `wmap_values` (`WeakMap#values`) -* [ ] `wmap_size` (`WeakMap#size`) -* [ ] `wmap_size` (`WeakMap#length`) - -## `hash.c` -* [ ] `rb_hash_initialize` (`Hash#initialize`) -* [ ] `rb_hash_replace` (`Hash#initialize_copy`) -* [✔ ] `rb_hash_rehash` (`Hash#rehash`) -* [ ] `rb_hash_to_hash` (`Hash#to_hash`) -* [ ] `rb_hash_to_h` (`Hash#to_h`) -* [ ] `rb_hash_to_a` (`Hash#to_a`) -* [ ] `rb_hash_inspect` (`Hash#inspect`) -* [ ] `rb_hash_to_proc` (`Hash#to_proc`) -* [ ] `rb_hash_equal` (`Hash#==`) -* [✔ ] `rb_hash_aref` (`Hash#[]`) -* [ ] `rb_hash_hash` (`Hash#hash`) -* [ ] `rb_hash_eql` (`Hash#eql?`) -* [ ] `rb_hash_fetch_m` (`Hash#fetch`) -* [✔ ] `rb_hash_aset` (`Hash#[]=`) -* [✔ ] `rb_hash_aset` (`Hash#store`) -* [ ] `rb_hash_default` (`Hash#default`) -* [ ] `rb_hash_set_default` (`Hash#default=`) -* [ ] `rb_hash_default_proc` (`Hash#default_proc`) -* [✔ ] `rb_hash_set_default_proc` (`Hash#default_proc=`) -* [ ] `rb_hash_key` (`Hash#key`) -* [ ] `rb_hash_index` (`Hash#index`) -* [✔ ] `rb_hash_size` (`Hash#size`) -* [✔ ] `rb_hash_size` (`Hash#length`) -* [ ] `rb_hash_empty_p` (`Hash#empty?`) -* [ ] `rb_hash_each_value` (`Hash#each_value`) -* [ ] `rb_hash_each_key` (`Hash#each_key`) -* [ ] `rb_hash_each_pair` (`Hash#each_pair`) -* [ ] `rb_hash_each_pair` (`Hash#each`) -* [ ] `rb_hash_transform_keys` (`Hash#transform_keys`) -* [ ] `rb_hash_transform_keys_bang` (`Hash#transform_keys!`) -* [ ] `rb_hash_transform_values` (`Hash#transform_values`) -* [ ] `rb_hash_transform_values_bang` (`Hash#transform_values!`) -* [✔ ] `rb_hash_keys` (`Hash#keys`) -* [✔ ] `rb_hash_values` (`Hash#values`) -* [✔ ] `rb_hash_values_at` (`Hash#values_at`) -* [✔ ] `rb_hash_fetch_values` (`Hash#fetch_values`) -* [ ] `rb_hash_shift` (`Hash#shift`) -* [ ] `rb_hash_delete_m` (`Hash#delete`) -* [✔ ] `rb_hash_delete_if` (`Hash#delete_if`) -* [✔ ] `rb_hash_keep_if` (`Hash#keep_if`) -* [✔ ] `rb_hash_select` (`Hash#select`) -* [✔ ] `rb_hash_select_bang` (`Hash#select!`) -* [✔ ] `rb_hash_select` (`Hash#filter`) -* [✔ ] `rb_hash_select_bang` (`Hash#filter!`) -* [✔ ] `rb_hash_reject` (`Hash#reject`) -* [✔ ] `rb_hash_reject_bang` (`Hash#reject!`) -* [ ] `rb_hash_slice` (`Hash#slice`) -* [✔ ] `rb_hash_clear` (`Hash#clear`) -* [ ] `rb_hash_invert` (`Hash#invert`) -* [ ] `rb_hash_update` (`Hash#update`) -* [ ] `rb_hash_replace` (`Hash#replace`) -* [ ] `rb_hash_update` (`Hash#merge!`) -* [ ] `rb_hash_merge` (`Hash#merge`) -* [✔ ] `rb_hash_assoc` (`Hash#assoc`) -* [✔ ] `rb_hash_rassoc` (`Hash#rassoc`) -* [ ] `rb_hash_flatten` (`Hash#flatten`) -* [ ] `rb_hash_compact` (`Hash#compact`) -* [ ] `rb_hash_compact_bang` (`Hash#compact!`) -* [✔ ] `rb_hash_has_key` (`Hash#include?`) -* [✔ ] `rb_hash_has_key` (`Hash#member?`) -* [✔ ] `rb_hash_has_key` (`Hash#has_key?`) -* [ ] `rb_hash_has_value` (`Hash#has_value?`) -* [✔ ] `rb_hash_has_key` (`Hash#key?`) -* [ ] `rb_hash_has_value` (`Hash#value?`) -* [ ] `rb_hash_compare_by_id` (`Hash#compare_by_identity`) -* [✔ ] `rb_hash_compare_by_id_p` (`Hash#compare_by_identity?`) -* [ ] `rb_hash_any_p` (`Hash#any?`) -* [ ✔] `rb_hash_dig` (`Hash#dig`) -* [ ] `rb_hash_le` (`Hash#<=`) -* [ ] `rb_hash_lt` (`Hash#<`) -* [ ] `rb_hash_ge` (`Hash#>=`) -* [ ] `rb_hash_gt` (`Hash#>`) -* [ ] `rb_hash_deconstruct_keys` (`Hash#deconstruct_keys`) - -## `io.c` -* [ ] `rb_obj_display` (`Kernel#display`) -* [ ] `rb_io_initialize` (`IO#initialize`) -* [ ] `rb_io_init_copy` (`IO#initialize_copy`) -* [ ] `rb_io_reopen` (`IO#reopen`) -* [✔ ] `rb_io_print` (`IO#print`) -* [ ] `rb_io_putc` (`IO#putc`) -* [✔ ] `rb_io_puts` (`IO#puts`) -* [✔ ] `rb_io_printf` (`IO#printf`) -* [ ] `rb_io_each_line` (`IO#each`) -* [ ] `rb_io_each_line` (`IO#each_line`) -* [ ] `rb_io_each_byte` (`IO#each_byte`) -* [ ] `rb_io_each_char` (`IO#each_char`) -* [ ] `rb_io_each_codepoint` (`IO#each_codepoint`) -* [ ] `rb_io_lines` (`IO#lines`) -* [ ] `rb_io_bytes` (`IO#bytes`) -* [ ] `rb_io_chars` (`IO#chars`) -* [ ] `rb_io_codepoints` (`IO#codepoints`) -* [ ] `rb_io_syswrite` (`IO#syswrite`) -* [ ] `rb_io_sysread` (`IO#sysread`) -* [ ] `rb_io_pread` (`IO#pread`) -* [ ] `rb_io_pwrite` (`IO#pwrite`) -* [ ] `rb_io_fileno` (`IO#fileno`) -* [ ] `rb_io_to_io` (`IO#to_io`) -* [ ] `rb_io_fsync` (`IO#fsync`) -* [ ] `rb_io_fdatasync` (`IO#fdatasync`) -* [ ] `rb_io_sync` (`IO#sync`) -* [ ] `rb_io_set_sync` (`IO#sync=`) -* [ ] `rb_io_lineno` (`IO#lineno`) -* [ ] `rb_io_set_lineno` (`IO#lineno=`) -* [ ] `rb_io_readlines` (`IO#readlines`) -* [ ] `io_readpartial` (`IO#readpartial`) -* [ ] `io_read` (`IO#read`) -* [ ] `io_write_m` (`IO#write`) -* [ ] `rb_io_gets_m` (`IO#gets`) -* [ ] `rb_io_readline` (`IO#readline`) -* [ ] `rb_io_getc` (`IO#getc`) -* [✔ ] `rb_io_getbyte` (`IO#getbyte`) -* [ ] `rb_io_readchar` (`IO#readchar`) -* [ ] `rb_io_readbyte` (`IO#readbyte`) -* [✔ ] `rb_io_ungetbyte` (`IO#ungetbyte`) -* [✔ ] `rb_io_ungetc` (`IO#ungetc`) -* [✔ ] `rb_io_addstr` (`IO#<<`) -* [✔ ] `rb_io_flush` (`IO#flush`) -* [ ] `rb_io_tell` (`IO#tell`) -* [ ] `rb_io_seek_m` (`IO#seek`) -* [ ] `rb_io_rewind` (`IO#rewind`) -* [ ] `rb_io_tell` (`IO#pos`) -* [ ] `rb_io_set_pos` (`IO#pos=`) -* [✔ ] `rb_io_eof` (`IO#eof`) -* [✔ ] `rb_io_eof` (`IO#eof?`) -* [ ] `rb_io_close_on_exec_p` (`IO#close_on_exec?`) -* [ ] `rb_io_set_close_on_exec` (`IO#close_on_exec=`) -* [ ] `rb_io_close_m` (`IO#close`) -* [ ] `rb_io_closed` (`IO#closed?`) -* [ ] `rb_io_close_read` (`IO#close_read`) -* [ ] `rb_io_close_write` (`IO#close_write`) -* [ ] `rb_io_isatty` (`IO#isatty`) -* [ ] `rb_io_isatty` (`IO#tty?`) -* [ ] `rb_io_binmode_m` (`IO#binmode`) -* [ ] `rb_io_binmode_p` (`IO#binmode?`) -* [ ] `rb_io_sysseek` (`IO#sysseek`) -* [ ] `rb_io_advise` (`IO#advise`) -* [ ] `rb_io_ioctl` (`IO#ioctl`) -* [ ] `rb_io_fcntl` (`IO#fcntl`) -* [ ] `rb_io_pid` (`IO#pid`) -* [ ] `rb_io_inspect` (`IO#inspect`) -* [ ] `rb_io_external_encoding` (`IO#external_encoding`) -* [ ] `rb_io_internal_encoding` (`IO#internal_encoding`) -* [ ] `rb_io_set_encoding` (`IO#set_encoding`) -* [ ] `rb_io_set_encoding_by_bom` (`IO#set_encoding_by_bom`) -* [ ] `rb_io_autoclose_p` (`IO#autoclose?`) -* [ ] `rb_io_set_autoclose` (`IO#autoclose=`) -* [ ] `argf_initialize` (`ARGF#initialize`) -* [ ] `argf_initialize_copy` (`ARGF#initialize_copy`) -* [ ] `argf_to_s` (`ARGF#to_s`) -* [ ] `argf_argv` (`ARGF#argv`) -* [ ] `argf_fileno` (`ARGF#fileno`) -* [ ] `argf_fileno` (`ARGF#to_i`) -* [ ] `argf_to_io` (`ARGF#to_io`) -* [ ] `argf_write_io` (`ARGF#to_write_io`) -* [ ] `argf_each_line` (`ARGF#each`) -* [ ] `argf_each_line` (`ARGF#each_line`) -* [ ] `argf_each_byte` (`ARGF#each_byte`) -* [ ] `argf_each_char` (`ARGF#each_char`) -* [ ] `argf_each_codepoint` (`ARGF#each_codepoint`) -* [ ] `argf_lines` (`ARGF#lines`) -* [ ] `argf_bytes` (`ARGF#bytes`) -* [ ] `argf_chars` (`ARGF#chars`) -* [ ] `argf_codepoints` (`ARGF#codepoints`) -* [ ] `argf_read` (`ARGF#read`) -* [ ] `argf_readpartial` (`ARGF#readpartial`) -* [ ] `argf_read_nonblock` (`ARGF#read_nonblock`) -* [ ] `argf_readlines` (`ARGF#readlines`) -* [ ] `argf_readlines` (`ARGF#to_a`) -* [ ] `argf_gets` (`ARGF#gets`) -* [ ] `argf_readline` (`ARGF#readline`) -* [ ] `argf_getc` (`ARGF#getc`) -* [ ] `argf_getbyte` (`ARGF#getbyte`) -* [ ] `argf_readchar` (`ARGF#readchar`) -* [ ] `argf_readbyte` (`ARGF#readbyte`) -* [ ] `argf_tell` (`ARGF#tell`) -* [ ] `argf_seek_m` (`ARGF#seek`) -* [ ] `argf_rewind` (`ARGF#rewind`) -* [ ] `argf_tell` (`ARGF#pos`) -* [ ] `argf_set_pos` (`ARGF#pos=`) -* [ ] `argf_eof` (`ARGF#eof`) -* [ ] `argf_eof` (`ARGF#eof?`) -* [ ] `argf_binmode_m` (`ARGF#binmode`) -* [ ] `argf_binmode_p` (`ARGF#binmode?`) -* [ ] `argf_write` (`ARGF#write`) -* [✔ ] `rb_io_print` (`ARGF#print`) -* [ ] `rb_io_putc` (`ARGF#putc`) -* [✔ ] `rb_io_puts` (`ARGF#puts`) -* [✔ ] `rb_io_printf` (`ARGF#printf`) -* [ ] `argf_filename` (`ARGF#filename`) -* [ ] `argf_filename` (`ARGF#path`) -* [ ] `argf_file` (`ARGF#file`) -* [ ] `argf_skip` (`ARGF#skip`) -* [ ] `argf_close_m` (`ARGF#close`) -* [ ] `argf_closed` (`ARGF#closed?`) -* [ ] `argf_lineno` (`ARGF#lineno`) -* [ ] `argf_set_lineno` (`ARGF#lineno=`) -* [ ] `argf_inplace_mode_get` (`ARGF#inplace_mode`) -* [ ] `argf_inplace_mode_set` (`ARGF#inplace_mode=`) -* [ ] `argf_external_encoding` (`ARGF#external_encoding`) -* [ ] `argf_internal_encoding` (`ARGF#internal_encoding`) -* [ ] `argf_set_encoding` (`ARGF#set_encoding`) -* [ ] `rb_file_initialize` (`File#initialize`) - -## `iseq.c` -* [ ] `iseqw_inspect` (`InstructionSequence#inspect`) -* [ ] `iseqw_disasm` (`InstructionSequence#disasm`) -* [ ] `iseqw_disasm` (`InstructionSequence#disassemble`) -* [ ] `iseqw_to_a` (`InstructionSequence#to_a`) -* [ ] `iseqw_eval` (`InstructionSequence#eval`) -* [ ] `iseqw_to_binary` (`InstructionSequence#to_binary`) -* [ ] `iseqw_path` (`InstructionSequence#path`) -* [ ] `iseqw_absolute_path` (`InstructionSequence#absolute_path`) -* [ ] `iseqw_label` (`InstructionSequence#label`) -* [ ] `iseqw_base_label` (`InstructionSequence#base_label`) -* [ ] `iseqw_first_lineno` (`InstructionSequence#first_lineno`) -* [ ] `iseqw_trace_points` (`InstructionSequence#trace_points`) -* [ ] `iseqw_each_child` (`InstructionSequence#each_child`) - -## `load.c` -* [ ] `rb_mod_autoload` (`Module#autoload`) -* [ ] `rb_mod_autoload_p` (`Module#autoload?`) - -## `numeric.c` -* [ ] `num_sadded` (`Numeric#singleton_method_added`) -* [ ] `num_coerce` (`Numeric#coerce`) -* [ ] `num_clone` (`Numeric#clone`) -* [ ] `num_dup` (`Numeric#dup`) -* [ ] `num_imaginary` (`Numeric#i`) -* [ ] `num_uplus` (`Numeric#+@`) -* [ ] `num_uminus` (`Numeric#-@`) -* [ ] `num_cmp` (`Numeric#<=>`) -* [ ] `num_eql` (`Numeric#eql?`) -* [ ] `num_fdiv` (`Numeric#fdiv`) -* [ ] `num_div` (`Numeric#div`) -* [ ] `num_divmod` (`Numeric#divmod`) -* [ ] `num_modulo` (`Numeric#%`) -* [ ] `num_modulo` (`Numeric#modulo`) -* [ ] `num_remainder` (`Numeric#remainder`) -* [ ] `num_abs` (`Numeric#abs`) -* [ ] `num_abs` (`Numeric#magnitude`) -* [ ] `num_to_int` (`Numeric#to_int`) -* [ ] `num_real_p` (`Numeric#real?`) -* [ ] `num_int_p` (`Numeric#integer?`) -* [ ] `num_zero_p` (`Numeric#zero?`) -* [ ] `num_nonzero_p` (`Numeric#nonzero?`) -* [ ] `num_finite_p` (`Numeric#finite?`) -* [ ] `num_infinite_p` (`Numeric#infinite?`) -* [ ] `num_floor` (`Numeric#floor`) -* [ ] `num_ceil` (`Numeric#ceil`) -* [ ] `num_round` (`Numeric#round`) -* [ ] `num_truncate` (`Numeric#truncate`) -* [ ] `num_step` (`Numeric#step`) -* [ ] `num_positive_p` (`Numeric#positive?`) -* [ ] `num_negative_p` (`Numeric#negative?`) -* [ ] `int_to_s` (`Integer#to_s`) -* [ ] `int_int_p` (`Integer#integer?`) -* [✔ ] `rb_int_odd_p` (`Integer#odd?`) -* [ ] `int_even_p` (`Integer#even?`) -* [ ] `int_allbits_p` (`Integer#allbits?`) -* [ ] `int_anybits_p` (`Integer#anybits?`) -* [ ] `int_nobits_p` (`Integer#nobits?`) -* [ ] `int_upto` (`Integer#upto`) -* [ ] `int_downto` (`Integer#downto`) -* [ ] `int_dotimes` (`Integer#times`) -* [ ] `int_succ` (`Integer#succ`) -* [ ] `int_succ` (`Integer#next`) -* [ ] `int_pred` (`Integer#pred`) -* [ ] `int_chr` (`Integer#chr`) -* [ ] `int_ord` (`Integer#ord`) -* [ ] `int_to_i` (`Integer#to_i`) -* [ ] `int_to_i` (`Integer#to_int`) -* [ ✔] `int_to_f` (`Integer#to_f`) -* [ ] `int_floor` (`Integer#floor`) -* [ ] `int_ceil` (`Integer#ceil`) -* [ ] `int_truncate` (`Integer#truncate`) -* [ ] `int_round` (`Integer#round`) -* [✔ ] `rb_int_cmp` (`Integer#<=>`) -* [✔ ] `rb_int_uminus` (`Integer#-@`) -* [✔ ] `rb_int_plus` (`Integer#+`) -* [✔ ] `rb_int_minus` (`Integer#-`) -* [✔ ] `rb_int_mul` (`Integer#*`) -* [✔ ] `rb_int_div` (`Integer#/`) -* [✔ ] `rb_int_idiv` (`Integer#div`) -* [✔ ] `rb_int_modulo` (`Integer#%`) -* [✔ ] `rb_int_modulo` (`Integer#modulo`) -* [ ] `int_remainder` (`Integer#remainder`) -* [✔ ] `rb_int_divmod` (`Integer#divmod`) -* [✔ ] `rb_int_fdiv` (`Integer#fdiv`) -* [✔ ] `rb_int_pow` (`Integer#**`) -* [✔ ] `rb_int_powm` (`Integer#pow`) -* [✔ ] `rb_int_abs` (`Integer#abs`) -* [✔ ] `rb_int_abs` (`Integer#magnitude`) -* [✔ ] `rb_int_equal` (`Integer#===`) -* [✔ ] `rb_int_equal` (`Integer#==`) -* [✔ ] `rb_int_gt` (`Integer#>`) -* [✔ ] `rb_int_ge` (`Integer#>=`) -* [ ] `int_lt` (`Integer#<`) -* [ ] `int_le` (`Integer#<=`) -* [ ] `int_comp` (`Integer#~`) -* [✔ ] `rb_int_and` (`Integer#&`) -* [ ] `int_or` (`Integer#|`) -* [ ] `int_xor` (`Integer#^`) -* [ ] `int_aref` (`Integer#[]`) -* [✔ ] `rb_int_lshift` (`Integer#<<`) -* [ ] `rb_int_rshift` (`Integer#>>`) -* [ ] `int_size` (`Integer#size`) -* [ ] `rb_int_bit_length` (`Integer#bit_length`) -* [ ] `rb_int_digits` (`Integer#digits`) -* [ ] `flo_to_s` (`Float#to_s`) -* [ ] `flo_coerce` (`Float#coerce`) -* [✔ ] `rb_float_uminus` (`Float#-@`) -* [✔ ] `rb_float_plus` (`Float#+`) -* [ ] `flo_minus` (`Float#-`) -* [✔ ] `rb_float_mul` (`Float#*`) -* [✔ ] `rb_float_div` (`Float#/`) -* [ ] `flo_quo` (`Float#quo`) -* [ ] `flo_quo` (`Float#fdiv`) -* [ ] `flo_mod` (`Float#%`) -* [ ] `flo_mod` (`Float#modulo`) -* [ ] `flo_divmod` (`Float#divmod`) -* [✔ ] `rb_float_pow` (`Float#**`) -* [ ] `flo_eq` (`Float#==`) -* [ ] `flo_eq` (`Float#===`) -* [ ] `flo_cmp` (`Float#<=>`) -* [✔ ] `rb_float_gt` (`Float#>`) -* [ ✔] `flo_ge` (`Float#>=`) -* [ ✔] `flo_lt` (`Float#<`) -* [ ✔] `flo_le` (`Float#<=`) -* [ ] `flo_eql` (`Float#eql?`) -* [ ] `flo_hash` (`Float#hash`) -* [ ] `flo_to_f` (`Float#to_f`) -* [✔ ] `rb_float_abs` (`Float#abs`) -* [✔ ] `rb_float_abs` (`Float#magnitude`) -* [ ] `flo_zero_p` (`Float#zero?`) -* [ ] `flo_to_i` (`Float#to_i`) -* [ ] `flo_to_i` (`Float#to_int`) -* [ ] `flo_floor` (`Float#floor`) -* [ ] `flo_ceil` (`Float#ceil`) -* [ ] `flo_round` (`Float#round`) -* [ ] `flo_truncate` (`Float#truncate`) -* [ ] `flo_is_nan_p` (`Float#nan?`) -* [✔ ] `rb_flo_is_infinite_p` (`Float#infinite?`) -* [✔ ] `rb_flo_is_finite_p` (`Float#finite?`) -* [ ] `flo_next_float` (`Float#next_float`) -* [ ] `flo_prev_float` (`Float#prev_float`) -* [ ] `flo_positive_p` (`Float#positive?`) -* [ ] `flo_negative_p` (`Float#negative?`) - -## `object.c` -* [✔ ] `rb_obj_equal` (`BasicObject#==`) -* [✔ ] `rb_obj_equal` (`BasicObject#equal?`) -* [✔ ] `rb_obj_not` (`BasicObject#!`) -* [✔ ] `rb_obj_not_equal` (`BasicObject#!=`) -* [✔ ] `rb_false` (`Kernel#nil?`) -* [✔ ] `rb_equal` (`Kernel#===`) -* [ ] `rb_obj_match` (`Kernel#=~`) -* [ ] `rb_obj_not_match` (`Kernel#!~`) -* [✔ ] `rb_obj_equal` (`Kernel#eql?`) -* [✔ ] `rb_obj_hash` (`Kernel#hash`) -* [ ] `rb_obj_cmp` (`Kernel#<=>`) -* [✔ ] `rb_obj_class` (`Kernel#class`) -* [ ] `rb_obj_singleton_class` (`Kernel#singleton_class`) -* [ ] `rb_obj_clone2` (`Kernel#clone`) -* [✔ ] `rb_obj_dup` (`Kernel#dup`) -* [ ] `rb_obj_itself` (`Kernel#itself`) -* [ ] `rb_obj_yield_self` (`Kernel#yield_self`) -* [ ] `rb_obj_yield_self` (`Kernel#then`) -* [✔ ] `rb_obj_init_copy` (`Kernel#initialize_copy`) -* [✔ ] `rb_obj_init_dup_clone` (`Kernel#initialize_dup`) -* [✔ ] `rb_obj_init_dup_clone` (`Kernel#initialize_clone`) -* [✔ ] `rb_obj_taint` (`Kernel#taint`) -* [✔ ] `rb_obj_tainted` (`Kernel#tainted?`) -* [✔ ] `rb_obj_untaint` (`Kernel#untaint`) -* [✔ ] `rb_obj_untrust` (`Kernel#untrust`) -* [✔ ] `rb_obj_untrusted` (`Kernel#untrusted?`) -* [✔ ] `rb_obj_trust` (`Kernel#trust`) -* [✔ ] `rb_obj_freeze` (`Kernel#freeze`) -* [✔ ] `rb_obj_frozen_p` (`Kernel#frozen?`) -* [✔ ] `rb_any_to_s` (`Kernel#to_s`) -* [ ] `rb_obj_inspect` (`Kernel#inspect`) -* [✔ ] `rb_obj_methods` (`Kernel#methods`) -* [✔ ] `rb_obj_singleton_methods` (`Kernel#singleton_methods`) -* [✔ ] `rb_obj_protected_methods` (`Kernel#protected_methods`) -* [✔ ] `rb_obj_private_methods` (`Kernel#private_methods`) -* [✔ ] `rb_obj_public_methods` (`Kernel#public_methods`) -* [✔ ] `rb_obj_instance_variables` (`Kernel#instance_variables`) -* [ ] `rb_obj_ivar_get` (`Kernel#instance_variable_get`) -* [ ] `rb_obj_ivar_set` (`Kernel#instance_variable_set`) -* [ ] `rb_obj_ivar_defined` (`Kernel#instance_variable_defined?`) -* [✔ ] `rb_obj_is_instance_of` (`Kernel#instance_of?`) -* [✔ ] `rb_obj_is_kind_of` (`Kernel#kind_of?`) -* [✔ ] `rb_obj_is_kind_of` (`Kernel#is_a?`) -* [✔ ] `rb_obj_tap` (`Kernel#tap`) -* [ ] `nil_to_i` (`NilClass#to_i`) -* [ ] `nil_to_f` (`NilClass#to_f`) -* [ ] `nil_to_s` (`NilClass#to_s`) -* [ ] `nil_to_a` (`NilClass#to_a`) -* [ ] `nil_to_h` (`NilClass#to_h`) -* [ ] `nil_inspect` (`NilClass#inspect`) -* [ ] `nil_match` (`NilClass#=~`) -* [ ] `false_and` (`NilClass#&`) -* [ ] `false_or` (`NilClass#|`) -* [ ] `false_xor` (`NilClass#^`) -* [✔ ] `rb_equal` (`NilClass#===`) -* [ ] `rb_true` (`NilClass#nil?`) -* [ ] `rb_mod_freeze` (`Module#freeze`) -* [ ] `rb_mod_eqq` (`Module#===`) -* [✔ ] `rb_obj_equal` (`Module#==`) -* [ ] `rb_mod_cmp` (`Module#<=>`) -* [ ] `rb_mod_lt` (`Module#<`) -* [✔ ] `rb_class_inherited_p` (`Module#<=`) -* [ ] `rb_mod_gt` (`Module#>`) -* [ ] `rb_mod_ge` (`Module#>=`) -* [✔ ] `rb_mod_init_copy` (`Module#initialize_copy`) -* [ ] `rb_mod_to_s` (`Module#to_s`) -* [✔ ] `rb_mod_included_modules` (`Module#included_modules`) -* [✔ ] `rb_mod_include_p` (`Module#include?`) -* [✔ ] `rb_mod_name` (`Module#name`) -* [✔ ] `rb_mod_ancestors` (`Module#ancestors`) -* [✔ ] `rb_mod_attr` (`Module#attr`) -* [ ] `rb_mod_attr_reader` (`Module#attr_reader`) -* [ ] `rb_mod_attr_writer` (`Module#attr_writer`) -* [ ] `rb_mod_attr_accessor` (`Module#attr_accessor`) -* [ ] `rb_mod_initialize` (`Module#initialize`) -* [ ] `rb_mod_initialize_clone` (`Module#initialize_clone`) -* [✔ ] `rb_class_instance_methods` (`Module#instance_methods`) -* [✔ ] `rb_mod_constants` (`Module#constants`) -* [ ] `rb_mod_const_get` (`Module#const_get`) -* [ ] `rb_mod_const_set` (`Module#const_set`) -* [ ] `rb_mod_const_defined` (`Module#const_defined?`) -* [ ] `rb_mod_const_source_location` (`Module#const_source_location`) -* [ ] `rb_mod_cvar_get` (`Module#class_variable_get`) -* [ ] `rb_mod_cvar_set` (`Module#class_variable_set`) -* [ ] `rb_mod_cvar_defined` (`Module#class_variable_defined?`) -* [✔ ] `rb_mod_public_constant` (`Module#public_constant`) -* [✔ ] `rb_mod_private_constant` (`Module#private_constant`) -* [✔ ] `rb_mod_deprecate_constant` (`Module#deprecate_constant`) -* [ ] `rb_mod_singleton_p` (`Module#singleton_class?`) -* [ ] `rb_class_alloc_m` (`Class#allocate`) -* [ ] `rb_class_s_new` (`Class#new`) -* [ ] `rb_class_initialize` (`Class#initialize`) -* [✔ ] `rb_class_superclass` (`Class#superclass`) -* [ ] `true_to_s` (`TrueClass#to_s`) -* [ ] `true_and` (`TrueClass#&`) -* [ ] `true_or` (`TrueClass#|`) -* [ ] `true_xor` (`TrueClass#^`) -* [✔ ] `rb_equal` (`TrueClass#===`) -* [ ] `false_to_s` (`FalseClass#to_s`) -* [ ] `false_and` (`FalseClass#&`) -* [ ] `false_or` (`FalseClass#|`) -* [ ] `false_xor` (`FalseClass#^`) -* [✔ ] `rb_equal` (`FalseClass#===`) - -## `parse.c` -* [ ] `ripper_initialize` (`Ripper#initialize`) -* [ ] `ripper_parse` (`Ripper#parse`) -* [ ] `ripper_column` (`Ripper#column`) -* [ ] `ripper_filename` (`Ripper#filename`) -* [ ] `ripper_lineno` (`Ripper#lineno`) -* [ ] `ripper_state` (`Ripper#state`) -* [ ] `ripper_token` (`Ripper#token`) -* [✔ ] `rb_parser_end_seen_p` (`Ripper#end_seen?`) -* [✔ ] `rb_parser_encoding` (`Ripper#encoding`) -* [ ] `rb_parser_get_yydebug` (`Ripper#yydebug`) -* [✔ ] `rb_parser_set_yydebug` (`Ripper#yydebug=`) -* [✔ ] `rb_parser_get_debug_output` (`Ripper#debug_output`) -* [✔ ] `rb_parser_set_debug_output` (`Ripper#debug_output=`) -* [ ] `ripper_error_p` (`Ripper#error?`) -* [ ] `ripper_assert_Qundef` (`Ripper#assert_Qundef`) -* [ ] `ripper_value` (`Ripper#rawVALUE`) -* [ ] `ripper_validate_object` (`Ripper#validate_object`) - -## `proc.c` -* [ ] `proc_call` (`Proc#call`) -* [ ] `proc_call` (`Proc#[]`) -* [ ] `proc_call` (`Proc#===`) -* [ ] `proc_call` (`Proc#yield`) -* [ ] `proc_to_proc` (`Proc#to_proc`) -* [ ] `proc_arity` (`Proc#arity`) -* [ ] `proc_clone` (`Proc#clone`) -* [✔ ] `rb_proc_dup` (`Proc#dup`) -* [ ] `proc_hash` (`Proc#hash`) -* [ ] `proc_to_s` (`Proc#to_s`) -* [✔ ] `rb_proc_lambda_p` (`Proc#lambda?`) -* [ ] `proc_binding` (`Proc#binding`) -* [ ] `proc_curry` (`Proc#curry`) -* [ ] `proc_compose_to_left` (`Proc#<<`) -* [ ] `proc_compose_to_right` (`Proc#>>`) -* [✔ ] `rb_proc_location` (`Proc#source_location`) -* [ ] `rb_proc_parameters` (`Proc#parameters`) -* [ ] `proc_ruby2_keywords` (`Proc#ruby2_keywords`) -* [ ] `localjump_xvalue` (`LocalJumpError#exit_value`) -* [ ] `localjump_reason` (`LocalJumpError#reason`) -* [ ] `method_eq` (`Method#==`) -* [ ] `method_eq` (`Method#eql?`) -* [ ] `method_hash` (`Method#hash`) -* [ ] `method_clone` (`Method#clone`) -* [ ] `rb_method_call_pass_called_kw` (`Method#call`) -* [ ] `rb_method_call_pass_called_kw` (`Method#===`) -* [ ] `rb_method_curry` (`Method#curry`) -* [ ] `rb_method_compose_to_left` (`Method#<<`) -* [ ] `rb_method_compose_to_right` (`Method#>>`) -* [ ] `rb_method_call_pass_called_kw` (`Method#[]`) -* [ ] `method_arity_m` (`Method#arity`) -* [ ] `method_inspect` (`Method#inspect`) -* [ ] `method_inspect` (`Method#to_s`) -* [ ] `method_to_proc` (`Method#to_proc`) -* [ ] `method_receiver` (`Method#receiver`) -* [ ] `method_name` (`Method#name`) -* [ ] `method_original_name` (`Method#original_name`) -* [ ] `method_owner` (`Method#owner`) -* [ ] `method_unbind` (`Method#unbind`) -* [✔ ] `rb_method_location` (`Method#source_location`) -* [ ] `rb_method_parameters` (`Method#parameters`) -* [ ] `method_super_method` (`Method#super_method`) -* [✔ ] `rb_obj_method` (`Kernel#method`) -* [✔ ] `rb_obj_public_method` (`Kernel#public_method`) -* [✔ ] `rb_obj_singleton_method` (`Kernel#singleton_method`) -* [ ] `method_eq` (`UnboundMethod#==`) -* [ ] `method_eq` (`UnboundMethod#eql?`) -* [ ] `method_hash` (`UnboundMethod#hash`) -* [ ] `method_clone` (`UnboundMethod#clone`) -* [ ] `method_arity_m` (`UnboundMethod#arity`) -* [ ] `method_inspect` (`UnboundMethod#inspect`) -* [ ] `method_inspect` (`UnboundMethod#to_s`) -* [ ] `method_name` (`UnboundMethod#name`) -* [ ] `method_original_name` (`UnboundMethod#original_name`) -* [ ] `method_owner` (`UnboundMethod#owner`) -* [ ] `umethod_bind` (`UnboundMethod#bind`) -* [ ] `umethod_bind_call` (`UnboundMethod#bind_call`) -* [✔ ] `rb_method_location` (`UnboundMethod#source_location`) -* [ ] `rb_method_parameters` (`UnboundMethod#parameters`) -* [ ] `method_super_method` (`UnboundMethod#super_method`) -* [ ] `rb_mod_instance_method` (`Module#instance_method`) -* [ ] `rb_mod_public_instance_method` (`Module#public_instance_method`) -* [ ] `rb_mod_define_method` (`Module#define_method`) -* [ ] `rb_obj_define_method` (`Kernel#define_singleton_method`) -* [ ] `binding_clone` (`Binding#clone`) -* [ ] `binding_dup` (`Binding#dup`) -* [ ] `bind_eval` (`Binding#eval`) -* [ ] `bind_local_variables` (`Binding#local_variables`) -* [ ] `bind_local_variable_get` (`Binding#local_variable_get`) -* [ ] `bind_local_variable_set` (`Binding#local_variable_set`) -* [ ] `bind_local_variable_defined_p` (`Binding#local_variable_defined?`) -* [ ] `bind_receiver` (`Binding#receiver`) -* [ ] `bind_location` (`Binding#source_location`) - -## `process.c` -* [ ] `detach_process_pid` (`Waiter#pid`) -* [ ] `pst_equal` (`Status#==`) -* [ ] `pst_bitand` (`Status#&`) -* [ ] `pst_rshift` (`Status#>>`) -* [ ] `pst_to_i` (`Status#to_i`) -* [ ] `pst_to_s` (`Status#to_s`) -* [ ] `pst_inspect` (`Status#inspect`) -* [ ] `pst_pid` (`Status#pid`) -* [ ] `pst_wifstopped` (`Status#stopped?`) -* [ ] `pst_wstopsig` (`Status#stopsig`) -* [ ] `pst_wifsignaled` (`Status#signaled?`) -* [ ] `pst_wtermsig` (`Status#termsig`) -* [ ] `pst_wifexited` (`Status#exited?`) -* [ ] `pst_wexitstatus` (`Status#exitstatus`) -* [ ] `pst_success_p` (`Status#success?`) -* [ ] `pst_wcoredump` (`Status#coredump?`) - -## `random.c` -* [ ] `random_init` (`Random#initialize`) -* [ ] `random_rand` (`Random#rand`) -* [ ] `random_bytes` (`Random#bytes`) -* [ ] `random_get_seed` (`Random#seed`) -* [ ] `random_copy` (`Random#initialize_copy`) -* [ ] `random_equal` (`Random#==`) -* [ ] `rand_random_number` (`Formatter#random_number`) -* [ ] `rand_random_number` (`Formatter#rand`) - -## `range.c` -* [ ] `range_initialize` (`Range#initialize`) -* [ ] `range_initialize_copy` (`Range#initialize_copy`) -* [ ] `range_eq` (`Range#==`) -* [ ] `range_eqq` (`Range#===`) -* [ ] `range_eql` (`Range#eql?`) -* [ ] `range_hash` (`Range#hash`) -* [ ] `range_each` (`Range#each`) -* [ ] `range_step` (`Range#step`) -* [ ] `range_percent_step` (`Range#%`) -* [ ] `range_bsearch` (`Range#bsearch`) -* [ ] `range_begin` (`Range#begin`) -* [ ] `range_end` (`Range#end`) -* [ ] `range_first` (`Range#first`) -* [ ] `range_last` (`Range#last`) -* [ ] `range_min` (`Range#min`) -* [ ] `range_max` (`Range#max`) -* [ ] `range_minmax` (`Range#minmax`) -* [ ] `range_size` (`Range#size`) -* [ ] `range_to_a` (`Range#to_a`) -* [ ] `range_to_a` (`Range#entries`) -* [ ] `range_to_s` (`Range#to_s`) -* [ ] `range_inspect` (`Range#inspect`) -* [ ] `range_exclude_end_p` (`Range#exclude_end?`) -* [ ] `range_include` (`Range#member?`) -* [ ] `range_include` (`Range#include?`) -* [ ] `range_cover` (`Range#cover?`) -* [ ] `range_count` (`Range#count`) - -## `rational.c` -* [ ] `nurat_numerator` (`Rational#numerator`) -* [ ] `nurat_denominator` (`Rational#denominator`) -* [✔ ] `rb_rational_uminus` (`Rational#-@`) -* [✔ ] `rb_rational_plus` (`Rational#+`) -* [ ] `nurat_sub` (`Rational#-`) -* [✔ ] `rb_rational_mul` (`Rational#*`) -* [ ] `nurat_div` (`Rational#/`) -* [ ] `nurat_div` (`Rational#quo`) -* [ ] `nurat_fdiv` (`Rational#fdiv`) -* [ ] `nurat_expt` (`Rational#**`) -* [✔ ] `rb_rational_cmp` (`Rational#<=>`) -* [ ] `nurat_eqeq_p` (`Rational#==`) -* [ ] `nurat_coerce` (`Rational#coerce`) -* [ ] `nurat_positive_p` (`Rational#positive?`) -* [ ] `nurat_negative_p` (`Rational#negative?`) -* [✔ ] `rb_rational_abs` (`Rational#abs`) -* [✔ ] `rb_rational_abs` (`Rational#magnitude`) -* [ ] `nurat_floor_n` (`Rational#floor`) -* [ ] `nurat_ceil_n` (`Rational#ceil`) -* [ ] `nurat_truncate_n` (`Rational#truncate`) -* [ ] `nurat_round_n` (`Rational#round`) -* [ ] `nurat_truncate` (`Rational#to_i`) -* [ ] `nurat_to_f` (`Rational#to_f`) -* [ ] `nurat_to_r` (`Rational#to_r`) -* [ ] `nurat_rationalize` (`Rational#rationalize`) -* [ ] `nurat_hash` (`Rational#hash`) -* [ ] `nurat_to_s` (`Rational#to_s`) -* [ ] `nurat_inspect` (`Rational#inspect`) -* [✔ ] `rb_gcd` (`Integer#gcd`) -* [✔ ] `rb_lcm` (`Integer#lcm`) -* [✔ ] `rb_gcdlcm` (`Integer#gcdlcm`) -* [ ] `numeric_numerator` (`Numeric#numerator`) -* [ ] `numeric_denominator` (`Numeric#denominator`) -* [✔ ] `rb_numeric_quo` (`Numeric#quo`) -* [ ] `integer_numerator` (`Integer#numerator`) -* [ ] `integer_denominator` (`Integer#denominator`) -* [✔ ] `rb_float_numerator` (`Float#numerator`) -* [✔ ] `rb_float_denominator` (`Float#denominator`) -* [ ] `nilclass_to_r` (`NilClass#to_r`) -* [ ] `nilclass_rationalize` (`NilClass#rationalize`) -* [ ] `integer_to_r` (`Integer#to_r`) -* [ ] `integer_rationalize` (`Integer#rationalize`) -* [ ] `float_to_r` (`Float#to_r`) -* [ ] `float_rationalize` (`Float#rationalize`) -* [ ] `string_to_r` (`String#to_r`) - -## `re.c` -* [ ] `rb_reg_initialize_m` (`Regexp#initialize`) -* [ ] `rb_reg_init_copy` (`Regexp#initialize_copy`) -* [ ] `rb_reg_hash` (`Regexp#hash`) -* [ ] `rb_reg_equal` (`Regexp#eql?`) -* [ ] `rb_reg_equal` (`Regexp#==`) -* [✔ ] `rb_reg_match` (`Regexp#=~`) -* [✔ ] `rb_reg_eqq` (`Regexp#===`) -* [✔ ] `rb_reg_match2` (`Regexp#~`) -* [ ] `rb_reg_match_m` (`Regexp#match`) -* [ ] `rb_reg_match_m_p` (`Regexp#match?`) -* [ ] `rb_reg_to_s` (`Regexp#to_s`) -* [ ] `rb_reg_inspect` (`Regexp#inspect`) -* [ ] `rb_reg_source` (`Regexp#source`) -* [ ] `rb_reg_casefold_p` (`Regexp#casefold?`) -* [ ] `rb_reg_options_m` (`Regexp#options`) -* [✔ ] `rb_obj_encoding` (`Regexp#encoding`) -* [ ] `rb_reg_fixed_encoding_p` (`Regexp#fixed_encoding?`) -* [ ] `rb_reg_names` (`Regexp#names`) -* [ ] `rb_reg_named_captures` (`Regexp#named_captures`) -* [ ] `match_init_copy` (`MatchData#initialize_copy`) -* [ ] `match_regexp` (`MatchData#regexp`) -* [ ] `match_names` (`MatchData#names`) -* [ ] `match_size` (`MatchData#size`) -* [ ] `match_size` (`MatchData#length`) -* [ ] `match_offset` (`MatchData#offset`) -* [ ] `match_begin` (`MatchData#begin`) -* [ ] `match_end` (`MatchData#end`) -* [ ] `match_to_a` (`MatchData#to_a`) -* [ ] `match_aref` (`MatchData#[]`) -* [ ] `match_captures` (`MatchData#captures`) -* [ ] `match_named_captures` (`MatchData#named_captures`) -* [ ] `match_values_at` (`MatchData#values_at`) -* [✔ ] `rb_reg_match_pre` (`MatchData#pre_match`) -* [✔ ] `rb_reg_match_post` (`MatchData#post_match`) -* [ ] `match_to_s` (`MatchData#to_s`) -* [ ] `match_inspect` (`MatchData#inspect`) -* [ ] `match_string` (`MatchData#string`) -* [ ] `match_hash` (`MatchData#hash`) -* [ ] `match_equal` (`MatchData#eql?`) -* [ ] `match_equal` (`MatchData#==`) - -## `signal.c` -* [ ] `esignal_init` (`SignalException#initialize`) -* [ ] `esignal_signo` (`SignalException#signo`) -* [ ] `interrupt_init` (`Interrupt#initialize`) - -## `string.c` -* [ ] `rb_str_init` (`String#initialize`) -* [✔ ] `rb_str_replace` (`String#initialize_copy`) -* [ ] `rb_str_cmp_m` (`String#<=>`) -* [✔ ] `rb_str_equal` (`String#==`) -* [✔ ] `rb_str_equal` (`String#===`) -* [✔ ] `rb_str_eql` (`String#eql?`) -* [ ] `rb_str_hash_m` (`String#hash`) -* [ ] `rb_str_casecmp` (`String#casecmp`) -* [ ] `rb_str_casecmp_p` (`String#casecmp?`) -* [✔ ] `rb_str_plus` (`String#+`) -* [✔ ] `rb_str_times` (`String#*`) -* [ ] `rb_str_format_m` (`String#%`) -* [ ✔] `rb_str_aref_m` (`String#[]`) -* [ ] `rb_str_aset_m` (`String#[]=`) -* [ ] `rb_str_insert` (`String#insert`) -* [✔ ] `rb_str_length` (`String#length`) -* [✔ ] `rb_str_length` (`String#size`) -* [ ] `rb_str_bytesize` (`String#bytesize`) -* [ ] `rb_str_empty` (`String#empty?`) -* [ ] `rb_str_match` (`String#=~`) -* [ ] `rb_str_match_m` (`String#match`) -* [ ] `rb_str_match_m_p` (`String#match?`) -* [✔ ] `rb_str_succ` (`String#succ`) -* [ ] `rb_str_succ_bang` (`String#succ!`) -* [✔ ] `rb_str_succ` (`String#next`) -* [ ] `rb_str_succ_bang` (`String#next!`) -* [ ] `rb_str_upto` (`String#upto`) -* [ ] `rb_str_index_m` (`String#index`) -* [ ] `rb_str_rindex_m` (`String#rindex`) -* [✔ ] `rb_str_replace` (`String#replace`) -* [ ] `rb_str_clear` (`String#clear`) -* [ ] `rb_str_chr` (`String#chr`) -* [ ] `rb_str_getbyte` (`String#getbyte`) -* [ ] `rb_str_setbyte` (`String#setbyte`) -* [ ] `rb_str_byteslice` (`String#byteslice`) -* [ ] `str_scrub` (`String#scrub`) -* [ ] `str_scrub_bang` (`String#scrub!`) -* [✔ ] `rb_str_freeze` (`String#freeze`) -* [ ] `str_uplus` (`String#+@`) -* [ ] `str_uminus` (`String#-@`) -* [ ] `rb_str_to_i` (`String#to_i`) -* [ ] `rb_str_to_f` (`String#to_f`) -* [ ] `rb_str_to_s` (`String#to_s`) -* [ ] `rb_str_to_s` (`String#to_str`) -* [✔ ] `rb_str_inspect` (`String#inspect`) -* [✔ ] `rb_str_dump` (`String#dump`) -* [ ] `str_undump` (`String#undump`) -* [ ] `rb_str_upcase` (`String#upcase`) -* [ ] `rb_str_downcase` (`String#downcase`) -* [ ] `rb_str_capitalize` (`String#capitalize`) -* [ ] `rb_str_swapcase` (`String#swapcase`) -* [ ] `rb_str_upcase_bang` (`String#upcase!`) -* [ ] `rb_str_downcase_bang` (`String#downcase!`) -* [ ] `rb_str_capitalize_bang` (`String#capitalize!`) -* [ ] `rb_str_swapcase_bang` (`String#swapcase!`) -* [ ] `rb_str_hex` (`String#hex`) -* [ ] `rb_str_oct` (`String#oct`) -* [ ] `rb_str_split_m` (`String#split`) -* [ ] `rb_str_lines` (`String#lines`) -* [ ] `rb_str_bytes` (`String#bytes`) -* [ ] `rb_str_chars` (`String#chars`) -* [ ] `rb_str_codepoints` (`String#codepoints`) -* [ ] `rb_str_grapheme_clusters` (`String#grapheme_clusters`) -* [ ] `rb_str_reverse` (`String#reverse`) -* [ ] `rb_str_reverse_bang` (`String#reverse!`) -* [ ] `rb_str_concat_multi` (`String#concat`) -* [✔ ] `rb_str_concat` (`String#<<`) -* [ ] `rb_str_prepend_multi` (`String#prepend`) -* [ ] `rb_str_crypt` (`String#crypt`) -* [✔ ] `rb_str_intern` (`String#intern`) -* [✔ ] `rb_str_intern` (`String#to_sym`) -* [✔ ] `rb_str_ord` (`String#ord`) -* [ ] `rb_str_include` (`String#include?`) -* [ ✔] `rb_str_start_with` (`String#start_with?`) -* [ ] `rb_str_end_with` (`String#end_with?`) -* [ ] `rb_str_scan` (`String#scan`) -* [ ] `rb_str_ljust` (`String#ljust`) -* [ ] `rb_str_rjust` (`String#rjust`) -* [ ] `rb_str_center` (`String#center`) -* [ ] `rb_str_sub` (`String#sub`) -* [ ] `rb_str_gsub` (`String#gsub`) -* [ ] `rb_str_chop` (`String#chop`) -* [ ] `rb_str_chomp` (`String#chomp`) -* [ ] `rb_str_strip` (`String#strip`) -* [ ] `rb_str_lstrip` (`String#lstrip`) -* [ ] `rb_str_rstrip` (`String#rstrip`) -* [ ] `rb_str_delete_prefix` (`String#delete_prefix`) -* [ ] `rb_str_delete_suffix` (`String#delete_suffix`) -* [ ] `rb_str_sub_bang` (`String#sub!`) -* [ ] `rb_str_gsub_bang` (`String#gsub!`) -* [ ] `rb_str_chop_bang` (`String#chop!`) -* [ ] `rb_str_chomp_bang` (`String#chomp!`) -* [ ] `rb_str_strip_bang` (`String#strip!`) -* [ ] `rb_str_lstrip_bang` (`String#lstrip!`) -* [ ] `rb_str_rstrip_bang` (`String#rstrip!`) -* [ ] `rb_str_delete_prefix_bang` (`String#delete_prefix!`) -* [ ] `rb_str_delete_suffix_bang` (`String#delete_suffix!`) -* [ ] `rb_str_tr` (`String#tr`) -* [ ] `rb_str_tr_s` (`String#tr_s`) -* [ ] `rb_str_delete` (`String#delete`) -* [ ] `rb_str_squeeze` (`String#squeeze`) -* [ ] `rb_str_count` (`String#count`) -* [ ] `rb_str_tr_bang` (`String#tr!`) -* [ ] `rb_str_tr_s_bang` (`String#tr_s!`) -* [ ] `rb_str_delete_bang` (`String#delete!`) -* [ ] `rb_str_squeeze_bang` (`String#squeeze!`) -* [ ] `rb_str_each_line` (`String#each_line`) -* [ ] `rb_str_each_byte` (`String#each_byte`) -* [ ] `rb_str_each_char` (`String#each_char`) -* [ ] `rb_str_each_codepoint` (`String#each_codepoint`) -* [ ] `rb_str_each_grapheme_cluster` (`String#each_grapheme_cluster`) -* [ ] `rb_str_sum` (`String#sum`) -* [ ] `rb_str_aref_m` (`String#slice`) -* [ ] `rb_str_slice_bang` (`String#slice!`) -* [ ] `rb_str_partition` (`String#partition`) -* [ ] `rb_str_rpartition` (`String#rpartition`) -* [✔ ] `rb_obj_encoding` (`String#encoding`) -* [ ] `rb_str_force_encoding` (`String#force_encoding`) -* [ ] `rb_str_b` (`String#b`) -* [ ] `rb_str_valid_encoding_p` (`String#valid_encoding?`) -* [ ] `rb_str_is_ascii_only_p` (`String#ascii_only?`) -* [ ] `rb_str_unicode_normalize` (`String#unicode_normalize`) -* [ ] `rb_str_unicode_normalize_bang` (`String#unicode_normalize!`) -* [ ] `rb_str_unicode_normalized_p` (`String#unicode_normalized?`) -* [ ] `sym_equal` (`Symbol#==`) -* [ ] `sym_equal` (`Symbol#===`) -* [ ] `sym_inspect` (`Symbol#inspect`) -* [✔ ] `rb_sym_to_s` (`Symbol#to_s`) -* [✔ ] `rb_sym_to_s` (`Symbol#id2name`) -* [ ] `sym_to_sym` (`Symbol#intern`) -* [ ] `sym_to_sym` (`Symbol#to_sym`) -* [✔ ] `rb_sym_to_proc` (`Symbol#to_proc`) -* [ ] `sym_succ` (`Symbol#succ`) -* [ ] `sym_succ` (`Symbol#next`) -* [ ] `sym_cmp` (`Symbol#<=>`) -* [ ] `sym_casecmp` (`Symbol#casecmp`) -* [ ] `sym_casecmp_p` (`Symbol#casecmp?`) -* [ ] `sym_match` (`Symbol#=~`) -* [ ] `sym_aref` (`Symbol#[]`) -* [ ] `sym_aref` (`Symbol#slice`) -* [ ] `sym_length` (`Symbol#length`) -* [ ] `sym_length` (`Symbol#size`) -* [ ] `sym_empty` (`Symbol#empty?`) -* [ ] `sym_match_m` (`Symbol#match`) -* [ ] `sym_match_m_p` (`Symbol#match?`) -* [ ] `sym_upcase` (`Symbol#upcase`) -* [ ] `sym_downcase` (`Symbol#downcase`) -* [ ] `sym_capitalize` (`Symbol#capitalize`) -* [ ] `sym_swapcase` (`Symbol#swapcase`) -* [ ] `sym_start_with` (`Symbol#start_with?`) -* [ ] `sym_end_with` (`Symbol#end_with?`) -* [ ] `sym_encoding` (`Symbol#encoding`) - -## `struct.c` -* [ ] `rb_struct_initialize_m` (`Struct#initialize`) -* [✔ ] `rb_struct_init_copy` (`Struct#initialize_copy`) -* [ ] `rb_struct_equal` (`Struct#==`) -* [ ] `rb_struct_eql` (`Struct#eql?`) -* [ ] `rb_struct_hash` (`Struct#hash`) -* [ ] `rb_struct_inspect` (`Struct#inspect`) -* [ ] `rb_struct_to_a` (`Struct#to_a`) -* [ ] `rb_struct_to_h` (`Struct#to_h`) -* [ ] `rb_struct_to_a` (`Struct#values`) -* [✔ ] `rb_struct_size` (`Struct#size`) -* [✔ ] `rb_struct_size` (`Struct#length`) -* [ ] `rb_struct_each` (`Struct#each`) -* [ ] `rb_struct_each_pair` (`Struct#each_pair`) -* [✔ ] `rb_struct_aref` (`Struct#[]`) -* [✔ ] `rb_struct_aset` (`Struct#[]=`) -* [ ] `rb_struct_select` (`Struct#select`) -* [ ] `rb_struct_select` (`Struct#filter`) -* [ ] `rb_struct_values_at` (`Struct#values_at`) -* [ ] `rb_struct_members_m` (`Struct#members`) -* [ ] `rb_struct_dig` (`Struct#dig`) -* [ ] `rb_struct_to_a` (`Struct#deconstruct`) -* [ ] `rb_struct_deconstruct_keys` (`Struct#deconstruct_keys`) - -## `thread.c` -* [ ] `rb_thread_pending_interrupt_p` (`Thread#pending_interrupt?`) -* [ ] `thread_initialize` (`Thread#initialize`) -* [ ] `thread_raise_m` (`Thread#raise`) -* [ ] `thread_join_m` (`Thread#join`) -* [ ] `thread_value` (`Thread#value`) -* [✔ ] `rb_thread_kill` (`Thread#kill`) -* [✔ ] `rb_thread_kill` (`Thread#terminate`) -* [✔ ] `rb_thread_kill` (`Thread#exit`) -* [✔ ] `rb_thread_run` (`Thread#run`) -* [✔ ] `rb_thread_wakeup` (`Thread#wakeup`) -* [ ] `rb_thread_aref` (`Thread#[]`) -* [ ] `rb_thread_aset` (`Thread#[]=`) -* [ ] `rb_thread_fetch` (`Thread#fetch`) -* [ ] `rb_thread_key_p` (`Thread#key?`) -* [ ] `rb_thread_keys` (`Thread#keys`) -* [ ] `rb_thread_priority` (`Thread#priority`) -* [ ] `rb_thread_priority_set` (`Thread#priority=`) -* [ ] `rb_thread_status` (`Thread#status`) -* [ ] `rb_thread_variable_get` (`Thread#thread_variable_get`) -* [ ] `rb_thread_variable_set` (`Thread#thread_variable_set`) -* [ ] `rb_thread_variables` (`Thread#thread_variables`) -* [ ] `rb_thread_variable_p` (`Thread#thread_variable?`) -* [ ] `rb_thread_alive_p` (`Thread#alive?`) -* [ ] `rb_thread_stop_p` (`Thread#stop?`) -* [ ] `rb_thread_abort_exc` (`Thread#abort_on_exception`) -* [ ] `rb_thread_abort_exc_set` (`Thread#abort_on_exception=`) -* [ ] `rb_thread_report_exc` (`Thread#report_on_exception`) -* [ ] `rb_thread_report_exc_set` (`Thread#report_on_exception=`) -* [ ] `rb_thread_safe_level` (`Thread#safe_level`) -* [✔ ] `rb_thread_group` (`Thread#group`) -* [ ] `rb_thread_backtrace_m` (`Thread#backtrace`) -* [ ] `rb_thread_backtrace_locations_m` (`Thread#backtrace_locations`) -* [ ] `rb_thread_getname` (`Thread#name`) -* [ ] `rb_thread_setname` (`Thread#name=`) -* [ ] `rb_thread_to_s` (`Thread#to_s`) -* [ ] `thgroup_list` (`ThreadGroup#list`) -* [ ] `thgroup_enclose` (`ThreadGroup#enclose`) -* [ ] `thgroup_enclosed_p` (`ThreadGroup#enclosed?`) -* [ ] `thgroup_add` (`ThreadGroup#add`) - -## `thread_sync.c` -* [ ] `mutex_initialize` (`Mutex#initialize`) -* [✔ ] `rb_mutex_locked_p` (`Mutex#locked?`) -* [✔ ] `rb_mutex_trylock` (`Mutex#try_lock`) -* [✔ ] `rb_mutex_lock` (`Mutex#lock`) -* [✔ ] `rb_mutex_unlock` (`Mutex#unlock`) -* [ ] `mutex_sleep` (`Mutex#sleep`) -* [ ] `rb_mutex_synchronize_m` (`Mutex#synchronize`) -* [✔ ] `rb_mutex_owned_p` (`Mutex#owned?`) -* [ ] `rb_queue_initialize` (`Queue#initialize`) -* [ ] `undumpable` (`Queue#marshal_dump`) -* [ ] `rb_queue_close` (`Queue#close`) -* [ ] `rb_queue_closed_p` (`Queue#closed?`) -* [ ] `rb_queue_push` (`Queue#push`) -* [ ] `rb_queue_pop` (`Queue#pop`) -* [ ] `rb_queue_empty_p` (`Queue#empty?`) -* [ ] `rb_queue_clear` (`Queue#clear`) -* [ ] `rb_queue_length` (`Queue#length`) -* [ ] `rb_queue_num_waiting` (`Queue#num_waiting`) -* [ ] `rb_szqueue_initialize` (`SizedQueue#initialize`) -* [ ] `rb_szqueue_close` (`SizedQueue#close`) -* [ ] `rb_szqueue_max_get` (`SizedQueue#max`) -* [ ] `rb_szqueue_max_set` (`SizedQueue#max=`) -* [ ] `rb_szqueue_push` (`SizedQueue#push`) -* [ ] `rb_szqueue_pop` (`SizedQueue#pop`) -* [ ] `rb_szqueue_empty_p` (`SizedQueue#empty?`) -* [ ] `rb_szqueue_clear` (`SizedQueue#clear`) -* [ ] `rb_szqueue_length` (`SizedQueue#length`) -* [ ] `rb_szqueue_num_waiting` (`SizedQueue#num_waiting`) -* [ ] `rb_condvar_initialize` (`ConditionVariable#initialize`) -* [ ] `undumpable` (`ConditionVariable#marshal_dump`) -* [ ] `rb_condvar_wait` (`ConditionVariable#wait`) -* [ ] `rb_condvar_signal` (`ConditionVariable#signal`) -* [ ] `rb_condvar_broadcast` (`ConditionVariable#broadcast`) - -## `time.c` -* [ ] `time_to_i` (`Time#to_i`) -* [ ] `time_to_f` (`Time#to_f`) -* [ ] `time_to_r` (`Time#to_r`) -* [ ] `time_cmp` (`Time#<=>`) -* [ ] `time_eql` (`Time#eql?`) -* [ ] `time_hash` (`Time#hash`) -* [ ] `time_init` (`Time#initialize`) -* [ ] `time_init_copy` (`Time#initialize_copy`) -* [ ] `time_localtime_m` (`Time#localtime`) -* [ ] `time_gmtime` (`Time#gmtime`) -* [ ] `time_gmtime` (`Time#utc`) -* [ ] `time_getlocaltime` (`Time#getlocal`) -* [ ] `time_getgmtime` (`Time#getgm`) -* [ ] `time_getgmtime` (`Time#getutc`) -* [ ] `time_asctime` (`Time#ctime`) -* [ ] `time_asctime` (`Time#asctime`) -* [ ] `time_to_s` (`Time#to_s`) -* [ ] `time_inspect` (`Time#inspect`) -* [ ] `time_to_a` (`Time#to_a`) -* [ ] `time_plus` (`Time#+`) -* [ ] `time_minus` (`Time#-`) -* [ ] `time_succ` (`Time#succ`) -* [ ] `time_round` (`Time#round`) -* [ ] `time_floor` (`Time#floor`) -* [ ] `time_ceil` (`Time#ceil`) -* [ ] `time_sec` (`Time#sec`) -* [ ] `time_min` (`Time#min`) -* [ ] `time_hour` (`Time#hour`) -* [ ] `time_mday` (`Time#mday`) -* [ ] `time_mday` (`Time#day`) -* [ ] `time_mon` (`Time#mon`) -* [ ] `time_mon` (`Time#month`) -* [ ] `time_year` (`Time#year`) -* [ ] `time_wday` (`Time#wday`) -* [ ] `time_yday` (`Time#yday`) -* [ ] `time_isdst` (`Time#isdst`) -* [ ] `time_isdst` (`Time#dst?`) -* [ ] `time_zone` (`Time#zone`) -* [✔ ] `rb_time_utc_offset` (`Time#gmtoff`) -* [✔ ] `rb_time_utc_offset` (`Time#gmt_offset`) -* [✔ ] `rb_time_utc_offset` (`Time#utc_offset`) -* [ ] `time_utc_p` (`Time#utc?`) -* [ ] `time_utc_p` (`Time#gmt?`) -* [ ] `time_sunday` (`Time#sunday?`) -* [ ] `time_monday` (`Time#monday?`) -* [ ] `time_tuesday` (`Time#tuesday?`) -* [ ] `time_wednesday` (`Time#wednesday?`) -* [ ] `time_thursday` (`Time#thursday?`) -* [ ] `time_friday` (`Time#friday?`) -* [ ] `time_saturday` (`Time#saturday?`) -* [ ] `time_to_i` (`Time#tv_sec`) -* [ ] `time_usec` (`Time#tv_usec`) -* [ ] `time_usec` (`Time#usec`) -* [ ] `time_nsec` (`Time#tv_nsec`) -* [ ] `time_nsec` (`Time#nsec`) -* [ ] `time_subsec` (`Time#subsec`) -* [ ] `time_strftime` (`Time#strftime`) - -## `transcode.c` -* [ ] `str_encode` (`String#encode`) -* [ ] `str_encode_bang` (`String#encode!`) -* [ ] `econv_init` (`Converter#initialize`) -* [ ] `econv_inspect` (`Converter#inspect`) -* [ ] `econv_convpath` (`Converter#convpath`) -* [ ] `econv_source_encoding` (`Converter#source_encoding`) -* [ ] `econv_destination_encoding` (`Converter#destination_encoding`) -* [ ] `econv_primitive_convert` (`Converter#primitive_convert`) -* [ ] `econv_convert` (`Converter#convert`) -* [ ] `econv_finish` (`Converter#finish`) -* [ ] `econv_primitive_errinfo` (`Converter#primitive_errinfo`) -* [ ] `econv_insert_output` (`Converter#insert_output`) -* [ ] `econv_putback` (`Converter#putback`) -* [ ] `econv_last_error` (`Converter#last_error`) -* [ ] `econv_get_replacement` (`Converter#replacement`) -* [ ] `econv_set_replacement` (`Converter#replacement=`) -* [ ] `econv_equal` (`Converter#==`) -* [ ] `ecerr_source_encoding_name` (`UndefinedConversionError#source_encoding_name`) -* [ ] `ecerr_destination_encoding_name` (`UndefinedConversionError#destination_encoding_name`) -* [ ] `ecerr_source_encoding` (`UndefinedConversionError#source_encoding`) -* [ ] `ecerr_destination_encoding` (`UndefinedConversionError#destination_encoding`) -* [ ] `ecerr_error_char` (`UndefinedConversionError#error_char`) -* [ ] `ecerr_source_encoding_name` (`InvalidByteSequenceError#source_encoding_name`) -* [ ] `ecerr_destination_encoding_name` (`InvalidByteSequenceError#destination_encoding_name`) -* [ ] `ecerr_source_encoding` (`InvalidByteSequenceError#source_encoding`) -* [ ] `ecerr_destination_encoding` (`InvalidByteSequenceError#destination_encoding`) -* [ ] `ecerr_error_bytes` (`InvalidByteSequenceError#error_bytes`) -* [ ] `ecerr_readagain_bytes` (`InvalidByteSequenceError#readagain_bytes`) -* [ ] `ecerr_incomplete_input` (`InvalidByteSequenceError#incomplete_input?`) - -## `vm_backtrace.c` -* [ ] `location_lineno_m` (`Location#lineno`) -* [ ] `location_label_m` (`Location#label`) -* [ ] `location_base_label_m` (`Location#base_label`) -* [ ] `location_path_m` (`Location#path`) -* [ ] `location_absolute_path_m` (`Location#absolute_path`) -* [ ] `location_to_str_m` (`Location#to_s`) -* [ ] `location_inspect_m` (`Location#inspect`) - -## `vm_eval.c` -* [ ] `rb_obj_instance_eval_internal` (`BasicObject#instance_eval`) -* [ ] `rb_obj_instance_exec_internal` (`BasicObject#instance_exec`) -* [✔ ] `rb_f_send` (`BasicObject#__send__`) -* [✔ ] `rb_f_send` (`Kernel#send`) -* [ ] `rb_f_public_send` (`Kernel#public_send`) -* [ ] `rb_mod_module_exec_internal` (`Module#module_exec`) -* [ ] `rb_mod_module_exec_internal` (`Module#class_exec`) -* [ ] `rb_mod_module_eval_internal` (`Module#module_eval`) -* [ ] `rb_mod_module_eval_internal` (`Module#class_eval`) -* [ ] `uncaught_throw_init` (`UncaughtThrowError#initialize`) -* [ ] `uncaught_throw_tag` (`UncaughtThrowError#tag`) -* [ ] `uncaught_throw_value` (`UncaughtThrowError#value`) -* [ ] `uncaught_throw_to_s` (`UncaughtThrowError#to_s`) - -## `vm_method.c` -* [ ] `obj_respond_to` (`Kernel#respond_to?`) -* [ ] `obj_respond_to_missing` (`Kernel#respond_to_missing?`) -* [ ] `rb_mod_remove_method` (`Module#remove_method`) -* [ ] `rb_mod_undef_method` (`Module#undef_method`) -* [ ] `rb_mod_alias_method` (`Module#alias_method`) -* [ ] `rb_mod_method_defined` (`Module#method_defined?`) -* [ ] `rb_mod_public_method_defined` (`Module#public_method_defined?`) -* [ ] `rb_mod_private_method_defined` (`Module#private_method_defined?`) -* [ ] `rb_mod_protected_method_defined` (`Module#protected_method_defined?`) -* [ ] `rb_mod_public_method` (`Module#public_class_method`) -* [ ] `rb_mod_private_method` (`Module#private_class_method`) - -## `vm_trace.c` -* [ ] `thread_set_trace_func_m` (`Thread#set_trace_func`) -* [ ] `thread_add_trace_func_m` (`Thread#add_trace_func`) - -## Stats -* Total: 1488 -* Visible: 244 diff --git a/compiler/IREmitter/Intrinsics/make-sorbet-patch.rb b/compiler/IREmitter/Intrinsics/make-sorbet-patch.rb deleted file mode 100755 index ba18b78950..0000000000 --- a/compiler/IREmitter/Intrinsics/make-sorbet-patch.rb +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env ruby -# frozen_string_literal: true - -require 'optparse' - -module Intrinsics - class Main - def self.run(diff_path) - File.open('sorbet.patch', mode: 'w') do |diff| - - # Create @sorbet//third_party/ruby/export-intrinsics.patch - diff << "--- /dev/null\n" - diff << "+++ b/third_party/ruby/export-intrinsics.patch\n" - - lines = [] - File.open(diff_path, mode: 'r') do |io| - io.each_line do |line| - lines << '+' + line - end - end - - diff << "@@ -0,0 +1,#{lines.size} @@\n" - lines.each do |line| - diff << line - end - - # Patch @sorbet//third_party/externals.bzl - diff << "--- a/third_party/externals.bzl\n" - diff << "+++ b/third_party/externals.bzl\n" - diff << "@@ -274,2 +274,3 @@\n" - diff << " \"@com_stripe_ruby_typer//third_party/ruby:debug_counter.h.patch\",\n" - diff << "+ \"@com_stripe_ruby_typer//third_party/ruby:export-intrinsics.patch\",\n" - diff << " ],\n" - end - end - end -end - -if __FILE__ == $0 - diff_path = File.dirname($0) + '/export-intrinsics.patch' - - OptionParser.new do |opts| - opts.on '-dPATH', '--diff=PATH', 'The diff file to add to sorbet ruby' do |path| - diff_path = path - end - - opts.on '-h', '--help', 'Show this message' do - puts opts - exit - end - end.parse! - - Intrinsics::Main.run(diff_path) -end diff --git a/compiler/IREmitter/Intrinsics/wrap-intrinsics.rb b/compiler/IREmitter/Intrinsics/wrap-intrinsics.rb deleted file mode 100755 index fa7cd4a47e..0000000000 --- a/compiler/IREmitter/Intrinsics/wrap-intrinsics.rb +++ /dev/null @@ -1,763 +0,0 @@ -# coding: utf-8 -# frozen_string_literal: true -# typed: strict - -require 'set' -require 'optparse' -require 'stringio' -require_relative '../../../gems/sorbet-runtime/lib/sorbet-runtime' - -class Module - include T::Sig -end - -module Intrinsics - class ExportStatus < T::Enum - enums do - Exported = new - NotExported = new - PatchExported = new - end - end - - class Unlimited; end - class ArgRubyArray; end - class FixedArity < T::Struct - const :n, Integer - end - - class Method < T::Struct - prop :export_status, ExportStatus - prop :file, String - prop :klass, String - prop :rb_name, String - prop :c_name, String - prop :argc, T.any(FixedArity, ArgRubyArray, Unlimited) - - sig {params(argc: Integer).returns(T.any(FixedArity, ArgRubyArray, Unlimited))} - def self.arity_for_ruby_callconv(argc) - case argc - when -2 - return ArgRubyArray.new - when -1 - return Unlimited.new - else - return FixedArity.new(n: argc) - end - end - - sig {returns(T::Boolean)} - def already_exported - export_status == ExportStatus::Exported - end - - sig {returns(T::Boolean)} - def did_export - export_status == ExportStatus::PatchExported - end - - sig {void} - def mark_patch_exported - case export_status - when ExportStatus::NotExported - @export_status = ExportStatus::PatchExported - else - T.absurd(self) - end - end - - sig {returns(T::Boolean)} - def takes_ruby_array - argc.is_a?(ArgRubyArray) - end - - sig {returns(Integer)} - def ruby_callconv - case argc - when ArgRubyArray - -2 - when Unlimited - -1 - when FixedArity - argc.n - end - end - - sig {params(func_name: String).returns(String)} - def signature(func_name) - StringIO.open do |io| - io << "VALUE #{func_name}(" - - case argc - when Unlimited - io << 'int argc, const VALUE *args, VALUE obj' - when ArgRubyArray - $stderr.puts 'Need to implement calling convention -2' - exit 1 - when FixedArity - args = ['VALUE obj'] - - argc.n.times do |i| - args << "VALUE arg_#{i}" - end - - io << args.join(', ') - end - - io << ')' - - io.string - end - end - - sig {returns(String)} - def function_signature - signature(c_name) - end - - sig {returns(String)} - def exported_wrapper_signature - signature(exported_c_name) - end - - sig {returns(String)} - def exported_c_name - case export_status - when ExportStatus::Exported - c_name - when ExportStatus::PatchExported - "sorbet_#{c_name}" - else - T.absurd(self.c_name) - end - end - - sig {returns(String)} - def sorbet_payload_wrapper_name - return "sorbet_int_#{c_name}" - end - - sig {returns(String)} - def sorbet_payload_wrapper - # NOTE: the sorbet calling convention differs from -1, in that we put the - # receiver first, and pass the ID of the function explicitly. - StringIO.open do |io| - io << "VALUE #{sorbet_payload_wrapper_name}(" - io << 'VALUE recv, ' - io << 'ID fun, ' - io << 'int argc, ' - io << "VALUE *const restrict args, BlockFFIType blk, VALUE closure) {\n" - - case argc - - when Unlimited - # dispatch directly to the underlying intrinsic - io << " return #{exported_c_name}(argc, args, recv);\n" - - when ArgRubyArray - puts "Need to implement calling convention -2" - exit 1 - - when FixedArity - # check the arity - io << " rb_check_arity(argc, #{argc.n}, #{argc.n});\n" - - # extract individual arguments - args = ["recv"] - argc.n.times do |i| - args << "arg_#{i}" - io << " VALUE arg_#{i} = args[#{i}];\n" - end - - # call the underlying intrinsic - io << " return #{exported_c_name}(#{args.join(', ')});\n" - end - - io << "}\n" - - io.string - end - end - end - - class Main - - SORBET_SYMBOL_REFS = T.let(Set[ - 'Array', - 'BasicObject', - 'Class', - 'Complex', - 'Enumerable', - 'Enumerator', - 'FalseClass', - 'File', - 'Float', - 'Hash', - 'Integer', - 'Kernel', - 'Module', - 'NilClass', - 'Object', - 'Proc', - 'Range', - 'Rational', - 'Regexp', - 'Set', - 'Singleton', - 'StandardError', - 'String', - 'Struct', - 'Symbol', - 'TrueClass', - ], T::Set[String]) - - EXTERN_OVERRIDES = T.let({ - 'rb_int_powm' => 'extern VALUE rb_int_powm(int const argc, VALUE * const argv, VALUE const num);', - 'rb_f_send' => 'extern VALUE rb_f_send(int argc, VALUE *argv, VALUE recv);', - }, T::Hash[String, String]) - - METHOD_WHITELIST = T.let(Set[ - "Array#+", - "Array#-", - "Array#<<", - "Array#<=>", - "Array#[]", - "Array#assoc", - "Array#at", - "Array#clear", - "Array#concat", - "Array#delete", - "Array#first", - "Array#flatten", - "Array#include?", - "Array#initialize_copy", - "Array#join", - "Array#last", - "Array#push", - "Array#rassoc", - "Array#replace", - "Array#slice", - "Array#sort!", - "Array#sort", - "Float#*", - "Float#**", - "Float#+", - "Float#-@", - "Float#>", - "Float#>=", - "Float#<", - "Float#<=", - "Float#abs", - "Float#finite?", - "Float#infinite?", - "Float#magnitude", - "Hash#dig", - "Hash#has_key?", - "Hash#include?", - "Hash#key?", - "Hash#member?", - "Integer#%", - "Integer#&", - "Integer#*", - "Integer#**", - "Integer#+", - "Integer#-", - "Integer#-@", - "Integer#/", - "Integer#<<", - "Integer#<=>", - "Integer#==", - "Integer#===", - "Integer#>", - "Integer#>=", - "Integer#abs", - "Integer#div", - "Integer#divmod", - "Integer#fdiv", - "Integer#gcd", - "Integer#gcdlcm", - "Integer#lcm", - "Integer#magnitude", - "Integer#modulo", - "Integer#odd?", - "Integer#pow", - "Integer#to_f", - "Regexp#encoding", - "String#*", - "String#+", - "String#<<", - "String#==", - "String#===", - "String#[]", - "String#dump", - "String#encoding", - "String#eql?", - "String#freeze", - "String#initialize_copy", - "String#inspect", - "String#intern", - "String#length", - "String#next", - "String#ord", - "String#replace", - "String#size", - "String#slice", - "String#start_with?", - "String#succ", - "String#to_sym", - ], T::Set[String]) - - sig {params(topdir: String, ruby: String, ruby_source: String).void} - def self.run(topdir:, ruby:, ruby_source:) - puts "ruby object: #{ruby}" - puts "ruby source: #{ruby_source}" - - exported = exported_symbols(ruby: ruby) - methods = methods_defined(ruby_source: ruby_source, exported: exported) - - expose_methods(topdir, ruby_source, methods) - - File.open('intrinsic-report.md', 'w') do |report| - puts 'Writing intrinsic-report.md' - write_report(report, methods) - end - - # group methods together - grouped_methods = methods.values.flatten.group_by(&:c_name).values - - # if there are multiple methods with the same underlying C implementation, - # then they should all be specified as intrinsics. - grouped_methods.each do |methods| - next unless methods.any? {|method| method_whitelisted?(method)} - unless methods.all? {|method| method_whitelisted?(method)} - ruby_methods = methods.map {|method| "#{method.klass}##{method.rb_name}"} - raise "Must specify all of #{ruby_methods} as intrinsics" - end - end - - # ensure consistent output order - grouped_methods.sort_by! do |methods| - method = methods.fetch(0) - "#{method.klass}##{method.rb_name}" - end - - File.open('WrappedIntrinsics.h', 'w') do |header| - puts 'Writing WrappedIntrinsics.h' - write_header(header, grouped_methods) - end - - File.open('PayloadIntrinsics.c', 'w') do |wrapper| - puts 'Writing PayloadIntrinsics.c' - write_wrapper(wrapper, grouped_methods) - end - end - - # Collect a set of exported symbols from the ruby binary or shared object, using 'nm' - sig {params(ruby: String).returns(T::Set[String])} - def self.exported_symbols(ruby:) - exported = Set.new - - if !File.exist?(ruby) - puts "Ruby binary or shared object is missing: #{ruby}" - exit 1 - end - - IO.popen([ 'nm', ruby ]) do |io| - io.each_line do |line| - line = T.let(line, String) - if line =~ / T / - - # the line output format for nm is ' ' - symbol = line.split(' ')[2] - - # There ends up being leading underscores in front of all the - # symbols we care about on macOS - exported << T.must(symbol).delete_prefix('_') - end - end - end - - exported - end - - # Collect methods defined by file - sig {params(ruby_source: String, exported: T::Set[String]).returns(T::Hash[String, T::Array[Method]])} - def self.methods_defined(ruby_source:, exported:) - defined = T.let({}, T::Hash[String, T::Array[Method]]) - - # change to the source dir to avoid absolute paths in the output - Dir.chdir(ruby_source) do - files = T.let([], T::Array[String]) - IO.popen( - [ - 'find', '.', - '-name', '*.c', - '-not', '-path', '*/spec/*', - '-not', '-path', '*/ext/*', - '-not', '-path', '*/gems/*', - ]) do |io| - io.each_line do |file| - file = T.let(file, String) - # trim the trailing newline, and the leading './' - files << T.must(file.chomp[2..]) - end - end - - files.sort! - - files.each do |file| - methods = methods_from(exported: exported, file: file) - - if !methods.empty? - defined[file] = methods - end - end - end - - defined - end - - RB_DEFINE_CLASS = /([^\s]+)\s+=\s*rb_define_class\("([^"]+)/ - RB_DEFINE_MODULE = /([^\s]+)\s+=\s*rb_define_module\("([^"]+)/ - RB_DEFINE_CLASS_UNDER = /([^\s]+)\s+=\s*rb_define_class_under\([^,]+,\s*"([^"]+)/ - RB_DEFINE_MODULE_UNDER = /([^\s]+)\s+=\s*rb_define_module_under\([^,]+,\s*"([^"]+)/ - - RB_DEFINE_METHOD = /rb_define_method\(([^,]+),\s*\"([^,]+)\",([^,]+),(.*)/ - - # Collect methods defined in a single file. - sig {params(exported: T::Set[String], file: String).returns(T::Array[Method])} - def self.methods_from(exported:, file:) - methods = T.let([], T::Array[Method]) - - File.open(file, 'r') do |io| - - # Seed the class list with cases that are defined outside of the source - # file that defines its methods. - klasses = T.let({ - "rb_cArray" => "Array", - "rb_cBasicObject" => "BasicObject", - "rb_cFile" => "File", - "rb_cFloat" => "Float", - "rb_cIO" => "IO", - "rb_cInteger" => "Integer", - "rb_cModule" => "Module", - "rb_cNilClass" => "NilClass", - "rb_cNumeric" => "Numeric", - "rb_cRange" => "Range", - "rb_cString" => "String", - "rb_cThread" => "Thread", - "rb_eInterrupt" => "Interrupt", - "rb_eSignal" => "SignalException", - "rb_mEnumerable" => "Enumerable", - "rb_mKernel" => "Kernel", - }, T::Hash[String, String]) - - io.each_line do |line| - - # This is a heuristic, but Init functions usually define a class as a - # symbol, and thread that through the method definitions. - klass_match = - RB_DEFINE_CLASS.match(line) || - RB_DEFINE_MODULE.match(line) || - RB_DEFINE_CLASS_UNDER.match(line) || - RB_DEFINE_MODULE_UNDER.match(line) - - if klass_match - match_1 = T.must(klass_match[1]) - match_2 = T.must(klass_match[2]) - klasses[match_1.chomp] = match_2 - end - - if m = RB_DEFINE_METHOD.match(line) - m1, m2, m3, m4 = T.must(m[1]), T.must(m[2]), T.must(m[3]), T.must(m[4]) - klass = m1.chomp - - # It would be nice to log a message here if the class can't be - # resolved, but in most cases they're classes we're not interested - # in. - if klass_value = klasses[klass] - rb_name = m2 - c_name = m3.lstrip.rstrip - argc = m4.chomp.to_i - - status = if exported.include?(c_name) - ExportStatus::Exported - else - ExportStatus::NotExported - end - methods << Method.new( - export_status: status, - file: file, - klass: klass_value, - rb_name: rb_name, - c_name: c_name, - argc: Method.arity_for_ruby_callconv(argc) - ) - end - end - end - end - - methods - end - - sig {params(report: File, files: T::Hash[String, T::Array[Method]]).void} - def self.write_report(report, files) - total_intrinsics = 0 - visible_intrinsics = 0 - - report << "# Intrinsics by file\n" - report << "\n" - - files.each do |file,methods| - report << "## `#{file}`\n" - - methods.each do |method| - - total_intrinsics += 1 - if method.already_exported || method.did_export - visible_intrinsics += 1 - end - - already_exported = method.already_exported ? "✔" : " " - did_export = method.did_export ? "✔" : " " - - report << "* [#{already_exported} #{did_export}] `#{method.c_name}` (`#{method.klass}##{method.rb_name}`)\n" - end - - report << "\n" - end - - report << "## Stats\n" - report << "* Total: #{total_intrinsics}\n" - report << "* Visible: #{visible_intrinsics}\n" - end - - sig do - params( - topdir: String, - ruby_source: String, - methods: T::Hash[String, T::Array[Method]] - ) - .void - end - def self.expose_methods(topdir, ruby_source, methods) - methods.each do |file,methods| - ruby_file = File.join(ruby_source, file) - hidden = methods.filter {|m| should_patch?(m)} - if !hidden.empty? - wrappers = File.open(ruby_file, 'r') do |io| - nonstatic_wrappers_for_methods(topdir, hidden, io) - end - - if !wrappers.empty? - static_export_file = "#{topdir}/compiler/ruby-static-exports/#{file}" - puts "Writing #{static_export_file}\n" - File.open(static_export_file, 'w') do |io| - io << wrappers.join("\n") - end - end - end - end - end - - sig {params(topdir: String, hidden: T::Array[Method], io: File).returns(T::Array[String])} - def self.nonstatic_wrappers_for_methods(topdir, hidden, io) - to_export = T.let([], T::Array[Method]) - - lines = io.each_line.to_a - # Assume that we're never going to see a definition on the very first line. - lines.each_cons(2).each_with_index do |window, idx| - previous = window[0] - line = window[1] - # idx is 0-based, but the first line is actually line two. - idx = idx + 2 - mi = hidden.find_index do |method| - if !line.match?(Regexp.new("#{method.c_name}\\(")) - next false - end - - if previous.match?('^static VALUE$') - next true - end - - next line.match?('static VALUE') - end - - if !mi.nil? - method = hidden[mi] - to_export << method - method.mark_patch_exported - end - end - - wrappers = T.let([], T::Array[String]) - - to_export.each do |method| - wrappers << StringIO.open do |strio| - strio << method.exported_wrapper_signature - strio << " {\n" - case method.argc - when ::Intrinsics::Unlimited - strio << " return #{method.c_name}(argc, args, obj);\n" - strio << "}\n" - - when ::Intrinsics::ArgRubyArray - puts "Need to implement calling convention -2" - exit 1 - - when ::Intrinsics::FixedArity - args = ['obj'] - - method.argc.n.times do |i| - args << "arg_#{i}" - end - - strio << " return #{method.c_name}(#{args.join(', ')});\n" - strio << "}\n" - end - - strio.string - end - end - - wrappers - end - - # Make this method return `true` to emit all methods - sig {params(method: Method).returns(T::Boolean)} - def self.method_whitelisted?(method) - METHOD_WHITELIST.include?("#{method.klass}##{method.rb_name}") - end - - sig {params(klass: String).returns(T::Boolean)} - def self.have_symbol_ref?(klass) - SORBET_SYMBOL_REFS.include?(klass) - end - - sig {params(method: Method).returns(T::Boolean)} - def self.should_patch?(method) - !method.already_exported && \ - have_symbol_ref?(method.klass) && \ - method_whitelisted?(method) && \ - !method.takes_ruby_array - end - - sig {params(method: Method).returns(T.nilable(T::Boolean))} - def self.should_wrap?(method) - (method.already_exported || method.did_export) && \ - have_symbol_ref?(method.klass) && \ - method_whitelisted?(method) && \ - !method.takes_ruby_array - end - - sig {params(header: File, grouped_methods: T::Array[T::Array[Method]]).void} - def self.write_header(header, grouped_methods) - header << "// This file is autogenerated. Do not edit it by hand. Regenerate it with:\n" - header << "// cd compiler/IREmitter/Intrinsics && make\n" - header << "\n" - header << "// clang-format off\n" - - grouped_methods.each do |methods| - method = methods.fetch(0) - if should_wrap?(method) - methods.each do |method| - header << " {core::Symbols::#{method.klass}(), " - header << "\"#{method.rb_name}\", " - header << "CMethod{\"#{method.sorbet_payload_wrapper_name}\"}},\n" - end - end - end - header << " // clang-format on\n" - end - - sig {params(wrapper: File, grouped_methods: T::Array[T::Array[Method]]).void} - def self.write_wrapper(wrapper, grouped_methods) - - wrapper << <<~EOF - #ifndef SORBET_COMPILER_IMPORTED_INTRINSICS_H - #define SORBET_COMPILER_IMPORTED_INTRINSICS_H - - // This file is autogenerated. Do not edit it by hand. Regenerate it with: - // cd compiler/IREmitter/Intrinsics && make - - #include "ruby.h" - - typedef VALUE (*BlockFFIType)(VALUE firstYieldedArg, VALUE closure, int argCount, const VALUE *args, VALUE blockArg); - - EOF - - grouped_methods.each do |methods| - method = methods.fetch(0) - if should_wrap?(method) - - # TODO: comment about the ruby method - wrapper << "\n" - forward_declare_intrinsic(wrapper, method, methods) - wrapper << "\n" - wrapper_implementation(wrapper, method) - end - end - - wrapper << "#endif /* SORBET_COMPILER_IMPORTED_INTRINSICS_H */\n" - end - - sig {params(wrapper: File, method: Method, methods: T::Array[Method]).void} - def self.forward_declare_intrinsic(wrapper, method, methods) - - methods.each do |m| - wrapper << "// #{m.klass}##{m.rb_name}\n" - end - wrapper << "// Calling convention: #{method.ruby_callconv}\n" - - extern_override = EXTERN_OVERRIDES[method.c_name] - if extern_override - wrapper << extern_override - wrapper << "\n" - return - end - - wrapper << "extern #{method.exported_wrapper_signature};\n" - end - - sig {params(wrapper: File, method: Method).void} - def self.wrapper_implementation(wrapper, method) - wrapper << method.sorbet_payload_wrapper - end - - end -end - -if __FILE__ == $0 - - topdir = File.dirname($0) + '/../../..' - - if /darwin/ =~ RUBY_PLATFORM - ruby = topdir + '/bazel-bin/external/sorbet_ruby_2_7_unpatched/toolchain/lib/libruby.2.7.dylib' - else - ruby = topdir + '/bazel-bin/external/sorbet_ruby_2_7_unpatched/toolchain/lib/libruby.so.2.7' - end - - ruby_source = topdir + '/bazel-sorbet/external/sorbet_ruby_2_7_unpatched' - - OptionParser.new do |opts| - - opts.banner = "Usage: wrap-intrinsics.rb [options]" - - opts.on '-rPATH', '--ruby=PATH', 'Path to the ruby executable or shared object to analyze' do |path| - ruby = File.realpath(path) - end - - opts.on '-sPATH', '--ruby-source=PATH', 'Path to the ruby source to analyze' do |path| - ruby_source = File.realpath(path) - end - - opts.on '-h', '--help', 'Display this message' do - puts opts - exit - end - - end.parse! - - Intrinsics::Main.run(topdir: topdir, ruby: ruby, ruby_source: ruby_source) -end diff --git a/compiler/IREmitter/MethodCallContext.cc b/compiler/IREmitter/MethodCallContext.cc deleted file mode 100644 index 9afa237e66..0000000000 --- a/compiler/IREmitter/MethodCallContext.cc +++ /dev/null @@ -1,110 +0,0 @@ -// These violate our poisons so have to happen first -#include "llvm/IR/IRBuilder.h" - -#include "cfg/CFG.h" - -#include "compiler/Core/CompilerState.h" -#include "compiler/IREmitter/IREmitterContext.h" -#include "compiler/IREmitter/IREmitterHelpers.h" -#include "compiler/IREmitter/MethodCallContext.h" -#include "compiler/IREmitter/Payload.h" - -namespace sorbet::compiler { - -MethodCallContext MethodCallContext::create(CompilerState &cs, llvm::IRBuilderBase &builder, - const IREmitterContext &irctx, int rubyRegionId, cfg::Send *send, - std::optional blk) { - MethodCallContext ret{cs, builder, irctx, rubyRegionId, send, blk}; - - auto *func = builder.GetInsertBlock()->getParent(); - ret.sendEntry = llvm::BasicBlock::Create(cs, "sendEntry", func); - ret.sendContinuation = llvm::BasicBlock::Create(cs, "sendContinuation", func); - - builder.CreateBr(ret.sendEntry); - builder.SetInsertPoint(ret.sendContinuation); - - return ret; -} - -void MethodCallContext::finalize() { - ENFORCE(!this->isFinalized); - - auto saved = builder.saveIP(); - this->builder.SetInsertPoint(this->sendEntry); - this->builder.CreateBr(this->sendContinuation); - this->builder.restoreIP(saved); - - this->isFinalized = true; -} - -void MethodCallContext::initArgsAndCache() { - ENFORCE(!this->isFinalized); - ENFORCE(!this->rubyStackArgs.has_value()); - ENFORCE(this->inlineCache == nullptr); - - auto saved = this->builder.saveIP(); - - this->builder.SetInsertPoint(this->sendEntry); - - this->rubyStackArgs.emplace(IREmitterHelpers::buildSendArgs(*this, this->send->recv.variable, 0)); - - auto methodName = std::string(this->send->fun.shortName(this->cs)); - this->inlineCache = - IREmitterHelpers::makeInlineCache(this->cs, this->builder, methodName, this->rubyStackArgs->flags, - this->rubyStackArgs->stack.size(), this->rubyStackArgs->keywords); - - builder.restoreIP(saved); -} - -llvm::Value *MethodCallContext::varGetRecv() { - if (this->recv == nullptr) { - ENFORCE(!this->isFinalized); - - auto saved = builder.saveIP(); - this->builder.SetInsertPoint(this->sendEntry); - this->recv = - Payload::varGet(this->cs, this->send->recv.variable, this->builder, this->irctx, this->rubyRegionId); - this->builder.restoreIP(saved); - } - - return this->recv; -} - -llvm::Function *MethodCallContext::blkAsFunction() const { - if (!blk.has_value()) { - return nullptr; - } - - return irctx.rubyBlocks2Functions[*blk]; -} - -llvm::Value *MethodCallContext::getInlineCache() { - if (this->inlineCache == nullptr) { - this->initArgsAndCache(); - } - - return this->inlineCache; -} - -void MethodCallContext::emitMethodSearch() { - if (this->methodSearchPerformed) { - return; - } - - ENFORCE(!this->isFinalized); - - auto *cache = this->getInlineCache(); - - this->builder.CreateCall(cs.getFunction("sorbet_vmMethodSearch"), {cache, recv}); - this->methodSearchPerformed = true; -} - -const RubyStackArgs &MethodCallContext::getStackArgs() { - if (this->inlineCache == nullptr) { - this->initArgsAndCache(); - } - - return this->rubyStackArgs.value(); -} - -} // namespace sorbet::compiler diff --git a/compiler/IREmitter/MethodCallContext.h b/compiler/IREmitter/MethodCallContext.h deleted file mode 100644 index d3a583ac2f..0000000000 --- a/compiler/IREmitter/MethodCallContext.h +++ /dev/null @@ -1,89 +0,0 @@ -#ifndef SORBET_COMPILER_IREMITTER_METHODCALLCONTEXT_H -#define SORBET_COMPILER_IREMITTER_METHODCALLCONTEXT_H - -#include "llvm/IR/IRBuilder.h" - -#include "compiler/Core/ForwardDeclarations.h" -#include "compiler/IREmitter/RubyStackArgs.h" - -#include -#include - -namespace sorbet::compiler { - -class CompilerState; -struct IREmitterContext; - -class MethodCallContext { - llvm::BasicBlock *sendEntry = nullptr; - llvm::BasicBlock *sendContinuation = nullptr; - - // The LLVM register holding the evaluated receiver. - // Use `mcctx.varGetRecv(...)` to get or initialize this. - llvm::Value *recv = nullptr; - - llvm::Value *inlineCache = nullptr; - std::optional rubyStackArgs; - - bool isFinalized = false; - - bool methodSearchPerformed = false; - - void initArgsAndCache(); - - MethodCallContext(CompilerState &cs, llvm::IRBuilderBase &builder, const IREmitterContext &irctx, int rubyRegionId, - cfg::Send *send, std::optional blk) - : cs(cs), builder(builder), irctx(irctx), rubyRegionId(rubyRegionId), send(send), blk(blk){}; - -public: - CompilerState &cs; - - llvm::IRBuilderBase &builder; - - const IREmitterContext &irctx; - - // See IREmitterContext for a description of rubyRegionId. Primarily used to index into the - // various vectors in `irctx`. - int rubyRegionId; - - // The method call being emitted right now. - cfg::Send *send; - - // The block id associated with this send via core::SendAndBlockLink. - // `std::nullopt` if no `do ... end` block is provided at the call site. - std::optional blk; - - // Get the receiver for the send being emitted right now. - // Use this to avoid accidentally calling Payload::varGet multiple times per one send, - // duplicating work. - // - // WARNING: this method must be called on a path that will reach all remaning cases for method dispatch, or the - // receiver will be initialized conditionally. This will cause the generated llvm will fail verification. - llvm::Value *varGetRecv(); - - // Get the inline cache to be used by this call. - llvm::Value *getInlineCache(); - - // Emit a call to `sorbet_vmMethodSearch` when the cache is created. - // - // WARNING: this method must be called on a path that will reach all remaning cases for method dispatch, or the - // generated code will potentially query the inline cache without initializing it. - void emitMethodSearch(); - - // Get the args that would be pushed to the ruby stack along with other information - // pertinent to the call (the receiver will be the first element of .stack). - const RubyStackArgs &getStackArgs(); - - // Return the function associated with the block, nullptr if blk is std::nullopt. - llvm::Function *blkAsFunction() const; - - static MethodCallContext create(CompilerState &cs, llvm::IRBuilderBase &build, const IREmitterContext &irctx, - int rubyRegionId, cfg::Send *send, std::optional blk); - - // connect the send entry and the continuation - void finalize(); -}; - -} // namespace sorbet::compiler - -#endif diff --git a/compiler/IREmitter/NameBasedIntrinsics.cc b/compiler/IREmitter/NameBasedIntrinsics.cc deleted file mode 100644 index d554a6e9a5..0000000000 --- a/compiler/IREmitter/NameBasedIntrinsics.cc +++ /dev/null @@ -1,943 +0,0 @@ -// These violate our poisons so have to happen first -#include "llvm/IR/Attributes.h" -#include "llvm/IR/DerivedTypes.h" // FunctionType, StructType -#include "llvm/IR/IRBuilder.h" -#include "llvm/IR/Verifier.h" - -#include "absl/base/casts.h" -#include "ast/Helpers.h" -#include "ast/ast.h" -#include "cfg/CFG.h" -#include "common/FileOps.h" -#include "common/sort/sort.h" -#include "compiler/Core/CompilerState.h" -#include "compiler/Core/FailCompilation.h" -#include "compiler/Errors/Errors.h" -#include "compiler/IREmitter/IREmitter.h" -#include "compiler/IREmitter/IREmitterContext.h" -#include "compiler/IREmitter/IREmitterHelpers.h" -#include "compiler/IREmitter/MethodCallContext.h" -#include "compiler/IREmitter/NameBasedIntrinsics.h" -#include "compiler/IREmitter/Payload.h" -#include "core/Names.h" -#include -using namespace std; -namespace sorbet::compiler { -namespace { -core::ClassOrModuleRef typeToSym(const core::GlobalState &gs, core::TypePtr typ) { - core::ClassOrModuleRef sym; - if (core::isa_type(typ)) { - sym = core::cast_type_nonnull(typ).symbol; - } else if (auto appliedType = core::cast_type(typ)) { - sym = appliedType->klass; - } else { - ENFORCE(false); - } - sym = IREmitterHelpers::fixupOwningSymbol(gs, sym).asClassOrModuleRef(); - return sym; -} - -class DoNothingIntrinsic : public NameBasedIntrinsicMethod { -public: - DoNothingIntrinsic() : NameBasedIntrinsicMethod(Intrinsics::HandleBlock::Handled){}; - virtual llvm::Value *makeCall(MethodCallContext &mcctx) const override { - return Payload::rubyNil(mcctx.cs, mcctx.builder); - } - virtual InlinedVector applicableMethods(CompilerState &cs) const override { - return {core::Names::keepForCfg(), core::Names::nilForSafeNavigation()}; - } -} DoNothingIntrinsic; - -class ShouldNeverSeeIntrinsic : public NameBasedIntrinsicMethod { -public: - ShouldNeverSeeIntrinsic() : NameBasedIntrinsicMethod(Intrinsics::HandleBlock::Handled){}; - virtual llvm::Value *makeCall(MethodCallContext &mcctx) const override { - failCompilation(mcctx.cs, core::Loc(core::FileRef(), mcctx.send->receiverLoc), - "Emitting intrinsic that should have been deleted!"); - } - virtual InlinedVector applicableMethods(CompilerState &cs) const override { - return {core::Names::keepForIde()}; - } -} ShouldNeverSeeIntrinsic; - -class DefineClassIntrinsic : public NameBasedIntrinsicMethod { -public: - DefineClassIntrinsic() : NameBasedIntrinsicMethod(Intrinsics::HandleBlock::Unhandled){}; - virtual llvm::Value *makeCall(MethodCallContext &mcctx) const override { - auto &cs = mcctx.cs; - auto &builder = mcctx.builder; - auto *send = mcctx.send; - auto sym = typeToSym(cs, send->args[0].type); - auto attachedClass = sym.data(cs)->attachedClass(cs); - - if (attachedClass.data(cs)->name.isTEnumName(cs)) { - // T::Enum classes like `class X$1 < MyEnum; end` are fake and for the type system only - // (We don't define them at runtime, because classes are expensive compared to how many - // individual enum values there are.) - return Payload::rubyNil(cs, builder); - } - - llvm::Value *module = nullptr; - - auto funcSym = cs.gs.lookupStaticInitForClass(attachedClass); - if (!attachedClass.data(cs)->isSingletonClass(cs)) { - auto classNameCStr = Payload::toCString(cs, IREmitterHelpers::showClassNameWithoutOwner(cs, sym), builder); - auto isModule = sym.data(cs)->superClass() == core::Symbols::Module(); - - if (!IREmitterHelpers::isRootishSymbol(cs, sym.data(cs)->owner)) { - auto getOwner = Payload::getRubyConstant(cs, sym.data(cs)->owner, builder); - if (isModule) { - module = builder.CreateCall(cs.getFunction("sorbet_defineNestedModule"), {getOwner, classNameCStr}); - } else { - auto rawCall = Payload::getRubyConstant(cs, sym.data(cs)->superClass(), builder); - module = builder.CreateCall(cs.getFunction("sorbet_defineNestedClass"), - {getOwner, classNameCStr, rawCall}); - } - } else { - if (isModule) { - module = builder.CreateCall(cs.getFunction("sorbet_defineTopLevelModule"), {classNameCStr}); - } else { - auto rawCall = Payload::getRubyConstant(cs, sym.data(cs)->superClass(), builder); - module = - builder.CreateCall(cs.getFunction("sorbet_defineTopClassOrModule"), {classNameCStr, rawCall}); - } - } - } else { - module = Payload::getRubyConstant(cs, attachedClass, builder); - module = builder.CreateCall(cs.getFunction("sorbet_singleton_class"), {module}, "singletonClass"); - } - builder.CreateCall(cs.getFunction("sorbet_callStaticInitDirect"), - {IREmitterHelpers::getOrCreateStaticInit(cs, funcSym, send->receiverLoc), - llvm::ConstantInt::get(cs, llvm::APInt(32, 0, true)), - llvm::ConstantPointerNull::get(llvm::Type::getInt64PtrTy(cs)), module}); - return Payload::rubyNil(cs, builder); - } - virtual InlinedVector applicableMethods(CompilerState &cs) const override { - return {core::Names::defineTopClassOrModule()}; - } -} DefineClassIntrinsic; - -class IdentityIntrinsic : public NameBasedIntrinsicMethod { -public: - IdentityIntrinsic() : NameBasedIntrinsicMethod(Intrinsics::HandleBlock::Unhandled){}; - virtual llvm::Value *makeCall(MethodCallContext &mcctx) const override { - return Payload::varGet(mcctx.cs, mcctx.send->args[0].variable, mcctx.builder, mcctx.irctx, mcctx.rubyRegionId); - } - virtual InlinedVector applicableMethods(CompilerState &cs) const override { - return {core::Names::suggestConstantType(), core::Names::suggestFieldType()}; - } -} IdentityIntrinsic; - -llvm::Value *prepareBlockHandler(MethodCallContext &mcctx, cfg::VariableUseSite &blkVar) { - auto &cs = mcctx.cs; - auto &irctx = mcctx.irctx; - auto rubyRegionId = mcctx.rubyRegionId; - - if (IREmitterHelpers::canPassThroughBlockViaRubyVM(mcctx, blkVar.variable)) { - return Payload::getPassedBlockHandler(cs, mcctx.builder); - } else { - // TODO(perf) `makeBlockHandlerProc` uses `to_proc` under the hood, and could be rewritten here to make an - // inline cache. - auto *block = Payload::varGet(cs, blkVar.variable, mcctx.builder, irctx, rubyRegionId); - return Payload::makeBlockHandlerProc(cs, mcctx.builder, block); - } -} - -class CallWithBlock : public NameBasedIntrinsicMethod { -public: - CallWithBlock() : NameBasedIntrinsicMethod(Intrinsics::HandleBlock::Unhandled){}; - - virtual llvm::Value *makeCall(MethodCallContext &mcctx) const override { - // args[0] is the receiver - // args[1] is the method - // args[2] is the block - // args[3...] are the remaining arguements - // equivalent to (args[0]).args[1](*args[3..], &args[2]) - - auto &cs = mcctx.cs; - auto &irctx = mcctx.irctx; - auto rubyRegionId = mcctx.rubyRegionId; - auto *send = mcctx.send; - auto &builder = mcctx.builder; - - // TODO: this implementation generates code that is stupidly slow, we should be able to reuse instrinsics here - // one day - auto recv = send->args[0].variable; - auto lit = core::cast_type_nonnull(send->args[1].type); - ENFORCE(lit.literalKind == core::NamedLiteralType::LiteralTypeKind::Symbol); - auto methodName = lit.asName(); - - llvm::Value *blockHandler = prepareBlockHandler(mcctx, send->args[2]); - - auto shortName = methodName.shortName(cs); - - auto [stack, keywords, flags] = IREmitterHelpers::buildSendArgs(mcctx, recv, 3); - flags.blockarg = true; - auto *cfp = Payload::getCFPForBlock(cs, builder, irctx, rubyRegionId); - Payload::pushRubyStackVector(cs, builder, cfp, Payload::varGet(cs, recv, builder, irctx, rubyRegionId), stack); - auto *cache = IREmitterHelpers::makeInlineCache(cs, builder, string(shortName), flags, stack.size(), keywords); - if (methodName == core::Names::super()) { - return Payload::callSuperFuncWithCache(mcctx.cs, mcctx.builder, cache, blockHandler); - } - return Payload::callFuncWithCache(mcctx.cs, mcctx.builder, cache, blockHandler); - } - - virtual InlinedVector applicableMethods(CompilerState &cs) const override { - return {core::Names::callWithBlock()}; - } -} CallWithBlock; - -class ExceptionRetry : public NameBasedIntrinsicMethod { -public: - ExceptionRetry() : NameBasedIntrinsicMethod(Intrinsics::HandleBlock::Unhandled){}; - - virtual llvm::Value *makeCall(MethodCallContext &mcctx) const override { - auto &cs = mcctx.cs; - auto &builder = mcctx.builder; - auto &irctx = mcctx.irctx; - auto rubyRegionId = mcctx.rubyRegionId; - - auto *retrySingleton = Payload::retrySingleton(cs, builder, irctx); - IREmitterHelpers::emitReturn(cs, builder, irctx, rubyRegionId, retrySingleton); - - auto *dead = llvm::BasicBlock::Create(cs, "dead-retry", irctx.rubyBlocks2Functions[rubyRegionId]); - builder.SetInsertPoint(dead); - - return retrySingleton; - } - - virtual InlinedVector applicableMethods(CompilerState &cs) const override { - return {core::Names::retry()}; - } -} ExceptionRetry; - -enum ShouldTakeReceiver { - TakesReceiver, - NoReceiver, -}; - -llvm::Value *buildCMethodCall(MethodCallContext &mcctx, const string &cMethod, ShouldTakeReceiver takesReceiver, - core::ClassOrModuleRef klass) { - auto &cs = mcctx.cs; - auto &builder = mcctx.builder; - - auto args = IREmitterHelpers::fillSendArgArray(mcctx); - - llvm::Value *recv; - if (takesReceiver == TakesReceiver) { - recv = mcctx.varGetRecv(); - } else { - recv = Payload::rubyNil(cs, builder); - } - - llvm::Value *blkPtr; - if (auto *blk = mcctx.blkAsFunction()) { - blkPtr = blk; - } else { - blkPtr = llvm::ConstantPointerNull::get(cs.getRubyBlockFFIType()->getPointerTo()); - } - - llvm::Value *offset = Payload::buildLocalsOffset(cs); - - auto fun = Payload::idIntern(cs, builder, mcctx.send->fun.shortName(cs)); - auto *value = - builder.CreateCall(cs.getFunction(cMethod), {recv, fun, args.argc, args.argv, blkPtr, offset}, "rawSendResult"); - if (klass.exists()) { - Payload::assumeType(cs, builder, value, klass); - } - return value; -} - -class CallCMethod : public NameBasedIntrinsicMethod { -protected: - core::NameRef rubyMethod; - string cMethod; - ShouldTakeReceiver takesReceiver; - core::ClassOrModuleRef klass; - -public: - CallCMethod(core::NameRef rubyMethod, string cMethod, ShouldTakeReceiver takesReceiver, - Intrinsics::HandleBlock supportsBlocks, core::ClassOrModuleRef klass = core::ClassOrModuleRef{}) - : NameBasedIntrinsicMethod(supportsBlocks), rubyMethod(rubyMethod), cMethod(cMethod), - takesReceiver(takesReceiver), klass(klass){}; - - virtual llvm::Value *makeCall(MethodCallContext &mcctx) const override { - return buildCMethodCall(mcctx, cMethod, takesReceiver, klass); - } - virtual InlinedVector applicableMethods(CompilerState &cs) const override { - return {rubyMethod}; - } -}; - -class BuildHash : public NameBasedIntrinsicMethod { -public: - BuildHash() : NameBasedIntrinsicMethod(Intrinsics::HandleBlock::Unhandled) {} - - bool isLiteralish(CompilerState &cs, const core::TypePtr &t) const { - // See IREmitterHelpers::emitLiteralish; we put the expected fast test first. - if (core::isa_type(t)) { - return true; - } - - if (core::isa_type(t)) { - return true; - } - - if (core::isa_type(t)) { - return true; - } - - // IREmitterHelpers::emitLiteralish knows that its TypePtr argument is - // restricted. We have no such guarantees, so we have to be more defensive. - if (t.hasUntyped()) { - return false; - } - - return t.derivesFrom(cs, core::Symbols::FalseClass()) || t.derivesFrom(cs, core::Symbols::TrueClass()) || - t.derivesFrom(cs, core::Symbols::NilClass()); - } - - virtual llvm::Value *makeCall(MethodCallContext &mcctx) const override { - bool literalHash = absl::c_all_of(mcctx.send->args, [&](auto &v) { return isLiteralish(mcctx.cs, v.type); }); - - // Building an empty hash at runtime is just as cheap as duplicating an - // empty hash, and we don't have to waste space on the extra pre-built - // hash. - if (mcctx.send->args.empty() || !literalHash) { - return buildCMethodCall(mcctx, "sorbet_buildHashIntrinsic", NoReceiver, core::Symbols::Hash()); - } - - // We're going to build a literal hash at initialization time, and then - // duplicate that hash wherever we need it. This arrangement saves - // re-hashing the keys every time the hash literal is constructed. - static unsigned int counter = 0; - auto &builder = mcctx.builder; - string rawName = fmt::format("ruby_hashLiteral{}", ++counter); - auto tp = llvm::Type::getInt64Ty(mcctx.cs); - auto zero = llvm::ConstantInt::get(mcctx.cs, llvm::APInt(64, 0)); - - auto oldInsertPoint = builder.saveIP(); - auto globalDeclaration = - static_cast(mcctx.cs.module->getOrInsertGlobal(rawName, tp, [&] { - llvm::IRBuilder<> globalInitBuilder(mcctx.cs); - auto ret = new llvm::GlobalVariable(*mcctx.cs.module, tp, false, llvm::GlobalVariable::InternalLinkage, - zero, rawName); - ret->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global); - ret->setAlignment(llvm::MaybeAlign(8)); - - auto voidTy = llvm::Type::getVoidTy(mcctx.cs); - std::vector NoArgs(0, voidTy); - auto ft = llvm::FunctionType::get(voidTy, NoArgs, false); - auto constr = - llvm::Function::Create(ft, llvm::Function::InternalLinkage, {"Constr_", rawName}, *mcctx.cs.module); - - auto bb = llvm::BasicBlock::Create(mcctx.cs, "constrHashLiteral", constr); - globalInitBuilder.SetInsertPoint(bb); - auto argArray = globalInitBuilder.CreateAlloca(llvm::ArrayType::get(tp, mcctx.send->args.size()), - nullptr, "argArray"); - - int i = -1; - for (auto &v : mcctx.send->args) { - i++; - llvm::Value *val = IREmitterHelpers::emitLiteralish(mcctx.cs, globalInitBuilder, v.type); - globalInitBuilder.CreateStore( - val, globalInitBuilder.CreateConstGEP2_64(argArray, 0, i, fmt::format("hashArgs{}Addr", i))); - } - - auto hashValue = globalInitBuilder.CreateCall( - mcctx.cs.getFunction("sorbet_literalHashBuild"), - {llvm::ConstantInt::get(mcctx.cs, llvm::APInt(32, mcctx.send->args.size(), true)), - globalInitBuilder.CreateConstGEP2_64(argArray, 0, 0)}, - "builtHash"); - - globalInitBuilder.CreateStore(hashValue, ret); - globalInitBuilder.CreateRetVoid(); - globalInitBuilder.SetInsertPoint(mcctx.cs.globalConstructorsEntry); - globalInitBuilder.CreateCall(constr, {}); - - return ret; - })); - builder.restoreIP(oldInsertPoint); - - auto *index = builder.CreateLoad(globalDeclaration, "hashLiteral"); - auto *copy = builder.CreateCall(mcctx.cs.getFunction("sorbet_globalConstDupHash"), {index}, "duplicatedHash"); - Payload::assumeType(mcctx.cs, builder, copy, core::Symbols::Hash()); - return copy; - } - - virtual InlinedVector applicableMethods(CompilerState &cs) const override { - return {core::Names::buildHash()}; - } -} BuildHash; - -std::tuple prepareSplatArgs(MethodCallContext &mcctx, cfg::VariableUseSite &splatArgsVar, - cfg::VariableUseSite &kwArgsVar) { - auto &cs = mcctx.cs; - auto &irctx = mcctx.irctx; - auto *send = mcctx.send; - auto &builder = mcctx.builder; - - // For the VM send there will be two cases: - // - // 1. We do not have keyword args (args[3] is nil). Then we can just dup args[2] and use VM_CALL_ARGS_SPLAT. - // - // 2. We do have keyword args (args[3] is not nil). Then we'll need to construct an array of this form: - // - // [posarg0, posarg1, ..., posargn, kwhash] - // - // where args[2] = [posarg0, posarg1, ..., posargsn], and use VM_CALL_ARGS_SPLAT | VM_CALL_KW_SPLAT. - // - // There are two subcases: - // - // 2a. All keywords were inline (args[3] has even length, and its contents will be of the form - // [sym,val,sym,val,...,sym,val]). Then we can just construct kwhash by build_hash'ing args[3]. - // 2b. Not all keywords were inline (args[3] has odd length, and its contents will be of the form) - // [sym,val,sym,val,...,sym,val,kwhash1]). Then we dup args[3], pop kwhash1 from it, construct - // kwhash0 from what's remaining, and update kwhash0 with kwhash1 to obtain kwhash. - // - // Note that for now, the desugarer does not produce (2b) cases where the array is not simply [kwhash1]. - // Thus we can't really test this case, so we throw an error, even though the code that is there does - // attempt to do the right thing. - // - // TODO(perf): We can probably save quite a bit of intermediate dupping, popping, etc., by cleverer addressing - // of the array contents. - auto *splatArgs = Payload::varGet(mcctx.cs, splatArgsVar.variable, builder, irctx, mcctx.rubyRegionId); - - // TODO(perf) we can avoid duplicating the array here if we know that it was created specifically for this - // splat. - llvm::Value *splatArray = builder.CreateCall(cs.getFunction("sorbet_arrayDup"), {splatArgs}, "splatArray"); - - CallCacheFlags flags; - - if (kwArgsVar.type.derivesFrom(mcctx.cs, core::Symbols::NilClass())) { - flags.args_splat = true; - } else if (auto *ptt = core::cast_type(kwArgsVar.type)) { - flags.args_splat = true; - flags.kw_splat = true; - - auto *kwArgArray = Payload::varGet(mcctx.cs, kwArgsVar.variable, mcctx.builder, irctx, mcctx.rubyRegionId); - - llvm::Value *kwHash; - - if (ptt->elems.size() & 0x1) { - auto *kwHash1 = builder.CreateCall(cs.getFunction("sorbet_arrayPop"), {kwArgArray}, "kwHash1"); - if (ptt->elems.size() > 1) { - auto *size = llvm::ConstantInt::get(cs, llvm::APInt(32, ptt->elems.size() - 1, true)); - auto *innerPtr = builder.CreateCall(cs.getFunction("sorbet_rubyArrayInnerPtr"), {kwArgArray}); - kwHash = builder.CreateCall(cs.getFunction("sorbet_hashBuild"), {size, innerPtr}, "kwHash"); - builder.CreateCall(cs.getFunction("sorbet_hashUpdate"), {kwHash, kwHash1}); - - // Failing compilation because as of this writing, this case is not produced by the desugarer, so - // the above code is untested. In theory, once the case is implemented in the desugarer, it should - // be okay to remove this. - failCompilation(cs, core::Loc(cs.file, send->receiverLoc), - "internal error: arg 3 to call-with-splat has odd length > 1"); - } else { - kwHash = builder.CreateCall(cs.getFunction("sorbet_hashDup"), {kwHash1}, "kwHash"); - } - } else { - auto *size = llvm::ConstantInt::get(cs, llvm::APInt(32, ptt->elems.size(), true)); - auto *innerPtr = builder.CreateCall(cs.getFunction("sorbet_rubyArrayInnerPtr"), {kwArgArray}); - kwHash = builder.CreateCall(cs.getFunction("sorbet_hashBuild"), {size, innerPtr}, "kwHash"); - } - - builder.CreateCall(cs.getFunction("sorbet_arrayPush"), {splatArray, kwHash}); - } else { - // This should not be possible (desugarer will only pass nil or a tuple). - failCompilation(cs, core::Loc(cs.file, send->receiverLoc), - "internal error: arg 3 to call-with-splat has neither nil nor tuple type"); - } - - if (send->isPrivateOk) { - flags.fcall = true; - } - - return {flags, splatArray}; -} - -class CallWithSplat : public NameBasedIntrinsicMethod { -public: - CallWithSplat() : NameBasedIntrinsicMethod(Intrinsics::HandleBlock::Handled){}; - - virtual llvm::Value *makeCall(MethodCallContext &mcctx) const override { - // args[0] is the receiver - // args[1] is the method - // args[2] are the splat arguments - // args[3] are the keyword args - auto &cs = mcctx.cs; - auto &irctx = mcctx.irctx; - auto &builder = mcctx.builder; - auto *send = mcctx.send; - auto recv = send->args[0].variable; - auto *cfp = Payload::getCFPForBlock(cs, builder, irctx, mcctx.rubyRegionId); - - auto [flags, splatArray] = prepareSplatArgs(mcctx, send->args[2], send->args[3]); - - auto lit = core::cast_type_nonnull(send->args[1].type); - ENFORCE(lit.literalKind == core::NamedLiteralType::LiteralTypeKind::Symbol); - auto methodName = lit.asName(); - - // setup the inline cache - // Note that in the case of calling `super`, the VM's search mechanism will - // fetch the method name ID from the method definition itself, not the call - // cache, so it's OK that we're saying the cache is for `super`. - auto shortName = methodName.shortName(cs); - auto *cache = IREmitterHelpers::makeInlineCache(cs, builder, std::string(shortName), flags, 1, {}); - - // Push receiver and the splat array. - // For the receiver, we can't use MethodCallContext::varGetRecv here because the real receiver - // is actually the first arg of the callWithSplat intrinsic method. - Payload::pushRubyStackVector( - cs, builder, cfp, Payload::varGet(mcctx.cs, recv, mcctx.builder, irctx, mcctx.rubyRegionId), {splatArray}); - - // Call the receiver. - if (auto *blk = mcctx.blkAsFunction()) { - auto blkId = mcctx.blk.value(); - auto usesBreak = irctx.blockUsesBreak[blkId]; - auto *ifunc = Payload::getOrBuildBlockIfunc(cs, builder, irctx, blkId); - if (methodName == core::Names::super()) { - return Payload::callSuperFuncBlockWithCache(mcctx.cs, mcctx.builder, cache, usesBreak, ifunc); - } - return Payload::callFuncBlockWithCache(mcctx.cs, mcctx.builder, cache, usesBreak, ifunc); - } else { - auto *blockHandler = Payload::vmBlockHandlerNone(mcctx.cs, mcctx.builder); - if (methodName == core::Names::super()) { - return Payload::callSuperFuncWithCache(mcctx.cs, mcctx.builder, cache, blockHandler); - } - return Payload::callFuncWithCache(mcctx.cs, mcctx.builder, cache, blockHandler); - } - } - virtual InlinedVector applicableMethods(CompilerState &cs) const override { - return {core::Names::callWithSplat()}; - } -} CallWithSplat; - -class CallWithSplatAndBlock : public NameBasedIntrinsicMethod { -public: - CallWithSplatAndBlock() : NameBasedIntrinsicMethod{Intrinsics::HandleBlock::Unhandled} {}; - - virtual llvm::Value *makeCall(MethodCallContext &mcctx) const override { - ENFORCE(mcctx.blkAsFunction() == nullptr); - - // args[0] is the receiver - // args[1] is the method - // args[2] are the splat arguments - // args[3] are the keyword arguments - // args[4] is the block - auto &cs = mcctx.cs; - auto &irctx = mcctx.irctx; - auto &builder = mcctx.builder; - auto *send = mcctx.send; - auto recv = send->args[0].variable; - - auto [flags, splatArray] = prepareSplatArgs(mcctx, send->args[2], send->args[3]); - flags.blockarg = true; - auto *blockHandler = prepareBlockHandler(mcctx, send->args[4]); - - auto lit = core::cast_type_nonnull(send->args[1].type); - ENFORCE(lit.literalKind == core::NamedLiteralType::LiteralTypeKind::Symbol); - auto methodName = lit.asName(); - - // setup the inline cache - // Note that in the case of calling `super`, the VM's search mechanism will - // fetch the method name ID from the method definition itself, not the call - // cache, so it's OK that we're saying the cache is for `super`. - auto shortName = methodName.shortName(cs); - auto *cache = IREmitterHelpers::makeInlineCache(cs, builder, std::string(shortName), flags, 1, {}); - - // Push receiver and the splat array. - // For the receiver, we can't use MethodCallContext::varGetRecv here because the real receiver - // is actually the first arg of the callWithSplat intrinsic method. - auto *cfp = Payload::getCFPForBlock(cs, builder, irctx, mcctx.rubyRegionId); - Payload::pushRubyStackVector( - cs, builder, cfp, Payload::varGet(mcctx.cs, recv, mcctx.builder, irctx, mcctx.rubyRegionId), {splatArray}); - - if (methodName == core::Names::super()) { - return Payload::callSuperFuncWithCache(mcctx.cs, mcctx.builder, cache, blockHandler); - } - return Payload::callFuncWithCache(mcctx.cs, mcctx.builder, cache, blockHandler); - } - - virtual InlinedVector applicableMethods(CompilerState &cs) const override { - return {core::Names::callWithSplatAndBlock()}; - } -} CallWithSplatAndBlock; - -class NewIntrinsic : public NameBasedIntrinsicMethod { -public: - NewIntrinsic() : NameBasedIntrinsicMethod{Intrinsics::HandleBlock::Unhandled} {}; - - virtual llvm::Value *makeCall(MethodCallContext &mcctx) const override { - auto &cs = mcctx.cs; - auto &builder = mcctx.builder; - auto &irctx = mcctx.irctx; - int rubyRegionId = mcctx.rubyRegionId; - - auto *klass = mcctx.varGetRecv(); - auto *newCache = mcctx.getInlineCache(); - - auto slowCall = llvm::BasicBlock::Create(cs, "slowNew", builder.GetInsertBlock()->getParent()); - auto fastCall = llvm::BasicBlock::Create(cs, "fastNew", builder.GetInsertBlock()->getParent()); - auto afterNew = llvm::BasicBlock::Create(cs, "afterNew", builder.GetInsertBlock()->getParent()); - - auto *cfp = Payload::getCFPForBlock(cs, builder, irctx, rubyRegionId); - auto *allocatedObject = - builder.CreateCall(cs.getFunction("sorbet_maybeAllocateObjectFastPath"), {klass, newCache}); - auto *isUndef = Payload::testIsUndef(cs, builder, allocatedObject); - builder.CreateCondBr(isUndef, slowCall, fastCall); - - // We're pushing these arguments always, the only question is what we actually - // wind up calling with them. - auto &rubyStackArgs = mcctx.getStackArgs(); - - builder.SetInsertPoint(slowCall); - Payload::pushRubyStackVector(cs, builder, cfp, klass, rubyStackArgs.stack); - auto *nullBHForNew = Payload::vmBlockHandlerNone(cs, builder); - auto *slowValue = builder.CreateCall(cs.getFunction("sorbet_callFuncWithCache"), {newCache, nullBHForNew}); - builder.CreateBr(afterNew); - - builder.SetInsertPoint(fastCall); - - // Whatever the flags on the `new` call were, the call to initialize is always - // allowed to call a private method. - CallCacheFlags flags = rubyStackArgs.flags; - flags.fcall = true; - - auto *initializeCache = IREmitterHelpers::makeInlineCache(cs, builder, "initialize", flags, - rubyStackArgs.stack.size(), rubyStackArgs.keywords); - auto *nullBHForInitialize = Payload::vmBlockHandlerNone(cs, builder); - Payload::pushRubyStackVector(cs, builder, cfp, allocatedObject, rubyStackArgs.stack); - builder.CreateCall(cs.getFunction("sorbet_callFuncWithCache"), {initializeCache, nullBHForInitialize}); - builder.CreateBr(afterNew); - - builder.SetInsertPoint(afterNew); - auto *objectPhi = builder.CreatePHI(builder.getInt64Ty(), 2, "initializedObject"); - objectPhi->addIncoming(slowValue, slowCall); - objectPhi->addIncoming(allocatedObject, fastCall); - - return objectPhi; - } - - virtual InlinedVector applicableMethods(CompilerState &cs) const override { - return {core::Names::new_()}; - } -} NewIntrinsic; - -class DefinedClassVar : public NameBasedIntrinsicMethod { -public: - DefinedClassVar() : NameBasedIntrinsicMethod{Intrinsics::HandleBlock::Unhandled} {}; - - virtual llvm::Value *makeCall(MethodCallContext &mcctx) const override { - auto &cs = mcctx.cs; - auto &builder = mcctx.builder; - auto &irctx = mcctx.irctx; - int rubyRegionId = mcctx.rubyRegionId; - - auto *klass = Payload::getClassVariableStoreClass(cs, builder, irctx); - // TODO(froydnj): figure out how to access the ID of the argument directly. - auto *var = Payload::varGet(cs, mcctx.send->args[0].variable, builder, irctx, rubyRegionId); - return builder.CreateCall(cs.getFunction("sorbet_classVariableDefined"), {klass, var}, "is_cvar_defined"); - } - - virtual InlinedVector applicableMethods(CompilerState &cs) const override { - return {core::Names::definedClassVar()}; - } -} DefinedClassVar; - -class DefinedInstanceVar : public NameBasedIntrinsicMethod { -public: - DefinedInstanceVar() : NameBasedIntrinsicMethod{Intrinsics::HandleBlock::Unhandled} {}; - - virtual llvm::Value *makeCall(MethodCallContext &mcctx) const override { - auto &cs = mcctx.cs; - auto &builder = mcctx.builder; - auto &irctx = mcctx.irctx; - int rubyRegionId = mcctx.rubyRegionId; - - auto *self = Payload::varGet(cs, cfg::LocalRef::selfVariable(), builder, irctx, rubyRegionId); - // TODO(froydnj): figure out how to access the ID of the argument directly. - auto *var = Payload::varGet(cs, mcctx.send->args[0].variable, builder, irctx, rubyRegionId); - return builder.CreateCall(cs.getFunction("sorbet_instanceVariableDefined"), {self, var}, "is_cvar_defined"); - } - - virtual InlinedVector applicableMethods(CompilerState &cs) const override { - return {core::Names::definedInstanceVar()}; - } -} DefinedInstanceVar; - -class InstanceVariableGet : public NameBasedIntrinsicMethod { -public: - InstanceVariableGet() : NameBasedIntrinsicMethod{Intrinsics::HandleBlock::Unhandled} {}; - - virtual llvm::Value *makeCall(MethodCallContext &mcctx) const override { - if (mcctx.send->args.size() != 1) { - return IREmitterHelpers::emitMethodCallViaRubyVM(mcctx); - } - - auto &cs = mcctx.cs; - auto &builder = mcctx.builder; - auto &var = mcctx.send->args[0].type; - if (!core::isa_type(var)) { - return IREmitterHelpers::emitMethodCallViaRubyVM(mcctx); - } - auto lit = core::cast_type_nonnull(var); - if (lit.literalKind != core::NamedLiteralType::LiteralTypeKind::Symbol) { - return IREmitterHelpers::emitMethodCallViaRubyVM(mcctx); - } - - auto varName = lit.asName(); - auto varNameStr = varName.shortName(cs); - - auto *callCache = mcctx.getInlineCache(); - auto *ivarCache = Payload::buildInstanceVariableCache(cs, varNameStr); - auto *cfp = Payload::getCFPForBlock(cs, builder, mcctx.irctx, mcctx.rubyRegionId); - auto *recv = mcctx.varGetRecv(); - auto *ivarID = Payload::idIntern(cs, builder, varNameStr); - - return builder.CreateCall(cs.getFunction("sorbet_vm_instance_variable_get"), - {callCache, ivarCache, cfp, recv, ivarID}); - } - - virtual InlinedVector applicableMethods(CompilerState &cs) const override { - return {core::Names::instanceVariableGet()}; - } -} InstanceVariableGet; - -class InstanceVariableSet : public NameBasedIntrinsicMethod { -public: - InstanceVariableSet() : NameBasedIntrinsicMethod{Intrinsics::HandleBlock::Unhandled} {}; - - virtual llvm::Value *makeCall(MethodCallContext &mcctx) const override { - if (mcctx.send->args.size() != 2) { - return IREmitterHelpers::emitMethodCallViaRubyVM(mcctx); - } - - auto &cs = mcctx.cs; - auto &builder = mcctx.builder; - auto &var = mcctx.send->args[0].type; - if (!core::isa_type(var)) { - return IREmitterHelpers::emitMethodCallViaRubyVM(mcctx); - } - auto lit = core::cast_type_nonnull(var); - if (lit.literalKind != core::NamedLiteralType::LiteralTypeKind::Symbol) { - return IREmitterHelpers::emitMethodCallViaRubyVM(mcctx); - } - - auto varName = lit.asName(); - auto varNameStr = varName.shortName(cs); - - auto *callCache = mcctx.getInlineCache(); - auto *ivarCache = Payload::buildInstanceVariableCache(cs, varNameStr); - auto *cfp = Payload::getCFPForBlock(cs, builder, mcctx.irctx, mcctx.rubyRegionId); - auto *recv = mcctx.varGetRecv(); - auto *ivarID = Payload::idIntern(cs, builder, varNameStr); - auto *value = Payload::varGet(cs, mcctx.send->args[1].variable, builder, mcctx.irctx, mcctx.rubyRegionId); - - return builder.CreateCall(cs.getFunction("sorbet_vm_instance_variable_set"), - {callCache, ivarCache, cfp, recv, ivarID, value}); - } - - virtual InlinedVector applicableMethods(CompilerState &cs) const override { - return {core::Names::instanceVariableSet()}; - } -} InstanceVariableSet; - -class ClassIntrinsic : public NameBasedIntrinsicMethod { -public: - ClassIntrinsic() : NameBasedIntrinsicMethod{Intrinsics::HandleBlock::Unhandled} {}; - - virtual llvm::Value *makeCall(MethodCallContext &mcctx) const override { - if (mcctx.send->args.size() != 0) { - return IREmitterHelpers::emitMethodCallViaRubyVM(mcctx); - } - - auto &cs = mcctx.cs; - auto &builder = mcctx.builder; - auto *callCache = mcctx.getInlineCache(); - auto *cfp = Payload::getCFPForBlock(cs, builder, mcctx.irctx, mcctx.rubyRegionId); - auto *recv = mcctx.varGetRecv(); - return builder.CreateCall(cs.getFunction("sorbet_vm_class"), {callCache, cfp, recv}); - } - - virtual InlinedVector applicableMethods(CompilerState &cs) const override { - return {core::Names::class_()}; - } -} ClassIntrinsic; - -class BangIntrinsic : public NameBasedIntrinsicMethod { -public: - BangIntrinsic() : NameBasedIntrinsicMethod{Intrinsics::HandleBlock::Unhandled} {}; - - virtual llvm::Value *makeCall(MethodCallContext &mcctx) const override { - if (mcctx.send->args.size() != 0) { - return IREmitterHelpers::emitMethodCallViaRubyVM(mcctx); - } - - auto &cs = mcctx.cs; - auto &builder = mcctx.builder; - auto *callCache = mcctx.getInlineCache(); - auto *cfp = Payload::getCFPForBlock(cs, builder, mcctx.irctx, mcctx.rubyRegionId); - auto *recv = mcctx.varGetRecv(); - return builder.CreateCall(cs.getFunction("sorbet_vm_bang"), {callCache, cfp, recv}); - } - - virtual InlinedVector applicableMethods(CompilerState &cs) const override { - return {core::Names::bang()}; - } -} BangIntrinsic; - -class FreezeIntrinsic : public NameBasedIntrinsicMethod { -public: - FreezeIntrinsic() : NameBasedIntrinsicMethod{Intrinsics::HandleBlock::Unhandled} {} - - virtual llvm::Value *makeCall(MethodCallContext &mcctx) const override { - if (!mcctx.send->args.empty()) { - return IREmitterHelpers::emitMethodCallViaRubyVM(mcctx); - } - - auto &cs = mcctx.cs; - auto &builder = mcctx.builder; - auto *callCache = mcctx.getInlineCache(); - auto *cfp = Payload::getCFPForBlock(cs, builder, mcctx.irctx, mcctx.rubyRegionId); - auto *recv = mcctx.varGetRecv(); - return builder.CreateCall(cs.getFunction("sorbet_vm_freeze"), {callCache, cfp, recv}); - } - - virtual InlinedVector applicableMethods(CompilerState &cs) const override { - return {core::Names::freeze()}; - } -} FreezeIntrinsic; - -class IsAIntrinsic : public NameBasedIntrinsicMethod { -public: - IsAIntrinsic() : NameBasedIntrinsicMethod{Intrinsics::HandleBlock::Unhandled} {}; - - virtual llvm::Value *makeCall(MethodCallContext &mcctx) const override { - if (mcctx.send->args.size() != 1) { - return IREmitterHelpers::emitMethodCallViaRubyVM(mcctx); - } - - auto &cs = mcctx.cs; - auto &builder = mcctx.builder; - auto *callCache = mcctx.getInlineCache(); - auto *cfp = Payload::getCFPForBlock(cs, builder, mcctx.irctx, mcctx.rubyRegionId); - auto *recv = mcctx.varGetRecv(); - auto *var = Payload::varGet(cs, mcctx.send->args[0].variable, builder, mcctx.irctx, mcctx.rubyRegionId); - return builder.CreateCall(cs.getFunction("sorbet_vm_isa_p"), {callCache, cfp, recv, var}); - } - - virtual InlinedVector applicableMethods(CompilerState &cs) const override { - return {core::Names::isA_p(), core::Names::kindOf_p()}; - } -} IsAIntrinsic; - -class UntypedSpecialization : public NameBasedIntrinsicMethod { - const core::NameRef rubyMethod; - const uint32_t arity; - const string_view cMethod; - -public: - UntypedSpecialization(core::NameRef rubyMethod, uint32_t arity, string_view cMethod) - : NameBasedIntrinsicMethod{Intrinsics::HandleBlock::Unhandled}, rubyMethod(rubyMethod), arity(arity), - cMethod(cMethod) {} - - virtual llvm::Value *makeCall(MethodCallContext &mcctx) const override { - auto *send = mcctx.send; - if (send->args.size() != this->arity) { - return IREmitterHelpers::emitMethodCallViaRubyVM(mcctx); - } - // Should be taken care of by specifying Intrinsics::HandleBlock::Unhandled. - ENFORCE(!mcctx.blk.has_value()); - // If we had some kind of type information for the receiver, assume that we - // have already tested for a fast path earlier; this way we don't waste - // extra time doing another test that didn't work the first time. - if (!send->recv.type.isUntyped()) { - return IREmitterHelpers::emitMethodCallViaRubyVM(mcctx); - } - - auto &cs = mcctx.cs; - auto &builder = mcctx.builder; - auto *cache = mcctx.getInlineCache(); - auto *recv = mcctx.varGetRecv(); - auto &args = mcctx.getStackArgs(); - ENFORCE(args.stack.size() == this->arity); - ENFORCE(this->arity == 1 || this->arity == 2); - - auto *cFunction = cs.getFunction(llvm::StringRef{this->cMethod.data(), this->cMethod.size()}); - auto *cfp = Payload::getCFPForBlock(cs, builder, mcctx.irctx, mcctx.rubyRegionId); - - InlinedVector funcArgs{cfp, cache, recv, args.stack[0]}; - if (this->arity == 2) { - funcArgs.emplace_back(args.stack[1]); - } - - return builder.CreateCall(cFunction, llvm::ArrayRef{&funcArgs[0], funcArgs.size()}); - } - - virtual InlinedVector applicableMethods(CompilerState &cs) const override { - return {rubyMethod}; - } -}; - -static const vector knownCMethods{ - {core::Names::expandSplat(), "sorbet_expandSplatIntrinsic", NoReceiver, Intrinsics::HandleBlock::Unhandled, - core::Symbols::Array()}, - {core::Names::splat(), "sorbet_splatIntrinsic", NoReceiver, Intrinsics::HandleBlock::Unhandled, - core::Symbols::Array()}, - {core::Names::defined_p(), "sorbet_definedIntrinsic", NoReceiver, Intrinsics::HandleBlock::Unhandled}, - {core::Names::buildArray(), "sorbet_buildArrayIntrinsic", NoReceiver, Intrinsics::HandleBlock::Unhandled, - core::Symbols::Array()}, - {core::Names::buildRange(), "sorbet_buildRangeIntrinsic", NoReceiver, Intrinsics::HandleBlock::Unhandled, - core::ClassOrModuleRef()}, - {core::Names::stringInterpolate(), "sorbet_stringInterpolate", NoReceiver, Intrinsics::HandleBlock::Unhandled, - core::Symbols::String()}, - {core::Names::blockBreak(), "sorbet_block_break", NoReceiver, Intrinsics::HandleBlock::Unhandled}, - {core::Names::nil_p(), "sorbet_nil_p", TakesReceiver, Intrinsics::HandleBlock::Unhandled}, - {core::Names::checkMatchArray(), "sorbet_check_match_array", NoReceiver, Intrinsics::HandleBlock::Unhandled, - core::ClassOrModuleRef()}, - - // for kwsplat building - {core::Names::toHashDup(), "sorbet_magic_toHashDup", NoReceiver, Intrinsics::HandleBlock::Unhandled, - core::Symbols::Hash()}, - {core::Names::toHashNoDup(), "sorbet_magic_toHashNoDup", NoReceiver, Intrinsics::HandleBlock::Unhandled, - core::Symbols::Hash()}, - {core::Names::mergeHash(), "sorbet_magic_mergeHash", NoReceiver, Intrinsics::HandleBlock::Unhandled, - core::Symbols::Hash()}, - {core::Names::mergeHashValues(), "sorbet_magic_mergeHashValues", NoReceiver, Intrinsics::HandleBlock::Unhandled, - core::Symbols::Hash()}, -}; - -static const vector untypedSpecializations{ - {core::Names::squareBrackets(), 1, "sorbet_vm_aref"sv}, - {core::Names::plus(), 1, "sorbet_vm_plus"sv}, - {core::Names::minus(), 1, "sorbet_vm_minus"sv}, - {core::Names::eqeq(), 1, "sorbet_vm_eqeq"sv}, - {core::Names::neq(), 1, "sorbet_vm_neq"sv}, - {core::Names::leq(), 1, "sorbet_vm_leq"sv}, - {core::Names::lessThan(), 1, "sorbet_vm_lt"sv}, - {core::Names::geq(), 1, "sorbet_vm_geq"sv}, - {core::Names::greaterThan(), 1, "sorbet_vm_gt"sv}, -}; - -vector computeNameBasedIntrinsics() { - vector ret{ - &DoNothingIntrinsic, &DefineClassIntrinsic, &IdentityIntrinsic, &CallWithBlock, &ExceptionRetry, - &BuildHash, &CallWithSplat, &CallWithSplatAndBlock, &ShouldNeverSeeIntrinsic, &DefinedClassVar, - &DefinedInstanceVar, &NewIntrinsic, &InstanceVariableGet, &InstanceVariableSet, &ClassIntrinsic, - &BangIntrinsic, &FreezeIntrinsic, &IsAIntrinsic}; - for (auto &method : knownCMethods) { - ret.emplace_back(&method); - } - for (auto &method : untypedSpecializations) { - ret.emplace_back(&method); - } - return ret; -} - -} // namespace - -const vector &NameBasedIntrinsicMethod::definedIntrinsics() { - static vector ret = computeNameBasedIntrinsics(); - return ret; -} -} // namespace sorbet::compiler diff --git a/compiler/IREmitter/NameBasedIntrinsics.h b/compiler/IREmitter/NameBasedIntrinsics.h deleted file mode 100644 index a9cecc1a48..0000000000 --- a/compiler/IREmitter/NameBasedIntrinsics.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef SORBET_COMPILER_LLVMIREMITTER_INTRINSICS_H -#define SORBET_COMPILER_LLVMIREMITTER_INTRINSICS_H - -#include "IREmitterHelpers.h" -#include -#include - -namespace sorbet::compiler { - -struct IREmitterContext; - -class NameBasedIntrinsicMethod { -public: - const Intrinsics::HandleBlock blockHandled; - virtual llvm::Value *makeCall(MethodCallContext &mcctx) const = 0; - virtual InlinedVector applicableMethods(CompilerState &cs) const = 0; - NameBasedIntrinsicMethod(Intrinsics::HandleBlock blockHandled) : blockHandled(blockHandled){}; - virtual ~NameBasedIntrinsicMethod() = default; - static const std::vector &definedIntrinsics(); -}; -}; // namespace sorbet::compiler -#endif diff --git a/compiler/IREmitter/Payload.cc b/compiler/IREmitter/Payload.cc deleted file mode 100644 index 678dd9b4aa..0000000000 --- a/compiler/IREmitter/Payload.cc +++ /dev/null @@ -1,1130 +0,0 @@ -// These violate our poisons so have to happen first -#include "llvm/IR/DerivedTypes.h" // FunctionType -#include "llvm/IR/IRBuilder.h" - -#include "IREmitterHelpers.h" -#include "Payload.h" -#include "ast/Trees.h" -#include "common/sort/sort.h" -#include "common/typecase.h" -#include "compiler/Core/CompilerState.h" -#include "compiler/IREmitter/IREmitterContext.h" -#include - -using namespace std; -namespace sorbet::compiler { - -llvm::Value *Payload::setExpectedBool(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *value, - bool expected) { - return builder.CreateIntrinsic(llvm::Intrinsic::IndependentIntrinsics::expect, {llvm::Type::getInt1Ty(cs)}, - {value, builder.getInt1(expected)}); -} - -void Payload::boxRawValue(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::AllocaInst *target, - llvm::Value *rawData) { - builder.CreateStore(rawData, builder.CreateStructGEP(target, 0)); -} - -llvm::Value *Payload::unboxRawValue(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::AllocaInst *target) { - return builder.CreateLoad(builder.CreateStructGEP(target, 0), "rawRubyValue"); -} - -llvm::Value *Payload::rubyUndef(CompilerState &cs, llvm::IRBuilderBase &builder) { - return builder.CreateCall(cs.getFunction("sorbet_rubyUndef"), {}, "undefValueRaw"); -} - -llvm::Value *Payload::rubyNil(CompilerState &cs, llvm::IRBuilderBase &builder) { - return builder.CreateCall(cs.getFunction("sorbet_rubyNil"), {}, "nilValueRaw"); -} - -llvm::Value *Payload::rubyFalse(CompilerState &cs, llvm::IRBuilderBase &builder) { - return builder.CreateCall(cs.getFunction("sorbet_rubyFalse"), {}, "falseValueRaw"); -} - -llvm::Value *Payload::rubyTopSelf(CompilerState &cs, llvm::IRBuilderBase &builder) { - return builder.CreateCall(cs.getFunction("sorbet_rubyTopSelf"), {}, "topSelf"); -} - -llvm::Value *Payload::rubyTrue(CompilerState &cs, llvm::IRBuilderBase &builder) { - return builder.CreateCall(cs.getFunction("sorbet_rubyTrue"), {}, "trueValueRaw"); -} - -void Payload::raiseArity(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *currentArgCount, int minArgs, - int maxArgs) { - builder.CreateCall(cs.getFunction("sorbet_raiseArity"), - {currentArgCount, llvm::ConstantInt::get(cs, llvm::APInt(32, minArgs, true)), - llvm::ConstantInt::get(cs, llvm::APInt(32, maxArgs, true)) - - }); - builder.CreateUnreachable(); -} -llvm::Value *Payload::longToRubyValue(CompilerState &cs, llvm::IRBuilderBase &builder, long num) { - return builder.CreateCall(cs.getFunction("sorbet_longToRubyValue"), - {llvm::ConstantInt::get(cs, llvm::APInt(64, num, true))}, "rawRubyInt"); -} - -llvm::Value *Payload::doubleToRubyValue(CompilerState &cs, llvm::IRBuilderBase &builder, double num) { - return builder.CreateCall(cs.getFunction("sorbet_doubleToRubyValue"), - {llvm::ConstantFP::get(llvm::Type::getDoubleTy(cs), num)}, "rawRubyDouble"); -} - -llvm::Value *Payload::cPtrToRubyRegexp(CompilerState &cs, llvm::IRBuilderBase &builder, std::string_view str, - int options) { - // all regexp are frozen. We'll allocate it at load time and share it. - auto rawName = fmt::format("rubyRegexpFrozen_{}_{}", str, options); - auto tp = llvm::Type::getInt64Ty(cs); - auto zero = llvm::ConstantInt::get(cs, llvm::APInt(64, 0)); - - auto globalDeclaration = static_cast(cs.module->getOrInsertGlobal(rawName, tp, [&] { - llvm::IRBuilder<> globalInitBuilder(cs); - auto ret = - new llvm::GlobalVariable(*cs.module, tp, false, llvm::GlobalVariable::InternalLinkage, zero, rawName); - ret->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global); - ret->setAlignment(llvm::MaybeAlign(8)); - // create constructor - std::vector NoArgs(0, llvm::Type::getVoidTy(cs)); - auto ft = llvm::FunctionType::get(llvm::Type::getVoidTy(cs), NoArgs, false); - auto constr = llvm::Function::Create(ft, llvm::Function::InternalLinkage, {"Constr_", rawName}, *cs.module); - - auto bb = llvm::BasicBlock::Create(cs, "constr", constr); - globalInitBuilder.SetInsertPoint(bb); - auto rawCString = Payload::toCString(cs, str, globalInitBuilder); - auto rawStr = - globalInitBuilder.CreateCall(cs.getFunction("sorbet_cPtrToRubyRegexpFrozen"), - {rawCString, llvm::ConstantInt::get(cs, llvm::APInt(64, str.length())), - - llvm::ConstantInt::get(cs, llvm::APInt(32, options))}); - globalInitBuilder.CreateStore(rawStr, ret); - globalInitBuilder.CreateRetVoid(); - globalInitBuilder.SetInsertPoint(cs.globalConstructorsEntry); - globalInitBuilder.CreateCall(constr, {}); - - return ret; - })); - - ENFORCE(cs.functionEntryInitializers->getParent() == builder.GetInsertBlock()->getParent(), - "you're calling this function from something low-level that passed a IRBuilder that points outside of " - "function currently being generated"); - auto oldInsertPoint = builder.saveIP(); - builder.SetInsertPoint(cs.functionEntryInitializers); - auto name = llvm::StringRef(str.data(), str.length()); - auto global = builder.CreateLoad(globalDeclaration, {"rubyRegexp_", name}); - builder.restoreIP(oldInsertPoint); - - // todo(perf): mark these as immutable with https://llvm.org/docs/LangRef.html#llvm-invariant-start-intrinsic - return global; -} - -llvm::Value *Payload::cPtrToRubyString(CompilerState &cs, llvm::IRBuilderBase &builder, std::string_view str, - bool frozen) { - if (!frozen) { - auto rawCString = Payload::toCString(cs, str, builder); - return builder.CreateCall(cs.getFunction("sorbet_cPtrToRubyString"), - {rawCString, llvm::ConstantInt::get(cs, llvm::APInt(64, str.length(), true))}, - "rawRubyStr"); - } - auto *loadAddr = builder.CreateLoad(cs.rubyStringTableRef(str)); - auto *MD = llvm::MDNode::get(cs, llvm::None); - loadAddr->setMetadata(llvm::LLVMContext::MD_invariant_load, MD); - auto *load = builder.CreateLoad(loadAddr, {"rubyStr_", str}); - load->setMetadata(llvm::LLVMContext::MD_invariant_load, MD); - return load; -} - -llvm::Value *Payload::testIsUndef(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *val) { - return builder.CreateCall(cs.getFunction("sorbet_testIsUndef"), {val}, "isUndef"); -} - -llvm::Value *Payload::testIsTruthy(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *val) { - return builder.CreateCall(cs.getFunction("sorbet_testIsTruthy"), {val}, "cond"); -} - -llvm::Value *Payload::idIntern(CompilerState &cs, llvm::IRBuilderBase &builder, std::string_view idName) { - // We know that this load will always return the same reference at runtime, but - // the actual contents of the id table reference won't be filled in until - // later (see the code in CompilerState for why). But we want LLVM to know - // that the load always returns the same thing. So mark the load as - //`!invariant.load`. - auto *loadAddr = builder.CreateLoad(cs.idTableRef(idName)); - auto *MD = llvm::MDNode::get(cs, llvm::None); - loadAddr->setMetadata(llvm::LLVMContext::MD_invariant_load, MD); - auto *load = builder.CreateLoad(loadAddr, {"rubyId_", idName}); - load->setMetadata(llvm::LLVMContext::MD_invariant_load, MD); - return load; -} - -namespace { -std::string showClassName(const core::GlobalState &gs, core::SymbolRef sym) { - auto owner = sym.owner(gs); - bool includeOwner = !IREmitterHelpers::isRootishSymbol(gs, owner); - string ownerStr = includeOwner ? showClassName(gs, owner) + "::" : ""; - return ownerStr + IREmitterHelpers::showClassNameWithoutOwner(gs, sym); -} - -} // namespace - -llvm::Value *Payload::getRubyConstant(CompilerState &cs, core::SymbolRef sym, llvm::IRBuilderBase &builder) { - ENFORCE(sym.isClassOrModule() || sym.isStaticField(cs) || sym.isTypeMember()); - sym = IREmitterHelpers::fixupOwningSymbol(cs, sym); - auto str = showClassName(cs, sym); - ENFORCE(str.length() < 2 || (str[0] != ':'), "implementation assumes that strings dont start with ::"); - auto functionName = sym.isClassOrModule() ? "sorbet_i_getRubyClass" : "sorbet_i_getRubyConstant"; - return builder.CreateCall( - cs.getFunction(functionName), - {Payload::toCString(cs, str, builder), llvm::ConstantInt::get(cs, llvm::APInt(64, str.length()))}); -} - -llvm::Value *Payload::toCString(CompilerState &cs, string_view str, llvm::IRBuilderBase &builder) { - // We know that this load will always return the same reference at runtime, but - // the actual contents of the string table reference won't be filled in until - // later (see the code in CompilerState for why). But we want LLVM to know - // that the load always returns the same thing: such knowledge is important for - // transformations involving identical sorbet_i_getRuby{Class,Constant} calls - // and eliminating unnecessary type tests. So mark the load as `!invariant.load`. - auto *load = builder.CreateLoad(cs.stringTableRef(str)); - auto *MD = llvm::MDNode::get(cs, llvm::None); - load->setMetadata(llvm::LLVMContext::MD_invariant_load, MD); - return load; -} - -namespace { -const vector> optimizedTypeTests = { - {core::Symbols::untyped(), "sorbet_i_isa_Untyped"}, - {core::Symbols::Array(), "sorbet_i_isa_Array"}, - {core::Symbols::FalseClass(), "sorbet_i_isa_FalseClass"}, - {core::Symbols::TrueClass(), "sorbet_i_isa_TrueClass"}, - {core::Symbols::Float(), "sorbet_i_isa_Float"}, - {core::Symbols::Hash(), "sorbet_i_isa_Hash"}, - {core::Symbols::Integer(), "sorbet_i_isa_Integer"}, - {core::Symbols::NilClass(), "sorbet_i_isa_NilClass"}, - {core::Symbols::Proc(), "sorbet_i_isa_Proc"}, - {core::Symbols::Rational(), "sorbet_i_isa_Rational"}, - {core::Symbols::Regexp(), "sorbet_i_isa_Regexp"}, - {core::Symbols::String(), "sorbet_i_isa_String"}, - {core::Symbols::Symbol(), "sorbet_i_isa_Symbol"}, - {core::Symbols::Thread(), "sorbet_i_isa_Thread"}, - {core::Symbols::rootSingleton(), "sorbet_i_isa_RootSingleton"}, -}; - -bool hasOptimizedTest(core::ClassOrModuleRef sym) { - return absl::c_any_of(optimizedTypeTests, [sym](const auto &pair) { return pair.first == sym; }); -} - -core::ClassOrModuleRef hasOptimizedTest(const core::TypePtr &type) { - core::ClassOrModuleRef res; - typecase( - type, - [&res](const core::ClassType &ct) { - if (hasOptimizedTest(ct.symbol)) { - res = ct.symbol; - } - }, - [&res](const core::AppliedType &at) { - if (hasOptimizedTest(at.klass)) { - res = at.klass; - } - }, - [](const core::TypePtr &def) {}); - - return res; -} - -void flattenAndType(vector &results, const core::AndType &type) { - typecase( - type.left, [&results](const core::AndType &type) { flattenAndType(results, type); }, - [&results](const core::TypePtr &def) { results.emplace_back(def); }); - - typecase( - type.right, [&results](const core::AndType &type) { flattenAndType(results, type); }, - [&results](const core::TypePtr &def) { results.emplace_back(def); }); -} - -void flattenOrType(vector &results, const core::OrType &type) { - typecase( - type.left, [&results](const core::OrType &type) { flattenOrType(results, type); }, - [&results](const core::TypePtr &def) { results.emplace_back(def); }); - - typecase( - type.right, [&results](const core::OrType &type) { flattenOrType(results, type); }, - [&results](const core::TypePtr &def) { results.emplace_back(def); }); -} - -static bool isProc(core::ClassOrModuleRef sym) { - auto id = sym.id(); - return id >= core::Symbols::Proc0().id() && id <= core::Symbols::last_proc().id(); -} - -} // namespace - -llvm::Value *Payload::typeTest(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *val, - core::ClassOrModuleRef sym) { - for (const auto &[candidate, specializedCall] : optimizedTypeTests) { - if (sym == candidate) { - return builder.CreateCall(cs.getFunction(specializedCall), {val}); - } - } - - if (sym.data(cs)->name.isTEnumName(cs)) { - // T.let(..., MyEnum::X$1) is special. These are singleton values, so we can do a type - // test with an object (reference) equality check. - return builder.CreateCall(cs.getFunction("sorbet_testObjectEqual_p"), - {Payload::getRubyConstant(cs, sym, builder), val}); - } - - auto attachedClass = sym.data(cs)->attachedClass(cs); - // todo: handle attached of attached class - if (attachedClass.exists()) { - return builder.CreateCall(cs.getFunction("sorbet_isa_class_of"), - {val, Payload::getRubyConstant(cs, attachedClass, builder)}); - } - sym = isProc(sym) ? core::Symbols::Proc() : sym; - return builder.CreateCall(cs.getFunction("sorbet_i_objIsKindOf"), - {val, Payload::getRubyConstant(cs, sym, builder)}); -} - -llvm::Value *Payload::typeTest(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *val, - const core::TypePtr &type) { - llvm::Value *ret = nullptr; - typecase( - type, [&](const core::ClassType &ct) { ret = Payload::typeTest(cs, builder, val, ct.symbol); }, - [&](const core::AppliedType &at) { - ret = Payload::typeTest(cs, builder, val, at.klass); - // todo: ranges, hashes, sets, enumerator, and, overall, enumerables - }, - [&](const core::OrType &ct) { - // flatten the or, and order it so that the optimized type tests show up first - vector parts; - flattenOrType(parts, ct); - absl::c_partition(parts, [](const auto &ty) { return hasOptimizedTest(ty).exists(); }); - - // forward-declare the exit - auto *fun = builder.GetInsertBlock()->getParent(); - auto *exitBlock = llvm::BasicBlock::Create(cs, "orContinue", fun); - llvm::PHINode *phi; - - { - auto ip = builder.saveIP(); - builder.SetInsertPoint(exitBlock); - phi = builder.CreatePHI(builder.getInt1Ty(), 2, "orTypeTest"); - builder.restoreIP(ip); - } - - llvm::Value *testResult = nullptr; - for (const auto &part : parts) { - // for all cases after the first, close the previous block by adding a conditional branch - if (testResult != nullptr) { - auto *block = llvm::BasicBlock::Create(cs, "orCase", fun); - builder.CreateCondBr(testResult, exitBlock, block); - builder.SetInsertPoint(block); - } - - testResult = typeTest(cs, builder, val, part); - phi->addIncoming(testResult, builder.GetInsertBlock()); - } - - // close the last block by adding an unconditional branch to the exit block - builder.CreateBr(exitBlock); - builder.SetInsertPoint(exitBlock); - ret = phi; - }, - [&](const core::AndType &ct) { - // flatten the and, and order it so that the optimized type tests show up first - vector parts; - flattenAndType(parts, ct); - absl::c_partition(parts, [](const auto &ty) { return hasOptimizedTest(ty).exists(); }); - - // forward-declare the exit - auto *fun = builder.GetInsertBlock()->getParent(); - auto *exitBlock = llvm::BasicBlock::Create(cs, "andContinue", fun); - llvm::PHINode *phi; - - { - auto ip = builder.saveIP(); - builder.SetInsertPoint(exitBlock); - phi = builder.CreatePHI(builder.getInt1Ty(), 2, "andTypeTest"); - builder.restoreIP(ip); - } - - llvm::Value *testResult = nullptr; - for (const auto &part : parts) { - // for all cases after the first, close the previous block by adding a conditional branch - if (testResult != nullptr) { - auto *block = llvm::BasicBlock::Create(cs, "andCase", fun); - builder.CreateCondBr(testResult, block, exitBlock); - builder.SetInsertPoint(block); - } - - testResult = typeTest(cs, builder, val, part); - phi->addIncoming(testResult, builder.GetInsertBlock()); - } - - // close the last block by adding an unconditional branch to the exit block - builder.CreateBr(exitBlock); - builder.SetInsertPoint(exitBlock); - ret = phi; - }, - [&](const core::TypePtr &_default) { ret = builder.getInt1(true); }); - ENFORCE(ret != nullptr); - return ret; -} - -// Emit an `llvm.assume` intrinsic with the result of a `Payload::typeTest` with the given symbol. For example, this can -// be used assert that a value will be an array. -// -// NOTE: this will make the optimizer behave as though the type test can never be false, and undefined behavior will -// arise if it is false at runtime. If it's not clear that the type test will always be true, err on the side of not -// adding an assertion. -void Payload::assumeType(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *val, - core::ClassOrModuleRef sym) { - auto *cond = Payload::typeTest(cs, builder, val, sym); - builder.CreateIntrinsic(llvm::Intrinsic::IndependentIntrinsics::assume, {}, {cond}); - return; -} - -void Payload::assumeType(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *val, const core::TypePtr &type) { - auto *cond = Payload::typeTest(cs, builder, val, type); - builder.CreateIntrinsic(llvm::Intrinsic::IndependentIntrinsics::assume, {}, {cond}); - return; -} - -llvm::Value *Payload::boolToRuby(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *uint8_t) { - return builder.CreateCall(cs.getFunction("sorbet_boolToRuby"), {uint8_t}, "rubyBool"); -} - -namespace { - -llvm::Value *allocateRubyStackFrames(CompilerState &cs, llvm::IRBuilderBase &builder, const IREmitterContext &irctx, - const ast::MethodDef &md, int rubyRegionId); - -llvm::Value *getIseqType(CompilerState &cs, llvm::IRBuilderBase &builder, const IREmitterContext &irctx, - int rubyRegionId) { - switch (irctx.rubyBlockType[rubyRegionId]) { - case FunctionType::Method: - return builder.CreateCall(cs.getFunction("sorbet_rubyIseqTypeMethod"), {}, "ISEQ_TYPE_METHOD"); - - case FunctionType::StaticInitFile: - return builder.CreateCall(cs.getFunction("sorbet_rubyIseqTypeTop"), {}, "ISEQ_TYPE_TOP"); - - case FunctionType::StaticInitModule: - return builder.CreateCall(cs.getFunction("sorbet_rubyIseqTypeClass"), {}, "ISEQ_TYPE_CLASS"); - - case FunctionType::Block: - return builder.CreateCall(cs.getFunction("sorbet_rubyIseqTypeBlock"), {}, "ISEQ_TYPE_BLOCK"); - - case FunctionType::Rescue: - return builder.CreateCall(cs.getFunction("sorbet_rubyIseqTypeRescue"), {}, "ISEQ_TYPE_RESCUE"); - - case FunctionType::Ensure: - return builder.CreateCall(cs.getFunction("sorbet_rubyIseqTypeEnsure"), {}, "ISEQ_TYPE_ENSURE"); - - case FunctionType::ExceptionBegin: - // Exception body functions inherit the iseq entry for their containing context, so we should never be - // generating an iseq entry for them. - Exception::raise("Allocating an iseq for a FunctionType::ExceptionBegin function"); - break; - - case FunctionType::Unused: - // This should never happen, as we should be skipping iseq initialization for unused functions. - Exception::raise("Picking an ISEQ_TYPE for an unused function!"); - break; - } -} - -llvm::PointerType *iseqType(CompilerState &cs) { - return llvm::PointerType::getUnqual(llvm::StructType::getTypeByName(cs, "struct.rb_iseq_struct")); -} - -// Given a Ruby block, finds the block id of the nearest _proper_ ancestor of that block that allocates an iseq. -int getNearestIseqAllocatorBlock(const IREmitterContext &irctx, int rubyRegionId) { - do { - rubyRegionId = irctx.rubyBlockParent[rubyRegionId]; - } while (rubyRegionId > 0 && irctx.rubyBlockType[rubyRegionId] == FunctionType::ExceptionBegin); - - return rubyRegionId; -} - -std::tuple getIseqInfo(CompilerState &cs, llvm::IRBuilderBase &builder, - const IREmitterContext &irctx, const ast::MethodDef &md, - int rubyRegionId) { - auto &locationName = irctx.rubyBlockLocationNames[rubyRegionId]; - llvm::Value *parent = nullptr; - switch (irctx.rubyBlockType[rubyRegionId]) { - case FunctionType::Method: - case FunctionType::StaticInitFile: - case FunctionType::StaticInitModule: - parent = llvm::Constant::getNullValue(iseqType(cs)); - break; - - case FunctionType::Block: - case FunctionType::Rescue: - case FunctionType::Ensure: - parent = allocateRubyStackFrames(cs, builder, irctx, md, getNearestIseqAllocatorBlock(irctx, rubyRegionId)); - break; - - case FunctionType::ExceptionBegin: - // Exception body functions inherit the iseq entry for their containing context, so we should never be - // generating an iseq entry for them. - Exception::raise("Allocating an iseq for a FunctionType::ExceptionBegin function"); - break; - - case FunctionType::Unused: - // This should never happen, as we should be skipping iseq initialization for unused functions. - Exception::raise("Picking an ISEQ_TYPE for an unused function!"); - break; - } - - // If we get here, we know we have a valid iseq and a valid name. - ENFORCE(locationName.has_value()); - return {*locationName, parent}; -} - -// Fill the locals array with interned ruby IDs. -void fillLocals(CompilerState &cs, llvm::IRBuilderBase &builder, const IREmitterContext &irctx, int rubyRegionId, - int baseOffset, llvm::Value *locals) { - // The map used to store escaped variables isn't stable, so we first sort it into a vector. This isn't great, but - // without this step the locals are processed in random order, making the llvm output unstable. - vector> escapedVariables{}; - for (auto &entry : irctx.escapedVariableIndices) { - escapedVariables.emplace_back(entry); - } - - fast_sort(escapedVariables, [](const auto &left, const auto &right) -> bool { - return left.second.localIndex < right.second.localIndex; - }); - - for (auto &entry : escapedVariables) { - auto *id = Payload::idIntern(cs, builder, entry.first.data(irctx.cfg)._name.shortName(cs)); - builder.CreateStore(id, builder.CreateConstGEP1_32(locals, baseOffset + entry.second.localIndex)); - } -} - -// A description of where local variables' IDs are held prior to calling -// `sorbet_allocateRubyStackFrame`. -enum class LocalsIDStorage { - // A normal method or static init function: we will allocate a temporar - // array on the C stack. - Stack, - - // Blocks and exception-related functions: locals are inherited from the containing - // method, which already allocated ID storage according to one of the other enum - // values. We don't need to allocate any space. - Inherited, -}; - -LocalsIDStorage classifyStorageFor(CompilerState &cs, const IREmitterContext &irctx, const ast::MethodDef &md, - int rubyRegionId) { - switch (irctx.rubyBlockType[rubyRegionId]) { - case FunctionType::Method: - case FunctionType::StaticInitFile: - case FunctionType::StaticInitModule: - return LocalsIDStorage::Stack; - - case FunctionType::Block: - case FunctionType::Rescue: - case FunctionType::Ensure: - case FunctionType::ExceptionBegin: - case FunctionType::Unused: - return LocalsIDStorage::Inherited; - } -} - -// Allocate an array to hold local variable ids before calling `sorbet_allocateRubyStackFrame`. -tuple getLocals(CompilerState &cs, llvm::IRBuilderBase &builder, - const IREmitterContext &irctx, const ast::MethodDef &md, - int rubyRegionId) { - llvm::Value *locals = nullptr; - llvm::Value *numLocals = nullptr; - auto *idType = llvm::Type::getInt64Ty(cs); - auto *idPtrType = llvm::PointerType::getUnqual(idType); - - auto storageKind = classifyStorageFor(cs, irctx, md, rubyRegionId); - switch (storageKind) { - case LocalsIDStorage::Inherited: - numLocals = llvm::ConstantInt::get(cs, llvm::APInt(32, 0, true)); - locals = llvm::ConstantPointerNull::get(idPtrType); - break; - - case LocalsIDStorage::Stack: - numLocals = llvm::ConstantInt::get(cs, llvm::APInt(32, irctx.escapedVariableIndices.size(), true)); - locals = builder.CreateAlloca(idType, numLocals, "locals"); - fillLocals(cs, builder, irctx, rubyRegionId, 0, locals); - break; - } - - return {locals, numLocals}; -} - -llvm::Function *allocateRubyStackFramesImpl(CompilerState &cs, const IREmitterContext &irctx, const ast::MethodDef &md, - int rubyRegionId, llvm::GlobalVariable *store) { - std::vector argTys{llvm::Type::getInt64Ty(cs)}; - auto ft = llvm::FunctionType::get(llvm::Type::getVoidTy(cs), argTys, false); - auto constr = - llvm::Function::Create(ft, llvm::Function::InternalLinkage, {"Constr_", store->getName()}, *cs.module); - - auto realpath = constr->arg_begin(); - realpath->setName("realpath"); - - auto bei = llvm::BasicBlock::Create(cs, "entryInitializers", constr); - auto bb = llvm::BasicBlock::Create(cs, "constr", constr); - - llvm::IRBuilder<> builder(cs); - builder.SetInsertPoint(bb); - - // We are building a new function. We should redefine where do function initializers go - auto cs1 = cs.withFunctionEntry(bei); - - auto loc = md.symbol.data(cs)->loc(); - auto file = cs.file; - auto *iseqType = getIseqType(cs1, builder, irctx, rubyRegionId); - auto [funcName, parent] = getIseqInfo(cs1, builder, irctx, md, rubyRegionId); - auto funcNameId = Payload::idIntern(cs1, builder, funcName); - auto funcNameValue = Payload::cPtrToRubyString(cs1, builder, funcName, true); - auto filename = file.data(cs).path(); - auto filenameValue = Payload::cPtrToRubyString(cs1, builder, filename, true); - // The method might have been synthesized by Sorbet (e.g. in the case of packages). - // Give such methods line numbers of 0. - unsigned startLine; - if (loc.exists()) { - startLine = loc.position(cs).first.line; - } else { - startLine = 0; - } - auto [locals, numLocals] = getLocals(cs1, builder, irctx, md, rubyRegionId); - auto sendMax = llvm::ConstantInt::get(cs, llvm::APInt(32, irctx.maxSendArgCount, true)); - auto *fileLineNumberInfo = Payload::getFileLineNumberInfo(cs, builder, file); - auto *fn = cs.getFunction("sorbet_allocateRubyStackFrame"); - auto ret = builder.CreateCall(fn, {funcNameValue, funcNameId, filenameValue, realpath, parent, iseqType, - llvm::ConstantInt::get(cs, llvm::APInt(32, startLine)), fileLineNumberInfo, - locals, numLocals, sendMax}); - builder.CreateStore(ret, store); - builder.CreateRetVoid(); - builder.SetInsertPoint(bei); - builder.CreateBr(bb); - return constr; -} - -// The common suffix for stack frame related global names. -string getStackFrameGlobalName(CompilerState &cs, const IREmitterContext &irctx, core::MethodRef methodSym, - int rubyRegionId) { - auto name = IREmitterHelpers::getFunctionName(cs, methodSym); - - switch (irctx.rubyBlockType[rubyRegionId]) { - case FunctionType::Method: - case FunctionType::StaticInitFile: - case FunctionType::StaticInitModule: - return name; - - case FunctionType::Block: - case FunctionType::Rescue: - case FunctionType::Ensure: - case FunctionType::ExceptionBegin: - case FunctionType::Unused: - return name + "$block_" + std::to_string(rubyRegionId); - } -} - -llvm::GlobalVariable *rubyStackFrameVar(CompilerState &cs, llvm::IRBuilderBase &builder, const IREmitterContext &irctx, - core::MethodRef methodSym, int rubyRegionId) { - auto tp = iseqType(cs); - auto name = getStackFrameGlobalName(cs, irctx, methodSym, rubyRegionId); - string rawName = "stackFramePrecomputed_" + name; - auto *var = static_cast(cs.module->getOrInsertGlobal(rawName, tp, [&] { - auto ret = - new llvm::GlobalVariable(*cs.module, tp, false, llvm::GlobalVariable::InternalLinkage, nullptr, rawName); - ret->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global); - ret->setAlignment(llvm::MaybeAlign(8)); - - return ret; - })); - - return var; -} - -llvm::Value *allocateRubyStackFrames(CompilerState &cs, llvm::IRBuilderBase &builder, const IREmitterContext &irctx, - const ast::MethodDef &md, int rubyRegionId) { - llvm::IRBuilder<> globalInitBuilder(cs); - auto globalDeclaration = rubyStackFrameVar(cs, builder, irctx, md.symbol, rubyRegionId); - if (!globalDeclaration->hasInitializer()) { - auto nullv = llvm::ConstantPointerNull::get(llvm::cast(globalDeclaration->getValueType())); - globalDeclaration->setInitializer(nullv); - - // The realpath is the first argument to `sorbet_globalConstructors` - auto realpath = cs.globalConstructorsEntry->getParent()->arg_begin(); - realpath->setName("realpath"); - - // create constructor - auto constr = allocateRubyStackFramesImpl(cs, irctx, md, rubyRegionId, globalDeclaration); - globalInitBuilder.SetInsertPoint(cs.globalConstructorsEntry); - globalInitBuilder.CreateCall(constr, {realpath}); - } - - globalInitBuilder.SetInsertPoint(cs.functionEntryInitializers); - auto global = globalInitBuilder.CreateLoad(globalDeclaration, "stackFrame"); - - // todo(perf): mark these as immutable with https://llvm.org/docs/LangRef.html#llvm-invariant-start-intrinsic - return global; -} - -} // namespace - -llvm::Value *Payload::rubyStackFrameVar(CompilerState &cs, llvm::IRBuilderBase &builder, const IREmitterContext &irctx, - core::MethodRef methodSym) { - return ::sorbet::compiler::rubyStackFrameVar(cs, builder, irctx, methodSym, 0); -} - -llvm::Value *Payload::setRubyStackFrame(CompilerState &cs, llvm::IRBuilderBase &builder, const IREmitterContext &irctx, - const ast::MethodDef &md, int rubyRegionId) { - auto stackFrame = allocateRubyStackFrames(cs, builder, irctx, md, rubyRegionId); - auto *iseqType = getIseqType(cs, builder, irctx, rubyRegionId); - auto *isStaticInit = llvm::ConstantInt::get( - cs, llvm::APInt(1, static_cast(irctx.rubyBlockType[rubyRegionId] == FunctionType::StaticInitFile || - irctx.rubyBlockType[rubyRegionId] == FunctionType::StaticInitModule))); - builder.CreateCall(cs.getFunction("sorbet_setRubyStackFrame"), {isStaticInit, iseqType, stackFrame}); - auto *cfp = getCFPForBlock(cs, builder, irctx, rubyRegionId); - auto pc = builder.CreateCall(cs.getFunction("sorbet_getPc"), {cfp}); - return pc; -} - -// Ensure that the retry singleton is present during module initialization, and store it in a module-local global. -llvm::Value *Payload::retrySingleton(CompilerState &cs, llvm::IRBuilderBase &builder, const IREmitterContext &irctx) { - auto tp = llvm::Type::getInt64Ty(cs); - string rawName = ""; - auto *global = cs.module->getOrInsertGlobal(rawName, tp, [&] { - auto globalInitBuilder = llvm::IRBuilder<>(cs); - - auto isConstant = false; - auto zero = llvm::ConstantInt::get(cs, llvm::APInt(64, 0, true)); - auto global = - new llvm::GlobalVariable(*cs.module, tp, isConstant, llvm::GlobalVariable::InternalLinkage, zero, rawName); - - globalInitBuilder.SetInsertPoint(cs.globalConstructorsEntry); - auto *singletonValue = globalInitBuilder.CreateCall(cs.getFunction("sorbet_getTRetry"), {}, "retrySingleton"); - - globalInitBuilder.CreateStore(singletonValue, global); - - return global; - }); - - return builder.CreateLoad(global, rawName); -} - -// Ensure that the VOID singleton is present during module initialization, and store it in a module-local global. -llvm::Value *Payload::voidSingleton(CompilerState &cs, llvm::IRBuilderBase &builder, const IREmitterContext &irctx) { - auto tp = llvm::Type::getInt64Ty(cs); - string rawName = ""; - auto *global = cs.module->getOrInsertGlobal(rawName, tp, [&] { - auto globalInitBuilder = llvm::IRBuilder<>(cs); - - auto isConstant = false; - auto zero = llvm::ConstantInt::get(cs, llvm::APInt(64, 0, true)); - auto global = - new llvm::GlobalVariable(*cs.module, tp, isConstant, llvm::GlobalVariable::InternalLinkage, zero, rawName); - - globalInitBuilder.SetInsertPoint(cs.globalConstructorsEntry); - auto *singletonValue = - globalInitBuilder.CreateCall(cs.getFunction("sorbet_getVoidSingleton"), {}, "voidSingleton"); - - globalInitBuilder.CreateStore(singletonValue, global); - - return global; - }); - - return builder.CreateLoad(global, rawName); -} - -// Lazily initialize a global that contains enough noops to represent all the lines in the file as an iseq_encoded -// array. -llvm::Value *Payload::getFileLineNumberInfo(CompilerState &cs, llvm::IRBuilderBase &builder, core::FileRef file) { - auto *iseqEncodedInitFn = cs.module->getFunction("sorbet_initLineNumberInfo"); - auto *infoPointerTy = iseqEncodedInitFn->getFunctionType()->params()[0]; - ENFORCE(infoPointerTy != nullptr); - - auto *globalTy = llvm::cast(infoPointerTy)->getElementType(); - - auto *iseqEncoded = getIseqEncodedPointer(cs, builder, file); - const string rawName = "fileLineNumberInfo"; - auto *global = cs.module->getOrInsertGlobal( - rawName, globalTy, [&cs, &rawName, &iseqEncoded, file, globalTy, iseqEncodedInitFn]() { - auto globalInitBuilder = llvm::IRBuilder<>(cs); - - bool isConstant = false; - auto *zero = llvm::ConstantAggregateZero::get(globalTy); - auto *fileLineNumberInfo = new llvm::GlobalVariable(*cs.module, globalTy, isConstant, - llvm::GlobalVariable::InternalLinkage, zero, rawName); - - globalInitBuilder.SetInsertPoint(cs.globalConstructorsEntry); - - auto *numLines = llvm::ConstantInt::get(cs, llvm::APInt(32, file.data(cs).lineCount(), true)); - globalInitBuilder.CreateCall( - iseqEncodedInitFn, - {fileLineNumberInfo, globalInitBuilder.CreateConstGEP2_32(nullptr, iseqEncoded, 0, 0), numLines}); - - return fileLineNumberInfo; - }); - - return global; -} - -llvm::Value *Payload::getIseqEncodedPointer(CompilerState &cs, llvm::IRBuilderBase &builder, core::FileRef file) { - auto *int64Ty = llvm::Type::getInt64Ty(cs); - uint32_t lineCount = file.data(cs).lineCount(); - auto *globalTy = llvm::ArrayType::get(int64Ty, lineCount); - - const string rawName = "iseqEncodedArray"; - auto *global = cs.module->getOrInsertGlobal(rawName, globalTy, [&cs, &rawName, globalTy]() { - auto globalInitBuilder = llvm::IRBuilder<>(cs); - - bool isConstant = false; - auto *zero = llvm::ConstantAggregateZero::get(globalTy); - auto *iseqEncodedArray = new llvm::GlobalVariable(*cs.module, globalTy, isConstant, - llvm::GlobalVariable::InternalLinkage, zero, rawName); - - return iseqEncodedArray; - }); - - return global; -} - -core::Loc Payload::setLineNumber(CompilerState &cs, llvm::IRBuilderBase &builder, core::Loc loc, core::Loc methodStart, - core::Loc lastLoc, llvm::AllocaInst *lineNumberPtr) { - if (!loc.exists()) { - return lastLoc; - } - auto lineno = loc.position(cs).first.line; - if (lastLoc.exists() && lastLoc.position(cs).first.line == lineno) { - return lastLoc; - } - if (!methodStart.exists()) { - return lastLoc; - } - - // turn the line number into an offset into the iseq_encoded global array - auto *offset = llvm::ConstantInt::get(cs, llvm::APInt(32, lineno - 1)); - - auto *encoded = Payload::getIseqEncodedPointer(cs, builder, loc.file()); - builder.CreateCall(cs.getFunction("sorbet_setLineNumber"), - {offset, builder.CreateConstGEP2_32(nullptr, encoded, 0, 0), builder.CreateLoad(lineNumberPtr)}); - return loc; -} - -llvm::Value *Payload::readKWRestArg(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *maybeHash) { - return builder.CreateCall(cs.getFunction("sorbet_readKWRestArgs"), {maybeHash}); -} - -llvm::Value *Payload::addMissingKWArg(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *missing, - llvm::Value *sym) { - return builder.CreateCall(cs.getFunction("sorbet_addMissingKWArg"), {missing, sym}); -} - -llvm::Value *Payload::assertAllRequiredKWArgs(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *missing) { - return builder.CreateCall(cs.getFunction("sorbet_assertAllRequiredKWArgs"), {missing}); -} - -llvm::Value *Payload::assertNoExtraKWArg(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *maybeHash, - llvm::Value *numRequired, llvm::Value *optionalParsed) { - return builder.CreateCall(cs.getFunction("sorbet_assertNoExtraKWArg"), {maybeHash, numRequired, optionalParsed}); -} - -llvm::Value *Payload::getKWArg(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *maybeHash, - llvm::Value *rubySym) { - return builder.CreateCall(cs.getFunction("sorbet_getKWArg"), {maybeHash, rubySym}); -} - -llvm::Value *Payload::removeKWArg(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *maybeHash, - llvm::Value *rubySym) { - return builder.CreateCall(cs.getFunction("sorbet_removeKWArg"), {maybeHash, rubySym}); -} - -llvm::Value *Payload::readRestArgs(CompilerState &cs, llvm::IRBuilderBase &builder, int maxPositionalArgCount, - llvm::Value *argCountRaw, llvm::Value *argArrayRaw) { - return builder.CreateCall( - cs.getFunction("sorbet_readRestArgs"), - {llvm::ConstantInt::get(cs, llvm::APInt(32, maxPositionalArgCount)), argCountRaw, argArrayRaw}); -} - -namespace { -// For a variable that's escaped, compute its index into the locals from its unique id in the -// closure. -llvm::Value *indexForLocalVariable(CompilerState &cs, const IREmitterContext &irctx, int rubyRegionId, int escapeId) { - return llvm::ConstantInt::get(cs, llvm::APInt(64, escapeId, true)); -} - -} // namespace - -llvm::Value *Payload::buildInstanceVariableCache(CompilerState &cs, std::string_view name) { - auto *cacheTy = llvm::StructType::getTypeByName(cs, "struct.iseq_inline_iv_cache_entry"); - ENFORCE(cacheTy != nullptr); - auto *zero = llvm::ConstantAggregateZero::get(cacheTy); - // No special initialization necessary, unlike function inline caches. - return new llvm::GlobalVariable(*cs.module, cacheTy, false, llvm::GlobalVariable::InternalLinkage, zero, - llvm::Twine("ivc_") + string(name)); -} - -llvm::Value *Payload::getClassVariableStoreClass(CompilerState &cs, llvm::IRBuilderBase &builder, - const IREmitterContext &irctx) { - auto sym = irctx.cfg.symbol.data(cs)->owner; - return Payload::getRubyConstant(cs, sym.data(cs)->topAttachedClass(cs), builder); -} - -EscapedVariableInfo Payload::escapedVariableInfo(CompilerState &cs, cfg::LocalRef local, const IREmitterContext &irctx, - int rubyRegionId) { - auto &escapedUse = irctx.escapedVariableIndices.at(local); - auto *index = indexForLocalVariable(cs, irctx, rubyRegionId, escapedUse.localIndex); - auto level = irctx.rubyBlockLevel[rubyRegionId]; - return EscapedVariableInfo{escapedUse, index, llvm::ConstantInt::get(cs, llvm::APInt(64, level, true))}; -} - -llvm::Value *Payload::varGet(CompilerState &cs, cfg::LocalRef local, llvm::IRBuilderBase &builder, - const IREmitterContext &irctx, int rubyRegionId) { - if (local == cfg::LocalRef::selfVariable()) { - return Payload::unboxRawValue(cs, builder, irctx.selfVariables.at(rubyRegionId)); - } - if (irctx.aliases.contains(local)) { - // alias to a field or constant - auto alias = irctx.aliases.at(local); - - switch (alias.kind) { - case Alias::AliasKind::Constant: - return Payload::getRubyConstant(cs, alias.constantSym, builder); - case Alias::AliasKind::GlobalField: - return builder.CreateCall(cs.getFunction("sorbet_globalVariableGet"), - {Payload::idIntern(cs, builder, alias.globalField.shortName(cs))}); - case Alias::AliasKind::ClassField: - return builder.CreateCall(cs.getFunction("sorbet_classVariableGet"), - {getClassVariableStoreClass(cs, builder, irctx), - Payload::idIntern(cs, builder, alias.classField.shortName(cs))}); - case Alias::AliasKind::InstanceField: { - // Each instance variable reference receives its own cache; this - // is the easiest thing to do and is identical to what the VM does. - // - // TODO: attempt to create caches shared across variable references - // for each instance variable for a given class. This is different - // than what the VM does, but it ought to be a small size/performance - // win. This change would require Sorbet changes so we have the - // necessary information at this point. - auto name = alias.instanceField.shortName(cs); - auto *cache = buildInstanceVariableCache(cs, name); - return builder.CreateCall(cs.getFunction("sorbet_instanceVariableGet"), - {varGet(cs, cfg::LocalRef::selfVariable(), builder, irctx, rubyRegionId), - Payload::idIntern(cs, builder, name), cache}); - } - } - } - if (irctx.escapedVariableIndices.contains(local)) { - auto *cfp = getCFPForBlock(cs, builder, irctx, rubyRegionId); - auto info = escapedVariableInfo(cs, local, irctx, rubyRegionId); - auto *value = builder.CreateCall(cs.getFunction("sorbet_readLocal"), {cfp, info.index, info.level}); - // If we ever wrote to this local, we cannot guarantee that the type has - // been checked prior to the access from the locals array. - if (info.use.used == LocalUsedHow::WrittenTo) { - return value; - } - - core::ClassOrModuleRef klass = hasOptimizedTest(info.use.type); - if (!klass.exists()) { - return value; - } - - Payload::assumeType(cs, builder, value, klass); - - return value; - } - - // normal local variable - return Payload::unboxRawValue(cs, builder, irctx.llvmVariables.at(local)); -} - -void Payload::varSet(CompilerState &cs, cfg::LocalRef local, llvm::Value *var, llvm::IRBuilderBase &builder, - const IREmitterContext &irctx, int rubyRegionId) { - if (local == cfg::LocalRef::selfVariable()) { - return Payload::boxRawValue(cs, builder, irctx.selfVariables.at(rubyRegionId), var); - } - if (irctx.aliases.contains(local)) { - // alias to a field or constant - auto alias = irctx.aliases.at(local); - switch (alias.kind) { - case Alias::AliasKind::Constant: { - auto sym = alias.constantSym; - auto name = sym.name(cs.gs).show(cs.gs); - auto owner = sym.owner(cs.gs); - builder.CreateCall(cs.getFunction("sorbet_setConstant"), - {Payload::getRubyConstant(cs, owner, builder), Payload::toCString(cs, name, builder), - llvm::ConstantInt::get(cs, llvm::APInt(64, name.length())), var}); - } break; - case Alias::AliasKind::GlobalField: - builder.CreateCall(cs.getFunction("sorbet_globalVariableSet"), - {Payload::idIntern(cs, builder, alias.globalField.shortName(cs)), var}); - break; - case Alias::AliasKind::ClassField: - builder.CreateCall(cs.getFunction("sorbet_classVariableSet"), - {getClassVariableStoreClass(cs, builder, irctx), - Payload::idIntern(cs, builder, alias.classField.shortName(cs)), var}); - break; - case Alias::AliasKind::InstanceField: { - // Each instance variable reference receives its own cache; this - // is the easiest thing to do and is identical to what the VM does. - // - // TODO: attempt to create caches shared across variable references - // for each instance variable for a given class. This is different - // than what the VM does, but it ought to be a small size/performance - // win. This change would require Sorbet changes so we have the - // necessary information at this point. - auto name = alias.instanceField.shortName(cs); - auto *cache = buildInstanceVariableCache(cs, name); - builder.CreateCall(cs.getFunction("sorbet_instanceVariableSet"), - {Payload::varGet(cs, cfg::LocalRef::selfVariable(), builder, irctx, rubyRegionId), - Payload::idIntern(cs, builder, name), var, cache}); - break; - } - } - return; - } - if (irctx.escapedVariableIndices.contains(local)) { - auto *cfp = getCFPForBlock(cs, builder, irctx, rubyRegionId); - auto info = escapedVariableInfo(cs, local, irctx, rubyRegionId); - builder.CreateCall(cs.getFunction("sorbet_writeLocal"), {cfp, info.index, info.level, var}); - return; - } - - // normal local variable - Payload::boxRawValue(cs, builder, irctx.llvmVariables.at(local), var); -} - -void Payload::rubyStopInDebugger(CompilerState &cs, llvm::IRBuilderBase &builder) { - builder.CreateCall(cs.getFunction("sorbet_stopInDebugger"), {}); -} - -void Payload::dbg_p(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *val) { - builder.CreateCall(cs.getFunction("sorbet_dbg_p"), {val}); -} - -void Payload::pushRubyStackVector(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *cfp, llvm::Value *recv, - const std::vector &stack) { - auto *sorbetPush = cs.getFunction("sorbet_pushValueStack"); - - auto *spPtr = builder.CreateCall(cs.getFunction("sorbet_get_sp"), {cfp}); - auto spPtrType = llvm::dyn_cast(spPtr->getType()); - llvm::Value *sp = builder.CreateLoad(spPtrType->getElementType(), spPtr); - sp = builder.CreateCall(sorbetPush, {sp, recv}); - for (auto *arg : stack) { - sp = builder.CreateCall(sorbetPush, {sp, arg}); - } - builder.CreateStore(sp, spPtr); -} - -llvm::Value *Payload::vmBlockHandlerNone(CompilerState &cs, llvm::IRBuilderBase &builder) { - return builder.CreateCall(cs.getFunction("sorbet_vmBlockHandlerNone"), {}, "VM_BLOCK_HANDLER_NONE"); -} - -llvm::Value *Payload::makeBlockHandlerProc(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *block) { - return builder.CreateCall(cs.getFunction("sorbet_makeBlockHandlerProc"), {block}, "blockHandlerProc"); -} - -llvm::Value *Payload::getPassedBlockHandler(CompilerState &cs, llvm::IRBuilderBase &builder) { - return builder.CreateCall(cs.getFunction("sorbet_getPassedBlockHandler"), {}, "passedBlockHandler"); -} - -llvm::Value *Payload::callFuncWithCache(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *cache, - llvm::Value *blockHandler) { - return builder.CreateCall(cs.getFunction("sorbet_callFuncWithCache"), {cache, blockHandler}, "send"); -} - -llvm::Value *Payload::callFuncBlockWithCache(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *cache, - bool usesBreak, llvm::Value *ifunc) { - if (usesBreak) { - return builder.CreateCall(cs.getFunction("sorbet_callFuncBlockWithCache"), {cache, ifunc}, "sendWithBlock"); - } else { - return builder.CreateCall(cs.getFunction("sorbet_callFuncBlockWithCache_noBreak"), {cache, ifunc}, - "sendWithBlock"); - } -} - -llvm::Value *Payload::callSuperFuncWithCache(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *cache, - llvm::Value *blockHandler) { - return builder.CreateCall(cs.getFunction("sorbet_callSuperFuncWithCache"), {cache, blockHandler}, "send"); -} - -llvm::Value *Payload::callSuperFuncBlockWithCache(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *cache, - bool usesBreak, llvm::Value *ifunc) { - if (usesBreak) { - return builder.CreateCall(cs.getFunction("sorbet_callSuperFuncBlockWithCache"), {cache, ifunc}, - "sendWithBlock"); - } else { - return builder.CreateCall(cs.getFunction("sorbet_callSuperFuncBlockWithCache_noBreak"), {cache, ifunc}, - "sendWithBlock"); - } -} - -llvm::Value *Payload::callFuncDirect(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *cache, - llvm::Value *fn, llvm::Value *argc, llvm::Value *argv, llvm::Value *recv, - llvm::Value *iseq) { - return builder.CreateCall(cs.getFunction("sorbet_callFuncDirect"), {cache, fn, argc, argv, recv, iseq}, - "sendDirect"); -} - -void Payload::afterIntrinsic(CompilerState &cs, llvm::IRBuilderBase &builder) { - builder.CreateCall(cs.getFunction("sorbet_afterIntrinsic"), {}); -} - -llvm::Value *Payload::getCFPForBlock(CompilerState &cs, llvm::IRBuilderBase &builder, const IREmitterContext &irctx, - int rubyRegionId) { - switch (irctx.rubyBlockType[rubyRegionId]) { - case FunctionType::Method: - case FunctionType::StaticInitModule: - case FunctionType::StaticInitFile: - return irctx.rubyBlocks2Functions[rubyRegionId]->arg_begin() + 3; - case FunctionType::Block: - return builder.CreateLoad(irctx.blockControlFramePtrs.at(rubyRegionId), "cached_cfp"); - case FunctionType::ExceptionBegin: - return irctx.rubyBlocks2Functions[rubyRegionId]->arg_begin() + 2; - case FunctionType::Rescue: - case FunctionType::Ensure: - case FunctionType::Unused: - return builder.CreateCall(cs.getFunction("sorbet_getCFP"), {}, "cfp"); - } -} - -// Currently this function is an historical artifact. Prior to https://github.com/stripe/sorbet_llvm/pull/353, it was -// used to compute an offset into a global array that was used to hold locals for all method and class static-init -// functions. We now push stack frames for these functions to track the locals. Computing such an offset into the -// locals may become useful for other reasons in the future, however, so we keep the offsets and this function around, -// even though for now the offset is always zero. -llvm::Value *Payload::buildLocalsOffset(CompilerState &cs) { - return llvm::ConstantInt::get(cs, llvm::APInt(64, 0, true)); -} - -llvm::Value *Payload::getOrBuildBlockIfunc(CompilerState &cs, llvm::IRBuilderBase &builder, - const IREmitterContext &irctx, int blkId) { - auto *blk = irctx.rubyBlocks2Functions[blkId]; - auto &arity = irctx.rubyBlockArity[blkId]; - auto rawName = fmt::format("{}_ifunc", (string)blk->getName()); - - auto *globalTy = llvm::Type::getInt64Ty(cs); - - auto *global = cs.module->getOrInsertGlobal(rawName, globalTy, [&cs, &blk, &rawName, &globalTy, &arity]() { - auto globalInitBuilder = llvm::IRBuilder<>(cs); - - auto isConstant = false; - auto *zero = llvm::ConstantInt::get(cs, llvm::APInt(64, 0)); - auto global = new llvm::GlobalVariable(*cs.module, globalTy, isConstant, llvm::GlobalVariable::InternalLinkage, - zero, rawName); - - globalInitBuilder.SetInsertPoint(cs.globalConstructorsEntry); - - auto *blkMinArgs = IREmitterHelpers::buildS4(cs, arity.min); - auto *blkMaxArgs = IREmitterHelpers::buildS4(cs, arity.max); - auto *offset = buildLocalsOffset(cs); - auto *ifunc = globalInitBuilder.CreateCall(cs.getFunction("sorbet_buildBlockIfunc"), - {blk, blkMinArgs, blkMaxArgs, offset}); - auto *asValue = globalInitBuilder.CreateBitOrPointerCast(ifunc, globalTy); - auto *globalIndex = globalInitBuilder.CreateCall(cs.getFunction("sorbet_globalConstRegister"), {asValue}); - globalInitBuilder.CreateStore(globalIndex, global); - - return global; - }); - - auto *globalIndex = builder.CreateLoad(global); - return builder.CreateCall(cs.getFunction("sorbet_globalConstFetchIfunc"), {globalIndex}); -} - -} // namespace sorbet::compiler diff --git a/compiler/IREmitter/Payload.h b/compiler/IREmitter/Payload.h deleted file mode 100644 index 8c8ff2b19f..0000000000 --- a/compiler/IREmitter/Payload.h +++ /dev/null @@ -1,138 +0,0 @@ -#ifndef SORBET_COMPILER_PAYLOAD_H -#define SORBET_COMPILER_PAYLOAD_H - -#include "cfg/LocalRef.h" -#include "compiler/Core/ForwardDeclarations.h" -#include "core/core.h" - -namespace sorbet::compiler { - -struct IREmitterContext; -struct Alias; -struct EscapedUse; -class CompilerState; - -struct EscapedVariableInfo { - const EscapedUse &use; - // The index of the variable within the frame. - llvm::Value *index; - // The number of frames to traverse. - llvm::Value *level; -}; - -// This class serves as forwarder to payload.c, which are the c wrappers for -// Ruby functions. These functions can (and do) use information known during -// compile time to dispatch to different c functions, but other than that, they -// should mostly be forwarders. -class Payload { -public: - // api for payload debugging utilities - static void rubyStopInDebugger(CompilerState &cs, llvm::IRBuilderBase &builder); - static void dbg_p(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *val); - - // api for actual code emission - static llvm::Value *idIntern(CompilerState &cs, llvm::IRBuilderBase &builder, std::string_view idName); - static llvm::Value *setExpectedBool(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *boolean, - bool expected); - // boxed raw value from rawData into target. Assumes that types are compatible. - static void boxRawValue(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::AllocaInst *storeTarget, - llvm::Value *rawData); - static llvm::Value *unboxRawValue(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::AllocaInst *storeTarget); - - static llvm::Value *rubyUndef(CompilerState &cs, llvm::IRBuilderBase &builder); - static llvm::Value *rubyNil(CompilerState &cs, llvm::IRBuilderBase &builder); - static llvm::Value *rubyFalse(CompilerState &cs, llvm::IRBuilderBase &builder); - static llvm::Value *rubyTrue(CompilerState &cs, llvm::IRBuilderBase &builder); - static llvm::Value *rubyTopSelf(CompilerState &cs, llvm::IRBuilderBase &builder); - static void raiseArity(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *currentArgCount, int minArgs, - int maxArgs); - static llvm::Value *longToRubyValue(CompilerState &cs, llvm::IRBuilderBase &builder, long num); - static llvm::Value *doubleToRubyValue(CompilerState &cs, llvm::IRBuilderBase &builder, double num); - static llvm::Value *cPtrToRubyString(CompilerState &cs, llvm::IRBuilderBase &builder, std::string_view str, - bool frozen); - static llvm::Value *cPtrToRubyRegexp(CompilerState &cs, llvm::IRBuilderBase &builder, std::string_view str, - int options); - static llvm::Value *testIsUndef(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *val); - static llvm::Value *testIsTruthy(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *val); - static llvm::Value *getRubyConstant(CompilerState &cs, core::SymbolRef sym, llvm::IRBuilderBase &builder); - static llvm::Value *toCString(CompilerState &cs, std::string_view str, llvm::IRBuilderBase &builder); - - static llvm::Value *typeTest(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *val, - core::ClassOrModuleRef sym); - static llvm::Value *typeTest(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *val, - const core::TypePtr &type); - - static void assumeType(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *val, - core::ClassOrModuleRef sym); - static void assumeType(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *val, - const core::TypePtr &type); - - static llvm::Value *boolToRuby(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *uint8_t); - static llvm::Value *setRubyStackFrame(CompilerState &cs, llvm::IRBuilderBase &builder, - const IREmitterContext &irctx, const ast::MethodDef &md, int rubyRegionId); - - static llvm::Value *readKWRestArg(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *maybeHash); - static llvm::Value *assertNoExtraKWArg(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *maybeHash, - llvm::Value *numRequired, llvm::Value *optionalParsed); - static llvm::Value *assertAllRequiredKWArgs(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *missing); - static llvm::Value *addMissingKWArg(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *missing, - llvm::Value *sym); - - static llvm::Value *getKWArg(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *maybeHash, - llvm::Value *rubySym); - static llvm::Value *removeKWArg(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *maybeHash, - llvm::Value *rubySym); - static llvm::Value *readRestArgs(CompilerState &cs, llvm::IRBuilderBase &builder, int maxPositionalArgCount, - llvm::Value *argCountRaw, llvm::Value *argArrayRaw); - static core::Loc setLineNumber(CompilerState &cs, llvm::IRBuilderBase &builder, core::Loc loc, - core::Loc methodStart, core::Loc lastLoc, llvm::AllocaInst *lineNumberPtr); - static llvm::Value *buildInstanceVariableCache(CompilerState &cs, std::string_view name); - static llvm::Value *getClassVariableStoreClass(CompilerState &cs, llvm::IRBuilderBase &builder, - const IREmitterContext &irctx); - static llvm::Value *varGet(CompilerState &cs, cfg::LocalRef local, llvm::IRBuilderBase &builder, - const IREmitterContext &irctx, int rubyRegionId); - static void varSet(CompilerState &cs, cfg::LocalRef local, llvm::Value *var, llvm::IRBuilderBase &builder, - const IREmitterContext &irctx, int rubyRegionId); - - static EscapedVariableInfo escapedVariableInfo(CompilerState &cs, cfg::LocalRef local, - const IREmitterContext &irctx, int rubyRegionId); - - static llvm::Value *retrySingleton(CompilerState &cs, llvm::IRBuilderBase &builder, const IREmitterContext &irctx); - static llvm::Value *voidSingleton(CompilerState &cs, llvm::IRBuilderBase &builder, const IREmitterContext &irctx); - - static void pushRubyStackVector(CompilerState &cs, llvm::IRBuilderBase &build, llvm::Value *cfp, llvm::Value *recv, - const std::vector &stack); - - static llvm::Value *vmBlockHandlerNone(CompilerState &cs, llvm::IRBuilderBase &builder); - static llvm::Value *makeBlockHandlerProc(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *block); - static llvm::Value *getPassedBlockHandler(CompilerState &cs, llvm::IRBuilderBase &builder); - - static llvm::Value *callFuncWithCache(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *cache, - llvm::Value *blockHandler); - static llvm::Value *callFuncBlockWithCache(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *cache, - bool usesBreak, llvm::Value *ifunc); - static llvm::Value *callSuperFuncWithCache(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *cache, - llvm::Value *blockHandler); - static llvm::Value *callSuperFuncBlockWithCache(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *cache, - bool usesBreak, llvm::Value *ifunc); - static llvm::Value *callFuncDirect(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *cache, - llvm::Value *fn, llvm::Value *argc, llvm::Value *argv, llvm::Value *recv, - llvm::Value *iseq); - static void afterIntrinsic(CompilerState &cs, llvm::IRBuilderBase &builder); - - static llvm::Value *rubyStackFrameVar(CompilerState &cs, llvm::IRBuilderBase &builder, - const IREmitterContext &irctx, core::MethodRef methodSym); - - static llvm::Value *getFileLineNumberInfo(CompilerState &gs, llvm::IRBuilderBase &builder, core::FileRef file); - static llvm::Value *getIseqEncodedPointer(CompilerState &gs, llvm::IRBuilderBase &builder, core::FileRef file); - - static llvm::Value *getCFPForBlock(CompilerState &cs, llvm::IRBuilderBase &builder, const IREmitterContext &irctx, - int rubyRegionId); - - static llvm::Value *buildLocalsOffset(CompilerState &cs); - - static llvm::Value *getOrBuildBlockIfunc(CompilerState &cs, llvm::IRBuilderBase &builder, - const IREmitterContext &irctx, int blkId); -}; -} // namespace sorbet::compiler -#endif diff --git a/compiler/IREmitter/Payload/BUILD b/compiler/IREmitter/Payload/BUILD deleted file mode 100644 index 7f347fc269..0000000000 --- a/compiler/IREmitter/Payload/BUILD +++ /dev/null @@ -1,167 +0,0 @@ -cc_library( - name = "Payload", - srcs = glob([ - "*.cc", - "*.h", - ]) + [ - "Payload_gen.cc", - ], - linkstatic = select({ - "@com_stripe_ruby_typer//tools/config:linkshared": 0, - "//conditions:default": 1, - }), - visibility = ["//visibility:public"], - deps = [ - "//compiler/Core", - ], -) - -load("@com_stripe_ruby_typer//tools:clang.bzl", "clang_tool") # todo: this should be decoupled and use the library toolchain, not the compiler one - -clang_tool("llvm-as") - -clang_tool("llvm-dis") - -clang_tool("llvm-ar") - -clang_tool("llvm-link") - -cc_binary( - name = "generate_load_payload", - srcs = [ - "tools/generate_load_payload.cc", - ], - visibility = ["//tools:__pkg__"], - deps = [ - "@com_stripe_ruby_typer//common", - ], -) - -filegroup( - name = "vm_payload_srcs", - srcs = [ - "PayloadIntrinsics.c", - "serialize-runtime.c", - "sorbet-t-modules.c", - "vm-payload.c", - ], - visibility = ["//visibility:public"], -) - -payload_files = [ - ( - "vm_payload_for_compile_commands", - ["vm-payload.c"], - ), - ( - "pseudo_lib", - [ - "PayloadIntrinsics.c", - "codegen-payload.c", - ], - ), -] - -[cc_library( - name = name, - srcs = srcs, - copts = [ - "-Wno-gcc-compat", - "-fembed-bitcode", - # Note that we use -std=gnu99 here (instead of -std=c99) because we - # borrow a lot of code from Ruby (via, e.g., internal.h), and that code - # sometimes depends on GNU extensions (e.g. the "typeof" keyword). - "-std=gnu99", - "-O1", - "-g", - "-fPIC", - "-march=x86-64", - - # The definition of id_intern in ruby.h triggers this warning, and it's - # pretty noisy. - "-Wno-compound-token-split-by-macro", - ], - # disable opt flags, as they add `-ffunction-sections` and `-fdata-sections` - # See https://github.com/bazelbuild/bazel/issues/8706 for why `nocopts` is gone - features = ["-opt"], - linkstatic = 1, # we are doing this only for llvm ir - visibility = ["//visibility:public"], - deps = [ - "@com_stripe_ruby_typer//sorbet_version", - "@sorbet_ruby_2_7_for_compiler//:headers", - "@sorbet_ruby_2_7_for_compiler//:headers-internal", - ], -) for name, srcs in payload_files] - -cc_binary( - name = "postprocess_payload", - srcs = ["tools/postprocess_payload.cc"], - visibility = ["//visibility:public"], - deps = [ - "@llvm//:BitWriter", - "@llvm//:Core", - "@llvm//:IRReader", - "@llvm//:Support", - ], -) - -sh_binary( - name = "generate_payload_bc_orig_generator", - srcs = ["tools/generate_payload_bc_orig_generator.sh"], -) - -genrule( - name = "generate_payload_bc_orig", - outs = [ - "payload.bc.orig", - ], - cmd = "$(location :generate_payload_bc_orig_generator) $(location :llvm-ar) $(location :llvm-link) $(location payload.bc.orig) $(location //third_party/xxd) $(locations :pseudo_lib)", - tools = [ - ":generate_payload_bc_orig_generator", - ":llvm-ar", - ":llvm-link", - ":pseudo_lib", - "//third_party/xxd", - ], - visibility = ["//visibility:public"], -) - -genrule( - name = "generate_payload_bc", - srcs = [ - "payload.bc.orig", - ], - outs = [ - "payload.bc", - ], - cmd = "$(location :postprocess_payload) $(location payload.bc.orig) $(location payload.bc)", - tools = [ - ":postprocess_payload", - ], -) - -genrule( - name = "generate_payload_ll", - srcs = [ - "payload.bc", - ], - outs = [ - "payload.ll", - ], - cmd = "$(location :llvm-dis) $(location payload.bc) -o $(location payload.ll)", - tools = [ - ":llvm-dis", - ], -) - -genrule( - name = "generate_payload_gen_cc", - srcs = ["payload.bc"], - outs = [ - "Payload_gen.cc", - ], - cmd = "$(location :generate_load_payload) $(location payload.bc) $(location Payload_gen.cc)", - tools = [ - ":generate_load_payload", - ], -) diff --git a/compiler/IREmitter/Payload/PayloadIntrinsics.c b/compiler/IREmitter/Payload/PayloadIntrinsics.c deleted file mode 100644 index 57be570c97..0000000000 --- a/compiler/IREmitter/Payload/PayloadIntrinsics.c +++ /dev/null @@ -1,758 +0,0 @@ -#ifndef SORBET_COMPILER_IMPORTED_INTRINSICS_H -#define SORBET_COMPILER_IMPORTED_INTRINSICS_H - -// This file is autogenerated. Do not edit it by hand. Regenerate it with: -// cd compiler/IREmitter/Intrinsics && make - -#include "ruby.h" - -typedef VALUE (*BlockFFIType)(VALUE firstYieldedArg, VALUE closure, int argCount, const VALUE *args, VALUE blockArg); - -// Array#+ -// Calling convention: 1 -extern VALUE rb_ary_plus(VALUE obj, VALUE arg_0); - -VALUE sorbet_int_rb_ary_plus(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 1, 1); - VALUE arg_0 = args[0]; - return rb_ary_plus(recv, arg_0); -} - -// Array#- -// Calling convention: 1 -extern VALUE sorbet_rb_ary_diff(VALUE obj, VALUE arg_0); - -VALUE sorbet_int_rb_ary_diff(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 1, 1); - VALUE arg_0 = args[0]; - return sorbet_rb_ary_diff(recv, arg_0); -} - -// Array#<< -// Calling convention: 1 -extern VALUE rb_ary_push(VALUE obj, VALUE arg_0); - -VALUE sorbet_int_rb_ary_push(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 1, 1); - VALUE arg_0 = args[0]; - return rb_ary_push(recv, arg_0); -} - -// Array#<=> -// Calling convention: 1 -extern VALUE rb_ary_cmp(VALUE obj, VALUE arg_0); - -VALUE sorbet_int_rb_ary_cmp(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, VALUE closure) { - rb_check_arity(argc, 1, 1); - VALUE arg_0 = args[0]; - return rb_ary_cmp(recv, arg_0); -} - -// Array#[] -// Array#slice -// Calling convention: -1 -extern VALUE rb_ary_aref(int argc, const VALUE *args, VALUE obj); - -VALUE sorbet_int_rb_ary_aref(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - return rb_ary_aref(argc, args, recv); -} - -// Array#assoc -// Calling convention: 1 -extern VALUE rb_ary_assoc(VALUE obj, VALUE arg_0); - -VALUE sorbet_int_rb_ary_assoc(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 1, 1); - VALUE arg_0 = args[0]; - return rb_ary_assoc(recv, arg_0); -} - -// Array#at -// Calling convention: 1 -extern VALUE rb_ary_at(VALUE obj, VALUE arg_0); - -VALUE sorbet_int_rb_ary_at(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, VALUE closure) { - rb_check_arity(argc, 1, 1); - VALUE arg_0 = args[0]; - return rb_ary_at(recv, arg_0); -} - -// Array#clear -// Calling convention: 0 -extern VALUE rb_ary_clear(VALUE obj); - -VALUE sorbet_int_rb_ary_clear(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 0, 0); - return rb_ary_clear(recv); -} - -// Array#concat -// Calling convention: -1 -extern VALUE sorbet_rb_ary_concat_multi(int argc, const VALUE *args, VALUE obj); - -VALUE sorbet_int_rb_ary_concat_multi(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - return sorbet_rb_ary_concat_multi(argc, args, recv); -} - -// Array#delete -// Calling convention: 1 -extern VALUE rb_ary_delete(VALUE obj, VALUE arg_0); - -VALUE sorbet_int_rb_ary_delete(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 1, 1); - VALUE arg_0 = args[0]; - return rb_ary_delete(recv, arg_0); -} - -// Array#first -// Calling convention: -1 -extern VALUE sorbet_rb_ary_first(int argc, const VALUE *args, VALUE obj); - -VALUE sorbet_int_rb_ary_first(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - return sorbet_rb_ary_first(argc, args, recv); -} - -// Array#flatten -// Calling convention: -1 -extern VALUE sorbet_rb_ary_flatten(int argc, const VALUE *args, VALUE obj); - -VALUE sorbet_int_rb_ary_flatten(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - return sorbet_rb_ary_flatten(argc, args, recv); -} - -// Array#include? -// Calling convention: 1 -extern VALUE rb_ary_includes(VALUE obj, VALUE arg_0); - -VALUE sorbet_int_rb_ary_includes(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 1, 1); - VALUE arg_0 = args[0]; - return rb_ary_includes(recv, arg_0); -} - -// Array#initialize_copy -// Array#replace -// Calling convention: 1 -extern VALUE rb_ary_replace(VALUE obj, VALUE arg_0); - -VALUE sorbet_int_rb_ary_replace(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 1, 1); - VALUE arg_0 = args[0]; - return rb_ary_replace(recv, arg_0); -} - -// Array#join -// Calling convention: -1 -extern VALUE sorbet_rb_ary_join_m(int argc, const VALUE *args, VALUE obj); - -VALUE sorbet_int_rb_ary_join_m(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - return sorbet_rb_ary_join_m(argc, args, recv); -} - -// Array#last -// Calling convention: -1 -extern VALUE rb_ary_last(int argc, const VALUE *args, VALUE obj); - -VALUE sorbet_int_rb_ary_last(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - return rb_ary_last(argc, args, recv); -} - -// Array#push -// Calling convention: -1 -extern VALUE sorbet_rb_ary_push_m(int argc, const VALUE *args, VALUE obj); - -VALUE sorbet_int_rb_ary_push_m(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - return sorbet_rb_ary_push_m(argc, args, recv); -} - -// Array#rassoc -// Calling convention: 1 -extern VALUE rb_ary_rassoc(VALUE obj, VALUE arg_0); - -VALUE sorbet_int_rb_ary_rassoc(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 1, 1); - VALUE arg_0 = args[0]; - return rb_ary_rassoc(recv, arg_0); -} - -// Array#sort -// Calling convention: 0 -extern VALUE rb_ary_sort(VALUE obj); - -VALUE sorbet_int_rb_ary_sort(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 0, 0); - return rb_ary_sort(recv); -} - -// Array#sort! -// Calling convention: 0 -extern VALUE rb_ary_sort_bang(VALUE obj); - -VALUE sorbet_int_rb_ary_sort_bang(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 0, 0); - return rb_ary_sort_bang(recv); -} - -// Float#* -// Calling convention: 1 -extern VALUE rb_float_mul(VALUE obj, VALUE arg_0); - -VALUE sorbet_int_rb_float_mul(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 1, 1); - VALUE arg_0 = args[0]; - return rb_float_mul(recv, arg_0); -} - -// Float#** -// Calling convention: 1 -extern VALUE rb_float_pow(VALUE obj, VALUE arg_0); - -VALUE sorbet_int_rb_float_pow(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 1, 1); - VALUE arg_0 = args[0]; - return rb_float_pow(recv, arg_0); -} - -// Float#+ -// Calling convention: 1 -extern VALUE rb_float_plus(VALUE obj, VALUE arg_0); - -VALUE sorbet_int_rb_float_plus(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 1, 1); - VALUE arg_0 = args[0]; - return rb_float_plus(recv, arg_0); -} - -// Float#-@ -// Calling convention: 0 -extern VALUE rb_float_uminus(VALUE obj); - -VALUE sorbet_int_rb_float_uminus(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 0, 0); - return rb_float_uminus(recv); -} - -// Float#< -// Calling convention: 1 -extern VALUE sorbet_flo_lt(VALUE obj, VALUE arg_0); - -VALUE sorbet_int_flo_lt(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, VALUE closure) { - rb_check_arity(argc, 1, 1); - VALUE arg_0 = args[0]; - return sorbet_flo_lt(recv, arg_0); -} - -// Float#<= -// Calling convention: 1 -extern VALUE sorbet_flo_le(VALUE obj, VALUE arg_0); - -VALUE sorbet_int_flo_le(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, VALUE closure) { - rb_check_arity(argc, 1, 1); - VALUE arg_0 = args[0]; - return sorbet_flo_le(recv, arg_0); -} - -// Float#> -// Calling convention: 1 -extern VALUE rb_float_gt(VALUE obj, VALUE arg_0); - -VALUE sorbet_int_rb_float_gt(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 1, 1); - VALUE arg_0 = args[0]; - return rb_float_gt(recv, arg_0); -} - -// Float#>= -// Calling convention: 1 -extern VALUE sorbet_flo_ge(VALUE obj, VALUE arg_0); - -VALUE sorbet_int_flo_ge(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, VALUE closure) { - rb_check_arity(argc, 1, 1); - VALUE arg_0 = args[0]; - return sorbet_flo_ge(recv, arg_0); -} - -// Float#abs -// Float#magnitude -// Calling convention: 0 -extern VALUE rb_float_abs(VALUE obj); - -VALUE sorbet_int_rb_float_abs(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 0, 0); - return rb_float_abs(recv); -} - -// Float#finite? -// Calling convention: 0 -extern VALUE rb_flo_is_finite_p(VALUE obj); - -VALUE sorbet_int_rb_flo_is_finite_p(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 0, 0); - return rb_flo_is_finite_p(recv); -} - -// Float#infinite? -// Calling convention: 0 -extern VALUE rb_flo_is_infinite_p(VALUE obj); - -VALUE sorbet_int_rb_flo_is_infinite_p(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 0, 0); - return rb_flo_is_infinite_p(recv); -} - -// Hash#dig -// Calling convention: -1 -extern VALUE sorbet_rb_hash_dig(int argc, const VALUE *args, VALUE obj); - -VALUE sorbet_int_rb_hash_dig(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - return sorbet_rb_hash_dig(argc, args, recv); -} - -// Hash#include? -// Hash#member? -// Hash#has_key? -// Hash#key? -// Calling convention: 1 -extern VALUE rb_hash_has_key(VALUE obj, VALUE arg_0); - -VALUE sorbet_int_rb_hash_has_key(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 1, 1); - VALUE arg_0 = args[0]; - return rb_hash_has_key(recv, arg_0); -} - -// Integer#% -// Integer#modulo -// Calling convention: 1 -extern VALUE rb_int_modulo(VALUE obj, VALUE arg_0); - -VALUE sorbet_int_rb_int_modulo(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 1, 1); - VALUE arg_0 = args[0]; - return rb_int_modulo(recv, arg_0); -} - -// Integer#& -// Calling convention: 1 -extern VALUE rb_int_and(VALUE obj, VALUE arg_0); - -VALUE sorbet_int_rb_int_and(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, VALUE closure) { - rb_check_arity(argc, 1, 1); - VALUE arg_0 = args[0]; - return rb_int_and(recv, arg_0); -} - -// Integer#* -// Calling convention: 1 -extern VALUE rb_int_mul(VALUE obj, VALUE arg_0); - -VALUE sorbet_int_rb_int_mul(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, VALUE closure) { - rb_check_arity(argc, 1, 1); - VALUE arg_0 = args[0]; - return rb_int_mul(recv, arg_0); -} - -// Integer#** -// Calling convention: 1 -extern VALUE rb_int_pow(VALUE obj, VALUE arg_0); - -VALUE sorbet_int_rb_int_pow(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, VALUE closure) { - rb_check_arity(argc, 1, 1); - VALUE arg_0 = args[0]; - return rb_int_pow(recv, arg_0); -} - -// Integer#+ -// Calling convention: 1 -extern VALUE rb_int_plus(VALUE obj, VALUE arg_0); - -VALUE sorbet_int_rb_int_plus(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 1, 1); - VALUE arg_0 = args[0]; - return rb_int_plus(recv, arg_0); -} - -// Integer#- -// Calling convention: 1 -extern VALUE rb_int_minus(VALUE obj, VALUE arg_0); - -VALUE sorbet_int_rb_int_minus(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 1, 1); - VALUE arg_0 = args[0]; - return rb_int_minus(recv, arg_0); -} - -// Integer#-@ -// Calling convention: 0 -extern VALUE rb_int_uminus(VALUE obj); - -VALUE sorbet_int_rb_int_uminus(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 0, 0); - return rb_int_uminus(recv); -} - -// Integer#/ -// Calling convention: 1 -extern VALUE rb_int_div(VALUE obj, VALUE arg_0); - -VALUE sorbet_int_rb_int_div(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, VALUE closure) { - rb_check_arity(argc, 1, 1); - VALUE arg_0 = args[0]; - return rb_int_div(recv, arg_0); -} - -// Integer#<< -// Calling convention: 1 -extern VALUE rb_int_lshift(VALUE obj, VALUE arg_0); - -VALUE sorbet_int_rb_int_lshift(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 1, 1); - VALUE arg_0 = args[0]; - return rb_int_lshift(recv, arg_0); -} - -// Integer#<=> -// Calling convention: 1 -extern VALUE rb_int_cmp(VALUE obj, VALUE arg_0); - -VALUE sorbet_int_rb_int_cmp(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, VALUE closure) { - rb_check_arity(argc, 1, 1); - VALUE arg_0 = args[0]; - return rb_int_cmp(recv, arg_0); -} - -// Integer#=== -// Integer#== -// Calling convention: 1 -extern VALUE rb_int_equal(VALUE obj, VALUE arg_0); - -VALUE sorbet_int_rb_int_equal(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 1, 1); - VALUE arg_0 = args[0]; - return rb_int_equal(recv, arg_0); -} - -// Integer#> -// Calling convention: 1 -extern VALUE rb_int_gt(VALUE obj, VALUE arg_0); - -VALUE sorbet_int_rb_int_gt(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, VALUE closure) { - rb_check_arity(argc, 1, 1); - VALUE arg_0 = args[0]; - return rb_int_gt(recv, arg_0); -} - -// Integer#>= -// Calling convention: 1 -extern VALUE rb_int_ge(VALUE obj, VALUE arg_0); - -VALUE sorbet_int_rb_int_ge(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, VALUE closure) { - rb_check_arity(argc, 1, 1); - VALUE arg_0 = args[0]; - return rb_int_ge(recv, arg_0); -} - -// Integer#abs -// Integer#magnitude -// Calling convention: 0 -extern VALUE rb_int_abs(VALUE obj); - -VALUE sorbet_int_rb_int_abs(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, VALUE closure) { - rb_check_arity(argc, 0, 0); - return rb_int_abs(recv); -} - -// Integer#div -// Calling convention: 1 -extern VALUE rb_int_idiv(VALUE obj, VALUE arg_0); - -VALUE sorbet_int_rb_int_idiv(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 1, 1); - VALUE arg_0 = args[0]; - return rb_int_idiv(recv, arg_0); -} - -// Integer#divmod -// Calling convention: 1 -extern VALUE rb_int_divmod(VALUE obj, VALUE arg_0); - -VALUE sorbet_int_rb_int_divmod(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 1, 1); - VALUE arg_0 = args[0]; - return rb_int_divmod(recv, arg_0); -} - -// Integer#fdiv -// Calling convention: 1 -extern VALUE rb_int_fdiv(VALUE obj, VALUE arg_0); - -VALUE sorbet_int_rb_int_fdiv(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 1, 1); - VALUE arg_0 = args[0]; - return rb_int_fdiv(recv, arg_0); -} - -// Integer#gcd -// Calling convention: 1 -extern VALUE rb_gcd(VALUE obj, VALUE arg_0); - -VALUE sorbet_int_rb_gcd(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, VALUE closure) { - rb_check_arity(argc, 1, 1); - VALUE arg_0 = args[0]; - return rb_gcd(recv, arg_0); -} - -// Integer#gcdlcm -// Calling convention: 1 -extern VALUE rb_gcdlcm(VALUE obj, VALUE arg_0); - -VALUE sorbet_int_rb_gcdlcm(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, VALUE closure) { - rb_check_arity(argc, 1, 1); - VALUE arg_0 = args[0]; - return rb_gcdlcm(recv, arg_0); -} - -// Integer#lcm -// Calling convention: 1 -extern VALUE rb_lcm(VALUE obj, VALUE arg_0); - -VALUE sorbet_int_rb_lcm(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, VALUE closure) { - rb_check_arity(argc, 1, 1); - VALUE arg_0 = args[0]; - return rb_lcm(recv, arg_0); -} - -// Integer#odd? -// Calling convention: 0 -extern VALUE rb_int_odd_p(VALUE obj); - -VALUE sorbet_int_rb_int_odd_p(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 0, 0); - return rb_int_odd_p(recv); -} - -// Integer#pow -// Calling convention: -1 -extern VALUE rb_int_powm(int const argc, VALUE *const argv, VALUE const num); - -VALUE sorbet_int_rb_int_powm(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - return rb_int_powm(argc, args, recv); -} - -// Integer#to_f -// Calling convention: 0 -extern VALUE sorbet_int_to_f(VALUE obj); - -VALUE sorbet_int_int_to_f(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, VALUE closure) { - rb_check_arity(argc, 0, 0); - return sorbet_int_to_f(recv); -} - -// Regexp#encoding -// String#encoding -// Calling convention: 0 -extern VALUE rb_obj_encoding(VALUE obj); - -VALUE sorbet_int_rb_obj_encoding(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 0, 0); - return rb_obj_encoding(recv); -} - -// String#* -// Calling convention: 1 -extern VALUE rb_str_times(VALUE obj, VALUE arg_0); - -VALUE sorbet_int_rb_str_times(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 1, 1); - VALUE arg_0 = args[0]; - return rb_str_times(recv, arg_0); -} - -// String#+ -// Calling convention: 1 -extern VALUE rb_str_plus(VALUE obj, VALUE arg_0); - -VALUE sorbet_int_rb_str_plus(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 1, 1); - VALUE arg_0 = args[0]; - return rb_str_plus(recv, arg_0); -} - -// String#<< -// Calling convention: 1 -extern VALUE rb_str_concat(VALUE obj, VALUE arg_0); - -VALUE sorbet_int_rb_str_concat(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 1, 1); - VALUE arg_0 = args[0]; - return rb_str_concat(recv, arg_0); -} - -// String#== -// String#=== -// Calling convention: 1 -extern VALUE rb_str_equal(VALUE obj, VALUE arg_0); - -VALUE sorbet_int_rb_str_equal(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 1, 1); - VALUE arg_0 = args[0]; - return rb_str_equal(recv, arg_0); -} - -// String#[] -// String#slice -// Calling convention: -1 -extern VALUE sorbet_rb_str_aref_m(int argc, const VALUE *args, VALUE obj); - -VALUE sorbet_int_rb_str_aref_m(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - return sorbet_rb_str_aref_m(argc, args, recv); -} - -// String#dump -// Calling convention: 0 -extern VALUE rb_str_dump(VALUE obj); - -VALUE sorbet_int_rb_str_dump(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 0, 0); - return rb_str_dump(recv); -} - -// String#eql? -// Calling convention: 1 -extern VALUE rb_str_eql(VALUE obj, VALUE arg_0); - -VALUE sorbet_int_rb_str_eql(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, VALUE closure) { - rb_check_arity(argc, 1, 1); - VALUE arg_0 = args[0]; - return rb_str_eql(recv, arg_0); -} - -// String#freeze -// Calling convention: 0 -extern VALUE rb_str_freeze(VALUE obj); - -VALUE sorbet_int_rb_str_freeze(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 0, 0); - return rb_str_freeze(recv); -} - -// String#initialize_copy -// String#replace -// Calling convention: 1 -extern VALUE rb_str_replace(VALUE obj, VALUE arg_0); - -VALUE sorbet_int_rb_str_replace(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 1, 1); - VALUE arg_0 = args[0]; - return rb_str_replace(recv, arg_0); -} - -// String#inspect -// Calling convention: 0 -extern VALUE rb_str_inspect(VALUE obj); - -VALUE sorbet_int_rb_str_inspect(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 0, 0); - return rb_str_inspect(recv); -} - -// String#intern -// String#to_sym -// Calling convention: 0 -extern VALUE rb_str_intern(VALUE obj); - -VALUE sorbet_int_rb_str_intern(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 0, 0); - return rb_str_intern(recv); -} - -// String#length -// String#size -// Calling convention: 0 -extern VALUE rb_str_length(VALUE obj); - -VALUE sorbet_int_rb_str_length(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 0, 0); - return rb_str_length(recv); -} - -// String#ord -// Calling convention: 0 -extern VALUE rb_str_ord(VALUE obj); - -VALUE sorbet_int_rb_str_ord(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, VALUE closure) { - rb_check_arity(argc, 0, 0); - return rb_str_ord(recv); -} - -// String#start_with? -// Calling convention: -1 -extern VALUE sorbet_rb_str_start_with(int argc, const VALUE *args, VALUE obj); - -VALUE sorbet_int_rb_str_start_with(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - return sorbet_rb_str_start_with(argc, args, recv); -} - -// String#succ -// String#next -// Calling convention: 0 -extern VALUE rb_str_succ(VALUE obj); - -VALUE sorbet_int_rb_str_succ(VALUE recv, ID fun, int argc, VALUE *const restrict args, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 0, 0); - return rb_str_succ(recv); -} -#endif /* SORBET_COMPILER_IMPORTED_INTRINSICS_H */ diff --git a/compiler/IREmitter/Payload/PayloadLoader.cc b/compiler/IREmitter/Payload/PayloadLoader.cc deleted file mode 100644 index 06595dc346..0000000000 --- a/compiler/IREmitter/Payload/PayloadLoader.cc +++ /dev/null @@ -1,30 +0,0 @@ -#include "llvm/Bitcode/BitcodeReader.h" -#include "llvm/IR/Module.h" -// ^^^ violate poisons -#include "PayloadLoader.h" -#include "compiler/Core/AbortCompilation.h" -#include "compiler/Errors/Errors.h" -#include "core/core.h" -#include - -using namespace std; -namespace sorbet::compiler { -string_view getDefaultModuleBitcode(); - -// If possible, plesase write attributes in C code. Unfortunately, not all attributes have C function equivalent -const vector> additionalFunctionAttributes = { - {"ruby_stack_check", llvm::Attribute::InaccessibleMemOnly}, -}; - -std::unique_ptr PayloadLoader::readDefaultModule(llvm::LLVMContext &lctx) { - auto bitCode = getDefaultModuleBitcode(); - llvm::StringRef bitcodeAsStringRef(bitCode.data(), bitCode.size()); - auto memoryBuffer = llvm::MemoryBuffer::getMemBuffer(bitcodeAsStringRef, "payload", false); - auto ret = llvm::parseBitcodeFile(*memoryBuffer, lctx); - for (const auto &[funName, attr] : additionalFunctionAttributes) { - ret.get()->getFunction(funName)->addFnAttr(attr); - } - return move(ret.get()); -} - -} // namespace sorbet::compiler diff --git a/compiler/IREmitter/Payload/PayloadLoader.h b/compiler/IREmitter/Payload/PayloadLoader.h deleted file mode 100644 index 8aa7c223e9..0000000000 --- a/compiler/IREmitter/Payload/PayloadLoader.h +++ /dev/null @@ -1,14 +0,0 @@ -#include "compiler/Core/ForwardDeclarations.h" -#include - -namespace sorbet::compiler { -class PayloadLoader { -public: - // When accumulating all the functions from file into a new module, we initialize the module with the payload. - // (Unfortunately, this means that the payload currently gets copied into every generated C extension.) - // - // Among other things, this means that we can do `cs.module->getFunction("...")` and look up the LLVM data - // structure corresponding to any C function written in the payload. - static std::unique_ptr readDefaultModule(llvm::LLVMContext &); -}; -} // namespace sorbet::compiler diff --git a/compiler/IREmitter/Payload/codegen-payload.c b/compiler/IREmitter/Payload/codegen-payload.c deleted file mode 100644 index 814ffc8afb..0000000000 --- a/compiler/IREmitter/Payload/codegen-payload.c +++ /dev/null @@ -1,2954 +0,0 @@ -#include "sorbet_version/sorbet_version.h" - -// These are public Ruby headers. Feel free to add more from the include/ruby -// directory -#include "ruby/encoding.h" // for rb_encoding - -// These are special "public" headers which don't live in include/ruby for some -// reason -#include "internal.h" -#include "ruby.h" - -// This is probably a bad idea but is needed for so many things -#include "vm_core.h" - -#define SORBET_ATTRIBUTE(...) __attribute__((__VA_ARGS__)) -#define SORBET_INLINE __attribute__((always_inline)) - -typedef VALUE (*BlockFFIType)(VALUE firstYieldedArg, VALUE closure, int argCount, const VALUE *args, VALUE blockArg); -typedef VALUE (*ExceptionFFIType)(VALUE **pc, VALUE closure, rb_control_frame_t *); -typedef VALUE (*BlockConsumerFFIType)(VALUE recv, ID fun, int argc, VALUE *argv, BlockFFIType blk, - const struct rb_captured_block *captured, VALUE closure, int numPositionalArgs); - -// compiler is closely aware of layout of this struct -struct FunctionInlineCache { - struct rb_kwarg_call_data cd; -}; - -struct sorbet_iterMethodArg { - VALUE recv; - ID func; - int argc; - const VALUE *argv; - int kw_splat; - struct FunctionInlineCache *cache; -}; - -struct SorbetLineNumberInfo { - int iseq_size; - struct iseq_insn_info_entry *insns_info; - VALUE *iseq_encoded; -}; - -// We want some constants from Ruby to be available for the compiler to use, but we -// can't declare them `static` in this file because LLVM will see that they are unused -// and delete them. So we declare them hidden, via this macro, which tricks LLVM into -// thinking that they will be used outside of this compilation unit (and serves as a -// marker of "constants we want to be available for the compiler to use"). Then -// post-processing fixes them up to be as if they were originally declared `static`. -#define SORBET_CONSTANT(type, name, value) const type __attribute__((visibility("hidden"))) name = value; - -// Define a function that uses a name, keeping it in scope. The payload post-processing step will detect these functions -// and keep them as externally visible, which will allow functions used with this macro to avoid being garbage collected -// The functions generated by this macro will be explicitly pruned late in the pipeline allowing functions mentioned to -// survive to the lowerings passes. -#define KEEP_ALIVE(name) \ - VALUE sorbet_exists_to_keep_alive_##name() __attribute__((optnone)) { \ - return (long)&name; \ - } - -// Functions known to the compiler. -// -// We have to be a little tricky here, as LLVM will eliminate declarations that are unused from the emitted IR. So we -// use the following macro to ensure that our declarations are "used"; the placeholder function will be eliminated by -// other optimizations. -#define SORBET_ALIVE(rettype, name, rest) \ - extern rettype name rest; \ - KEEP_ALIVE(name) - -SORBET_ALIVE(VALUE, rb_id2sym, (ID)SORBET_ATTRIBUTE(const)); -SORBET_ALIVE(VALUE, rb_errinfo, ()); -SORBET_ALIVE(VALUE, rb_obj_is_kind_of, (VALUE, VALUE) __attribute__((const))); - -SORBET_ALIVE(const char *, sorbet_dbg_p, (VALUE obj)); -SORBET_ALIVE(void, sorbet_stopInDebugger, (void)); - -SORBET_ALIVE(void, sorbet_cast_failure, - (VALUE value, char *castMethod, char *type) __attribute__((__cold__, __noreturn__))); -SORBET_ALIVE(void, sorbet_raiseArity, (int argc, int min, int max) __attribute__((__noreturn__))); -SORBET_ALIVE(void, sorbet_raiseMissingKeywords, (VALUE missing) __attribute__((__noreturn__))); -SORBET_ALIVE(void, sorbet_raiseExtraKeywords, (VALUE hash) __attribute__((__noreturn__))); -SORBET_ALIVE(void, sorbet_raiseCallDataExtraKeywords, (int keyword_len, VALUE *keywords) __attribute__((__noreturn__))); -SORBET_ALIVE(VALUE, sorbet_t_absurd, (VALUE val) __attribute__((__cold__))); - -SORBET_ALIVE(VALUE, sorbet_addMissingKWArg, (VALUE missing, VALUE sym)); - -SORBET_ALIVE(rb_iseq_t *, sorbet_allocateRubyStackFrame, - (VALUE funcName, ID func, VALUE filename, VALUE realpath, rb_iseq_t *parent, int iseqType, int startLine, - struct SorbetLineNumberInfo *info, ID *locals, int numLocals, int stackMax)); -SORBET_ALIVE(void, sorbet_initLineNumberInfo, (struct SorbetLineNumberInfo * info, VALUE *, int numLines)); -SORBET_ALIVE(VALUE, sorbet_getConstant, (const char *path, long pathLen)); -SORBET_ALIVE(VALUE, sorbet_setConstant, (VALUE mod, const char *name, long nameLen, VALUE value)); - -SORBET_ALIVE(const VALUE, sorbet_readRealpath, (void)); -SORBET_ALIVE(rb_control_frame_t *, sorbet_pushCfuncFrame, (struct FunctionInlineCache *, VALUE, const rb_iseq_t *)); -SORBET_ALIVE(rb_control_frame_t *, sorbet_pushStaticInitFrame, (VALUE)); -SORBET_ALIVE(void, sorbet_pushBlockFrame, (const struct rb_captured_block *)); -SORBET_ALIVE(void, sorbet_popFrame, (void)); - -SORBET_ALIVE(void, sorbet_vm_env_write_slowpath, (const VALUE *, int, VALUE)); -// Takes varargs of the IDs for keyword arguments, if any. -SORBET_ALIVE(void, sorbet_setupFunctionInlineCache, - (struct FunctionInlineCache * cache, ID mid, unsigned int flags, int argc, int num_kwargs, ...)); -SORBET_ALIVE(VALUE, sorbet_callFuncWithCache, (struct FunctionInlineCache * cache, VALUE bh)); -SORBET_ALIVE(VALUE, sorbet_callSuperFuncWithCache, (struct FunctionInlineCache * cache, VALUE bh)); -SORBET_ALIVE(void, sorbet_vmMethodSearch, (struct FunctionInlineCache * cache, VALUE recv)); -SORBET_ALIVE(VALUE, sorbet_getPassedBlockHandler, ()); - -SORBET_ALIVE(void, sorbet_setMethodStackFrame, - (rb_execution_context_t * ec, rb_control_frame_t *cfp, const rb_iseq_t *iseq)); -SORBET_ALIVE(void, sorbet_setExceptionStackFrame, - (rb_execution_context_t * ec, rb_control_frame_t *cfp, const rb_iseq_t *iseq)); - -SORBET_ALIVE(VALUE, sorbet_blockReturnUndef, (VALUE * *pc, VALUE closure, rb_control_frame_t *)); - -SORBET_ALIVE(VALUE, sorbet_vm_expandSplatIntrinsic, (VALUE thing, VALUE before, VALUE after)); -SORBET_ALIVE(VALUE, sorbet_vm_check_match_array, (rb_execution_context_t * ec, VALUE target, VALUE pattern)); -SORBET_ALIVE(VALUE, sorbet_vm_splatIntrinsic, (VALUE thing)); -SORBET_ALIVE(VALUE, sorbet_definedIntrinsic, - (VALUE recv, ID fun, int argc, const VALUE *const restrict, BlockFFIType blk, VALUE closure)); -SORBET_ALIVE(VALUE, sorbet_stringInterpolate, - (VALUE recv, ID fun, int argc, const VALUE *const restrict, BlockFFIType blk, VALUE closure)); - -SORBET_ALIVE(VALUE, sorbet_rb_array_square_br_slowpath, - (VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, VALUE closure)); -SORBET_ALIVE(VALUE, rb_ary_compact_bang_forwarder, (VALUE recv)); -SORBET_ALIVE(VALUE, sorbet_ary_make_hash, (VALUE ary)); -SORBET_ALIVE(void, sorbet_ary_recycle_hash, (VALUE hash)); -SORBET_ALIVE(VALUE, sorbet_rb_array_uniq, - (VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, VALUE closure)); -SORBET_ALIVE(void, sorbet_rb_ary_set_len, (VALUE ary, long n)); - -SORBET_ALIVE(void, sorbet_hashUpdate, (VALUE hash, VALUE other)); - -SORBET_ALIVE(VALUE, sorbet_rb_int_plus_slowpath, (VALUE, VALUE)); -SORBET_ALIVE(VALUE, sorbet_rb_int_minus_slowpath, (VALUE, VALUE)); - -SORBET_ALIVE(VALUE, sorbet_rb_int_lt_slowpath, (VALUE, VALUE)); -SORBET_ALIVE(VALUE, sorbet_rb_int_gt_slowpath, (VALUE, VALUE)); -SORBET_ALIVE(VALUE, sorbet_rb_int_le_slowpath, (VALUE, VALUE)); -SORBET_ALIVE(VALUE, sorbet_rb_int_ge_slowpath, (VALUE, VALUE)); - -SORBET_ALIVE(VALUE, sorbet_i_getRubyClass, (const char *const className, long classNameLen) __attribute__((const))); -SORBET_ALIVE(VALUE, sorbet_i_getRubyConstant, (const char *const className, long classNameLen) __attribute__((const))); -SORBET_ALIVE(VALUE, sorbet_i_send, - (struct FunctionInlineCache *, _Bool blkUsesBreak, struct vm_ifunc *, bool searchSuper, - rb_control_frame_t *, ...)); - -SORBET_ALIVE(_Bool, sorbet_i_isa_Integer, (VALUE) __attribute__((const))); -SORBET_ALIVE(_Bool, sorbet_i_isa_TrueClass, (VALUE) __attribute__((const))); -SORBET_ALIVE(_Bool, sorbet_i_isa_FalseClass, (VALUE) __attribute__((const))); -SORBET_ALIVE(_Bool, sorbet_i_isa_NilClass, (VALUE) __attribute__((const))); -SORBET_ALIVE(_Bool, sorbet_i_isa_Symbol, (VALUE) __attribute__((const))); -SORBET_ALIVE(_Bool, sorbet_i_isa_Float, (VALUE) __attribute__((const))); -SORBET_ALIVE(_Bool, sorbet_i_isa_Untyped, (VALUE) __attribute__((const))); -SORBET_ALIVE(_Bool, sorbet_i_isa_Hash, (VALUE) __attribute__((const))); -SORBET_ALIVE(_Bool, sorbet_i_isa_Array, (VALUE) __attribute__((const))); -SORBET_ALIVE(_Bool, sorbet_i_isa_Regexp, (VALUE) __attribute__((const))); -SORBET_ALIVE(_Bool, sorbet_i_isa_Rational, (VALUE) __attribute__((const))); -SORBET_ALIVE(_Bool, sorbet_i_isa_String, (VALUE) __attribute__((const))); -SORBET_ALIVE(_Bool, sorbet_i_isa_Proc, (VALUE) __attribute__((const))); -SORBET_ALIVE(_Bool, sorbet_i_isa_Thread, (VALUE) __attribute__((const))); -SORBET_ALIVE(_Bool, sorbet_i_isa_RootSingleton, (VALUE) __attribute__((const))); -SORBET_ALIVE(_Bool, sorbet_i_objIsKindOf, (VALUE, VALUE) __attribute__((const))); - -SORBET_ALIVE(long, sorbet_globalConstRegister, (VALUE val)); -SORBET_ALIVE(VALUE, sorbet_globalConstDupHash, (long index)); -SORBET_ALIVE(struct vm_ifunc *, sorbet_globalConstFetchIfunc, (long index)); -SORBET_ALIVE(VALUE, sorbet_magic_mergeHashHelper, (VALUE, VALUE)); - -SORBET_ALIVE(VALUE, sorbet_vm_getivar, (VALUE obj, ID id, struct iseq_inline_iv_cache_entry *cache)); -SORBET_ALIVE(void, sorbet_vm_setivar, (VALUE obj, ID id, VALUE val, struct iseq_inline_iv_cache_entry *cache)); - -SORBET_ALIVE(void, sorbet_vm_register_sig, - (VALUE isSelf, VALUE method, VALUE self, VALUE arg, rb_block_call_func_t block)); -SORBET_ALIVE(void, sorbet_vm_define_method, - (VALUE klass, const char *name, rb_sorbet_func_t methodPtr, void *paramp, rb_iseq_t *iseq, bool isSelf)); -SORBET_ALIVE(void, sorbet_vm_define_prop_getter, - (VALUE klass, const char *name, rb_sorbet_func_t methodPtr, void *paramp, rb_iseq_t *iseq)); - -// The layout of these structs is known to the compiler. -struct IDDescriptor { - unsigned int offset; - unsigned int length; -}; - -struct RubyStringDescriptor { - unsigned int offset; - unsigned int length; -}; - -SORBET_ALIVE(void, sorbet_vm_intern_ids, - (ID * idTable, struct IDDescriptor *idDescriptors, unsigned int numIDs, const char *stringTable)); -SORBET_ALIVE(void, sorbet_vm_init_string_table, - (VALUE * rubyStringTable, struct RubyStringDescriptor *descriptors, unsigned int numRubyStrings, - const char *stringTable)); - -SORBET_ALIVE(VALUE, sorbet_maybeAllocateObjectFastPath, (VALUE recv, struct FunctionInlineCache *newCache)); -SORBET_ALIVE(VALUE, sorbet_vm_instance_variable_get, - (struct FunctionInlineCache * getCache, struct iseq_inline_iv_cache_entry *varCache, - rb_control_frame_t *cfp, VALUE recv, ID var)); -SORBET_ALIVE(VALUE, sorbet_vm_instance_variable_set, - (struct FunctionInlineCache * getCache, struct iseq_inline_iv_cache_entry *varCache, - rb_control_frame_t *cfp, VALUE recv, ID var, VALUE value)); -SORBET_ALIVE(VALUE, sorbet_vm_class, (struct FunctionInlineCache * classCache, rb_control_frame_t *cfp, VALUE recv)); -SORBET_ALIVE(VALUE, sorbet_vm_bang, (struct FunctionInlineCache * bangCache, rb_control_frame_t *cfp, VALUE recv)); -SORBET_ALIVE(VALUE, sorbet_vm_isa_p, - (struct FunctionInlineCache * classCache, rb_control_frame_t *cfp, VALUE recv, VALUE klass)); -SORBET_ALIVE(VALUE, sorbet_vm_freeze, (struct FunctionInlineCache * classCache, rb_control_frame_t *cfp, VALUE recv)); - -SORBET_ALIVE(VALUE, rb_hash_keys, (VALUE recv)); -SORBET_ALIVE(VALUE, rb_hash_values, (VALUE recv)); -SORBET_ALIVE(VALUE, sorbet_rb_hash_fetch_m, - (VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, VALUE closure)); -SORBET_ALIVE(VALUE, sorbet_rb_hash_update, - (VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, VALUE closure)); -SORBET_ALIVE(int, sorbet_rb_hash_update_withBlock_i, (VALUE key, VALUE value, VALUE argsv)); - -SORBET_ALIVE(VALUE, sorbet_vm_fstring_new, (const char *ptr, long len)); -SORBET_ALIVE(VALUE, sorbet_vm_str_uplus, (VALUE str)); - -extern void sorbet_throwReturn(rb_execution_context_t *ec, VALUE retval) __attribute__((noreturn)); -KEEP_ALIVE(sorbet_throwReturn); -SORBET_ALIVE(VALUE, sorbet_vm_callBlock, - (rb_control_frame_t * cfp, int argc, SORBET_ATTRIBUTE(noescape) const VALUE *const restrict argv, - int kw_splat)); - -SORBET_ALIVE(int, rb_cvar_lookup, (VALUE klass, ID id, VALUE *v)); - -struct rfb_status { - // The return value from the function. - VALUE return_value; - - // Whether the value was returned via return-from-block. - bool was_thrown; -}; -SORBET_ALIVE(struct rfb_status, sorbet_vm_return_from_block_wrapper, - (int argc, VALUE *argv, VALUE recv, rb_control_frame_t *cfp, void *calling, void *cd, - rb_sorbet_func_t wrapped)); -SORBET_ALIVE(VALUE, sorbet_run_exception_handling, - (rb_execution_context_t * ec, ExceptionFFIType body, VALUE **volatile pc, - // The locals offset for the body. - VALUE methodClosure, rb_control_frame_t *volatile cfp, - // May be nullptr. - ExceptionFFIType handlers, - // May be nullptr. - ExceptionFFIType elseClause, - // May be nullptr. - ExceptionFFIType ensureClause, - // The special value indicating that we need to retry. - VALUE retrySingleton, long exceptionValueIndex, long exceptionValueLevel)); - -SORBET_ALIVE(VALUE, sorbet_rb_iterate, (VALUE(*body)(VALUE), VALUE data1, const struct vm_ifunc *ifunc)); -SORBET_ALIVE(VALUE, sorbet_vm_aref, - (rb_control_frame_t * cfp, struct FunctionInlineCache *cache, VALUE recv, VALUE arg)); -SORBET_ALIVE(VALUE, sorbet_vm_plus, - (rb_control_frame_t * cfp, struct FunctionInlineCache *cache, VALUE recv, VALUE arg)); -SORBET_ALIVE(VALUE, sorbet_vm_minus, - (rb_control_frame_t * cfp, struct FunctionInlineCache *cache, VALUE recv, VALUE arg)); -SORBET_ALIVE(VALUE, sorbet_vm_eqeq, - (rb_control_frame_t * cfp, struct FunctionInlineCache *cache, VALUE recv, VALUE arg)); -SORBET_ALIVE(VALUE, sorbet_vm_neq, - (rb_control_frame_t * cfp, struct FunctionInlineCache *cache, VALUE recv, VALUE arg)); -SORBET_ALIVE(VALUE, sorbet_vm_leq, - (rb_control_frame_t * cfp, struct FunctionInlineCache *cache, VALUE recv, VALUE arg)); -SORBET_ALIVE(VALUE, sorbet_vm_lt, (rb_control_frame_t * cfp, struct FunctionInlineCache *cache, VALUE recv, VALUE arg)); -SORBET_ALIVE(VALUE, sorbet_vm_geq, - (rb_control_frame_t * cfp, struct FunctionInlineCache *cache, VALUE recv, VALUE arg)); -SORBET_ALIVE(VALUE, sorbet_vm_gt, (rb_control_frame_t * cfp, struct FunctionInlineCache *cache, VALUE recv, VALUE arg)); - -// The next several functions exist to convert Ruby definitions into LLVM IR, and -// are always inlined as a consequence. - -// **** -// **** Singletons -// **** - -SORBET_INLINE -VALUE sorbet_rubyTrue() { - return RUBY_Qtrue; -} -KEEP_ALIVE(sorbet_rubyTrue); - -SORBET_INLINE -VALUE sorbet_rubyFalse() { - return RUBY_Qfalse; -} - -SORBET_INLINE -VALUE sorbet_rubyNil() { - return RUBY_Qnil; -} - -// use this undefined value when you have a variable that should _never_ escape to ruby. -SORBET_INLINE -VALUE sorbet_rubyUndef() { - return RUBY_Qundef; -} - -SORBET_INLINE -SORBET_ATTRIBUTE(pure) -VALUE sorbet_rubyTopSelf() { - return GET_VM()->top_self; -} - -SORBET_INLINE -rb_execution_context_t *sorbet_getEC() { - return GET_EC(); -} - -SORBET_INLINE -rb_control_frame_t *sorbet_getCFP() { - return GET_EC()->cfp; -} - -SORBET_INLINE -VALUE sorbet_getSelfFromFrame() { - return GET_EC()->cfp->self; -} - -// **** -// **** Implementation helpers for type tests -// **** - -SORBET_INLINE -VALUE sorbet_getVoidSingleton() { - static const char name[] = "T::Private::Types::Void::VOID"; - return sorbet_getConstant(name, sizeof(name)); -} - -SORBET_ATTRIBUTE(const) -_Bool sorbet_isa_Integer(VALUE obj) { - if (LIKELY(RB_FIXNUM_P(obj))) { - return true; - } - return RB_TYPE_P(obj, T_BIGNUM); -} -KEEP_ALIVE(sorbet_isa_Integer); - -SORBET_ATTRIBUTE(const) -_Bool sorbet_isa_TrueClass(VALUE obj) { - return obj == RUBY_Qtrue; -} -KEEP_ALIVE(sorbet_isa_TrueClass); - -SORBET_ATTRIBUTE(const) -_Bool sorbet_isa_FalseClass(VALUE obj) { - return obj == RUBY_Qfalse; -} -KEEP_ALIVE(sorbet_isa_FalseClass); - -SORBET_ATTRIBUTE(const) -_Bool sorbet_isa_NilClass(VALUE obj) { - return obj == RUBY_Qnil; -} -KEEP_ALIVE(sorbet_isa_NilClass); - -SORBET_ATTRIBUTE(const) -_Bool sorbet_isa_Symbol(VALUE obj) { - return RB_SYMBOL_P(obj); -} -KEEP_ALIVE(sorbet_isa_Symbol); - -SORBET_ATTRIBUTE(const) -_Bool sorbet_isa_Float(VALUE obj) { - return RB_FLOAT_TYPE_P(obj); -} -KEEP_ALIVE(sorbet_isa_Float); - -SORBET_ATTRIBUTE(const) -_Bool sorbet_isa_Untyped(VALUE obj) { - return 1; -} -KEEP_ALIVE(sorbet_isa_Untyped); - -SORBET_ATTRIBUTE(const) -_Bool sorbet_isa_Hash(VALUE obj) { - return RB_TYPE_P(obj, T_HASH); -} -KEEP_ALIVE(sorbet_isa_Hash); - -SORBET_ATTRIBUTE(const) -_Bool sorbet_isa_Array(VALUE obj) { - return RB_TYPE_P(obj, T_ARRAY); -} -KEEP_ALIVE(sorbet_isa_Array); - -SORBET_ATTRIBUTE(const) -_Bool sorbet_isa_Regexp(VALUE obj) { - return RB_TYPE_P(obj, T_REGEXP); -} -KEEP_ALIVE(sorbet_isa_Regexp); - -SORBET_ATTRIBUTE(const) -_Bool sorbet_isa_Rational(VALUE obj) { - return RB_TYPE_P(obj, T_RATIONAL); -} -KEEP_ALIVE(sorbet_isa_Rational); - -SORBET_ATTRIBUTE(const) -_Bool sorbet_isa_String(VALUE obj) { - return RB_TYPE_P(obj, T_STRING); -} -KEEP_ALIVE(sorbet_isa_String); - -SORBET_ATTRIBUTE(const) -_Bool sorbet_isa_Proc(VALUE obj) { - return rb_obj_is_proc(obj) == Qtrue; -} -KEEP_ALIVE(sorbet_isa_Proc); - -SORBET_ATTRIBUTE(const) -_Bool sorbet_isa_Thread(VALUE obj) { - return rb_obj_is_kind_of(obj, rb_cThread) == Qtrue; -} -KEEP_ALIVE(sorbet_isa_Thread); - -SORBET_ATTRIBUTE(const) -_Bool sorbet_isa_RootSingleton(VALUE obj) { - return obj == GET_VM()->top_self; -} -KEEP_ALIVE(sorbet_isa_RootSingleton); - -SORBET_ATTRIBUTE(const) VALUE rb_class_inherited_p(VALUE, VALUE); - -SORBET_ATTRIBUTE(const) -_Bool sorbet_isa_class_of(VALUE obj, VALUE class) { - return (obj == class) || (sorbet_i_objIsKindOf(obj, rb_cModule) && rb_class_inherited_p(obj, class)); -} - -SORBET_INLINE -int sorbet_rubyIseqTypeMethod() { - return ISEQ_TYPE_METHOD; -} - -SORBET_INLINE -int sorbet_rubyIseqTypeTop() { - return ISEQ_TYPE_TOP; -} - -SORBET_INLINE -int sorbet_rubyIseqTypeClass() { - return ISEQ_TYPE_CLASS; -} - -SORBET_INLINE -int sorbet_rubyIseqTypeBlock() { - return ISEQ_TYPE_BLOCK; -} - -SORBET_INLINE -int sorbet_rubyIseqTypeRescue() { - return ISEQ_TYPE_RESCUE; -} - -SORBET_INLINE -int sorbet_rubyIseqTypeEnsure() { - return ISEQ_TYPE_ENSURE; -} - -RUBY_EXTERN rb_serial_t ruby_vm_global_constant_state; - -SORBET_INLINE -rb_serial_t sorbet_getConstantEpoch() { - return ruby_vm_global_constant_state; -} -KEEP_ALIVE(sorbet_getConstantEpoch); - -SORBET_INLINE -VALUE sorbet_getMethodBlockAsProc() { - if (rb_block_given_p()) { - return rb_block_proc(); - } - return Qnil; -} - -SORBET_INLINE -_Bool sorbet_block_isa_proc() { - /* If we get here, we know that we're checking the block handler for proc-ness. - * - * If we don't have a block handler, then we know that the block handler cannot - * be a proc. If we do have a block handler, pretty much all the different - * kinds of block handlers can be procs (i.e. rb_block_proc always returns a - * proc-like thing: https://github.com/ruby/ruby/blob/7332b3f367ce61089bf571fa1c458fde06a11eab/proc.c#L743-L808). - * - * Since we're only checking for proc-ness and not arity (or even "can this thing be called?" - * in the case of a symbol-as-proc), the only thing we can do is make sure that - * we have a block handler. - */ - return rb_block_given_p(); -} - -SORBET_INLINE -VALUE sorbet_defineTopLevelModule(const char *name) { - return rb_define_module(name); -} - -SORBET_INLINE -VALUE sorbet_defineNestedModule(VALUE owner, const char *name) { - return rb_define_module_under(owner, name); -} - -SORBET_INLINE -VALUE sorbet_defineTopClassOrModule(const char *name, VALUE super) { - return rb_define_class(name, super); -} - -SORBET_INLINE -VALUE sorbet_defineNestedClass(VALUE owner, const char *name, VALUE super) { - return rb_define_class_under(owner, name, super); -} - -// this DOES override existing methods -SORBET_INLINE -void sorbet_defineMethod(VALUE klass, const char *name, rb_sorbet_func_t methodPtr, void *paramp, rb_iseq_t *iseq) { - sorbet_vm_define_method(klass, name, methodPtr, paramp, iseq, false); -} - -SORBET_INLINE -void sorbet_definePropGetter(VALUE klass, const char *name, rb_sorbet_func_t methodPtr, void *paramp, rb_iseq_t *iseq) { - sorbet_vm_define_prop_getter(klass, name, methodPtr, paramp, iseq); -} - -// this DOES override existing methods -SORBET_INLINE -void sorbet_defineMethodSingleton(VALUE klass, const char *name, rb_sorbet_func_t methodPtr, void *paramp, - rb_iseq_t *iseq) { - sorbet_vm_define_method(klass, name, methodPtr, paramp, iseq, true); -} - -SORBET_INLINE -void sorbet_defineIvarMethod(VALUE klass, const char *name) { - // https://github.com/ruby/ruby/blob/5445e0435260b449decf2ac16f9d09bae3cafe72/vm_method.c#L1190-L1193 - ID mid = rb_intern(name); - ID attriv = rb_intern_str(rb_sprintf("@%" PRIsVALUE, rb_id2str(mid))); - rb_add_method(klass, mid, VM_METHOD_TYPE_IVAR, (void *)attriv, METHOD_VISI_PUBLIC); -} - -SORBET_INLINE -void sorbet_defineIvarMethodSingleton(VALUE klass, const char *name) { - sorbet_defineIvarMethod(rb_singleton_class(klass), name); -} - -VALUE sorbet_singleton_class(VALUE klass) { - return rb_singleton_class(klass); -} - -// **** -// **** Variables -// **** - -SORBET_INLINE -VALUE sorbet_globalVariableGet(ID name) { - return rb_gvar_get(rb_global_entry(name)); -} - -SORBET_INLINE -void sorbet_globalVariableSet(ID name, VALUE newValue) { - rb_gvar_set(rb_global_entry(name), newValue); -} - -SORBET_INLINE -VALUE sorbet_instanceVariableGet(VALUE receiver, ID name, struct iseq_inline_iv_cache_entry *cache) { - return sorbet_vm_getivar(receiver, name, cache); -} - -SORBET_INLINE -void sorbet_instanceVariableSet(VALUE receiver, ID name, VALUE newValue, struct iseq_inline_iv_cache_entry *cache) { - sorbet_vm_setivar(receiver, name, newValue, cache); -} - -SORBET_INLINE -VALUE sorbet_instanceVariableDefined(VALUE recv, VALUE name) { - return rb_ivar_defined(recv, SYM2ID(name)); -} - -SORBET_INLINE -VALUE sorbet_classVariableGet(VALUE _class, ID name) { - return rb_cvar_get(_class, name); -} - -SORBET_INLINE -void sorbet_classVariableSet(VALUE _class, ID name, VALUE newValue) { - rb_cvar_set(_class, name, newValue); -} - -SORBET_INLINE -VALUE sorbet_classVariableDefined(VALUE klass, VALUE name) { - return rb_cvar_defined(klass, SYM2ID(name)); -} - -SORBET_INLINE -_Bool sorbet_classVariableDefinedAndTruthy(VALUE klass, ID name) { - VALUE val = Qundef; - if (!rb_cvar_lookup(klass, name, &val)) { - return false; - } - return RTEST(val); -} - -// **** -// **** Operations on Ruby ID's -// **** - -SORBET_INLINE -ID sorbet_idIntern(const char *value, long length) { - return rb_intern2(value, length); -} - -SORBET_INLINE -ID sorbet_symToID(VALUE sym) { - return SYM2ID(sym); -} - -SORBET_INLINE -VALUE sorbet_IDToSym(ID id) { - return ID2SYM(id); -} - -SORBET_INLINE -VALUE sorbet_getRubyClassOf(VALUE value) { - return CLASS_OF(value); -} - -SORBET_INLINE -const char *sorbet_getRubyClassName(VALUE object) { - return rb_obj_classname(object); -} - -// **** -// **** Conversions between Ruby values and C values -// **** - -SORBET_INLINE -long sorbet_rubyValueToLong(VALUE val) { - return FIX2LONG(val); -} - -SORBET_INLINE -VALUE sorbet_longToRubyValue(long i) { - return LONG2FIX(i); -} - -SORBET_INLINE -double sorbet_rubyValueToDouble(VALUE val) { - return RFLOAT_VALUE(val); -} - -SORBET_INLINE -VALUE sorbet_doubleToRubyValue(double u) { - return DBL2NUM(u); -} - -// **** -// **** Operations on Arrays -// **** - -extern VALUE (*sorbet_rb_ary_to_a_func(void))(VALUE); -KEEP_ALIVE(sorbet_rb_ary_to_a_func); - -VALUE sorbet_int_ary_to_a(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - extern VALUE sorbet_rb_ary_to_a(VALUE); - - return sorbet_rb_ary_to_a(recv); -} - -SORBET_INLINE -int sorbet_rubyArrayLen(VALUE array) { - return RARRAY_LEN(array); -} - -SORBET_INLINE -VALUE sorbet_rubyArrayArefUnchecked(VALUE array, int i) { - return RARRAY_AREF(array, i); -} - -SORBET_INLINE -const VALUE *sorbet_rubyArrayInnerPtr(VALUE array) { - // there's also a transient version of this function if we ever decide to want more speed. transient stands for that - // we _should not_ allow to execute any code between getting these pointers and reading elements from - return RARRAY_CONST_PTR(array); -} - -SORBET_INLINE -VALUE sorbet_newRubyArray(long size) { - return rb_ary_new2(size); -} - -SORBET_INLINE -VALUE sorbet_newRubyArrayWithElems(long size, const VALUE *elems) { - return rb_ary_new4(size, elems); -} - -SORBET_INLINE -void sorbet_arrayPush(VALUE array, VALUE element) { - rb_ary_push(array, element); -} - -SORBET_INLINE -VALUE sorbet_arrayDup(VALUE array) { - return rb_ary_dup(array); -} - -SORBET_INLINE -VALUE sorbet_arrayPop(VALUE array) { - return rb_ary_pop(array); -} - -SORBET_INLINE -VALUE sorbet_arrayNewFromValues(int argc, VALUE *argv) { - return rb_ary_new_from_values(argc, argv); -} - -// **** -// **** Operations on Hashes -// **** - -extern VALUE (*sorbet_rb_hash_to_h_func(void))(VALUE); -KEEP_ALIVE(sorbet_rb_hash_to_h_func); - -SORBET_INLINE -VALUE sorbet_int_hash_to_h(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - extern VALUE sorbet_rb_hash_to_h(VALUE); - return sorbet_rb_hash_to_h(recv); -} - -// This is the block variant of rb_hash_fetch_m: -// https://github.com/sorbet/ruby/blob/12d8c43330278a744d6e45135d1a8735f23f5afe/hash.c#L2113-L2147 -SORBET_INLINE -VALUE sorbet_rb_hash_fetch_m_withBlock(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - const struct rb_captured_block *captured, VALUE closure, int numPositionalArgs) { - extern VALUE sorbet_hash_stlike_lookup(VALUE hash, VALUE key, VALUE * val); - - rb_check_arity(argc, 1, 2); - VALUE key = argv[0]; - VALUE val; - - // NOTE: the vm will issue a warning when two arguments and a block are - // given, but we don't issue that warning here. - - if (sorbet_hash_stlike_lookup(recv, key, &val)) { - return (VALUE)val; - } else { - sorbet_pushBlockFrame(captured); - val = blk(key, closure, 1, &key, Qnil); - sorbet_popFrame(); - return val; - } -} - -struct sorbet_int_hash_to_h_closure { - BlockFFIType fun; - VALUE closure_for_block; - VALUE hash; -}; - -// `rb_hash_to_h` uses `rb_yield_values(2, key, value)` in the function given to -// `rb_hash_foreach`, so we always pass two values through the stack here. -static int sorbet_int_hash_to_h_withBlock_body(VALUE key, VALUE value, VALUE closure) { - VALUE argv[2]; - argv[0] = key; - argv[1] = value; - struct sorbet_int_hash_to_h_closure *c = (struct sorbet_int_hash_to_h_closure *)closure; - rb_hash_set_pair(c->hash, c->fun(key, c->closure_for_block, 2, &argv[0], Qnil)); - return ST_CONTINUE; -} - -SORBET_INLINE -VALUE sorbet_int_hash_to_h_withBlock(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - const struct rb_captured_block *captured, VALUE closure, int numPositionalArgs) { - VALUE h = rb_hash_new_with_size(RHASH_SIZE(recv)); - - struct sorbet_int_hash_to_h_closure passthrough; - passthrough.fun = blk; - passthrough.closure_for_block = closure; - passthrough.hash = h; - - sorbet_pushBlockFrame(captured); - rb_hash_foreach(recv, sorbet_int_hash_to_h_withBlock_body, (VALUE)&passthrough); - sorbet_popFrame(); - - return h; -} - -SORBET_INLINE -VALUE sorbet_newRubyHash() { - return rb_hash_new(); -} - -SORBET_INLINE -VALUE sorbet_hashBuild(int argc, const VALUE *argv) { - VALUE ret = rb_hash_new_with_size(argc / 2); - if (argc > 0) { - // We can use rb_hash_bulk_insert here because rb_hash_new_with_size freshly allocates. - // We have tried in the past to use rb_hash_bulk_insert after clearing an existing hash, - // and things broke wonderfully, because Ruby Hash objects are either backed by a small (<8 element) - // or large hash table implementation, and neither Hash#clear nor rb_hash_bulk_insert changes - // what kind of Hash object it is. - rb_hash_bulk_insert(argc, argv, ret); - } - return ret; -} - -SORBET_INLINE -long sorbet_literalHashBuild(int argc, const VALUE *argv) { - VALUE ret = sorbet_hashBuild(argc, argv); - long index = sorbet_globalConstRegister(ret); - return index; -} - -SORBET_INLINE -VALUE sorbet_hashDup(VALUE hash) { - return rb_hash_dup(hash); -} - -SORBET_INLINE -void sorbet_hashStore(VALUE hash, VALUE key, VALUE value) { - rb_hash_aset(hash, key, value); -} - -SORBET_INLINE -VALUE sorbet_hashGet(VALUE hash, VALUE key) { - return rb_hash_aref(hash, key); -} - -// possible return values for `func`: -// - ST_CONTINUE, then the rest of the hash is processed as normal. -// - ST_STOP, then no further processing of the hash is done. -// - ST_DELETE, then the current hash key is deleted from the hash and the rest -// of the hash is processed -// - ST_CHECK, then the hash is checked to see if it has been modified during -// this operation. If so, processing of the hash stops. -/* -void sorbet_hashEach(VALUE hash, int(*func)(VALUE key, VALUE val, -VALUE in), VALUE closure) { return rb_hash_foreach(hash, func, closure); -} -*/ - -// **** -// **** Operations on Strings -// **** - -// The address of the static symbol rb_str_to_s, for guarding the String#to_s -// intrinsic. -extern VALUE (*sorbet_rb_str_to_s_func(void))(VALUE); -KEEP_ALIVE(sorbet_rb_str_to_s_func); - -VALUE sorbet_int_str_to_s(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - extern VALUE sorbet_rb_str_to_s(VALUE); - return sorbet_rb_str_to_s(recv); -} - -SORBET_INLINE -const char *sorbet_rubyStringToCPtr(VALUE value) { - return RSTRING_PTR(value); -} - -SORBET_INLINE -long sorbet_rubyStringLength(VALUE value) { - return RSTRING_LEN(value); -} - -SORBET_INLINE -VALUE sorbet_cPtrToRubyString(const char *ptr, long length) { - return rb_str_new(ptr, length); -} - -SORBET_INLINE -VALUE sorbet_cPtrToRubyRegexpFrozen(const char *ptr, long length, int options) { - VALUE ret = rb_reg_new(ptr, length, options); - rb_gc_register_mark_object(ret); - return ret; -} - -SORBET_INLINE -VALUE sorbet_stringPlus(VALUE str1, VALUE str2) { - return rb_str_plus(str1, str2); -} - -// **** -// **** Tests -// **** - -SORBET_INLINE -_Bool sorbet_testIsTruthy(VALUE value) { - return RB_TEST(value); -} - -SORBET_INLINE -_Bool sorbet_testIsUndef(VALUE value) { - return value == RUBY_Qundef; -} - -// https://ruby-doc.org/core-2.6.3/Object.html#method-i-eql-3F -SORBET_INLINE -_Bool sorbet_testObjectEqual_p(VALUE obj1, VALUE obj2) { - return obj1 == obj2; -} - -SORBET_INLINE -void sorbet_ensure_arity(int argc, int expected) { - if (UNLIKELY(argc != expected)) { - sorbet_raiseArity(argc, expected, expected); - } -} - -SORBET_INLINE -void sorbet_checkStack() { - // This is actually pretty slow. We should probably use guard pages instead. - ruby_stack_check(); -} - -SORBET_INLINE -VALUE sorbet_boolToRuby(_Bool b) { - if (b) { - return RUBY_Qtrue; - } - return RUBY_Qfalse; -} - -// TODO: add many from https://github.com/ruby/ruby/blob/ruby_2_6/include/ruby/intern.h#L55 -SORBET_INLINE -VALUE sorbet_T_unsafe(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, VALUE closure) { - sorbet_ensure_arity(argc, 1); - return argv[0]; -} - -SORBET_INLINE -VALUE sorbet_T_must(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, VALUE closure) { - sorbet_ensure_arity(argc, 1); - if (UNLIKELY(argv[0] == Qnil)) { - rb_raise(rb_eTypeError, "Passed `nil` into T.must"); - } else { - return argv[0]; - } -} - -SORBET_INLINE -VALUE sorbet_rb_sym_equal(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - sorbet_ensure_arity(argc, 1); - return sorbet_boolToRuby(recv == argv[0]); -} - -SORBET_INLINE -VALUE sorbet_Thread_current(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - return rb_thread_current(); -} - -SORBET_INLINE -VALUE sorbet_Thread_main(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - return rb_thread_main(); -} - -// https://github.com/ruby/ruby/blob/5445e0435260b449decf2ac16f9d09bae3cafe72/thread.c#L3281-L3287 -SORBET_INLINE -VALUE sorbet_Thread_square_br(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - sorbet_ensure_arity(argc, 1); - VALUE key = argv[0]; - ID id = rb_check_id(&key); - if (!id) { - return Qnil; - } - return rb_thread_local_aref(recv, id); -} - -SORBET_INLINE -VALUE sorbet_Thread_square_br_symarg(VALUE recv, ID id) { - return rb_thread_local_aref(recv, id); -} - -// https://github.com/ruby/ruby/blob/5445e0435260b449decf2ac16f9d09bae3cafe72/thread.c#L3386-L3390 -SORBET_INLINE -VALUE sorbet_Thread_square_br_eq(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - sorbet_ensure_arity(argc, 2); - VALUE id = argv[0]; - VALUE val = argv[1]; - return rb_thread_local_aset(recv, rb_to_id(id), val); -} - -SORBET_INLINE -VALUE sorbet_Thread_square_br_eq_symarg(VALUE recv, ID id, VALUE val) { - return rb_thread_local_aset(recv, id, val); -} - -// https://github.com/ruby/ruby/blob/5445e0435260b449decf2ac16f9d09bae3cafe72/thread.c#L3386-L3390 -SORBET_INLINE -VALUE sorbet_rb_obj_is_kind_of(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - sorbet_ensure_arity(argc, 1); - VALUE klass = argv[0]; - return rb_obj_is_kind_of(recv, klass); -} - -VALUE sorbet_rb_array_len(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - sorbet_ensure_arity(argc, 0); - return LONG2FIX(rb_array_len(recv)); -} - -SORBET_INLINE -VALUE sorbet_rb_array_square_br(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - VALUE ary = recv; - rb_check_arity(argc, 1, 2); - if (LIKELY(argc == 1)) { - VALUE arg = argv[0]; - if (LIKELY(FIXNUM_P(arg))) { - return rb_ary_entry(ary, FIX2LONG(arg)); - } - } - return sorbet_rb_array_square_br_slowpath(recv, fun, argc, argv, blk, closure); -} - -static VALUE sorbet_array_enum_length(VALUE recv, VALUE args, VALUE eobj) { - long len = RARRAY_LEN(recv); - return LONG2NUM(len); -} - -// This is the no-block version of rb_ary_each: https://github.com/ruby/ruby/blob/ruby_2_7/array.c#L2128-L2138 -// In that version, the `RETURN_SIZED_ENUMERATOR` macro is what causes the early return when a block is not passed. In -// this case, we know that the block wasn't passed, so we always return an enumerator -SORBET_INLINE -VALUE sorbet_rb_array_each(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 0, 0); - return rb_enumeratorize_with_size(recv, ID2SYM(fun), argc, argv, sorbet_array_enum_length); -} - -// This is the block version of rb_ary_each: https://github.com/ruby/ruby/blob/ruby_2_7/array.c#L2128-L2138 -// In that version the for loop uses `rb_yield`, whereas we call the block function pointer directly. -SORBET_INLINE -VALUE sorbet_rb_array_each_withBlock(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - const struct rb_captured_block *captured, VALUE closure, int numPositionalArgs) { - rb_check_arity(argc, 0, 0); - - // must push a frame for the captured block - sorbet_pushBlockFrame(captured); - - for (int i = 0; i < RARRAY_LEN(recv); ++i) { - VALUE val = RARRAY_AREF(recv, i); - blk(val, closure, 1, &val, Qnil); - } - - sorbet_popFrame(); - - return recv; -} - -// This is the no-block version of enum_each_with_object for arrays: -// https://github.com/ruby/ruby/blob/b0b7751f3b94e7983d124e43102f76ff598caabd/enum.c#L2749-L2757 In that version, the -// `RETURN_SIZED_ENUMERATOR` macro is what causes the early return when a block is not passed. In this case, we know -// that the block wasn't passed, so we always return an enumerator -SORBET_INLINE -VALUE sorbet_rb_array_each_with_object(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 1, 1); - return rb_enumeratorize_with_size(recv, ID2SYM(fun), argc, argv, sorbet_array_enum_length); -} - -// This is the block version of Array#each_with_object -- which doesn't exist as a separate C function in the VM. -// In the no-block version, above, the code uses `rb_yield`, whereas we call the block function pointer directly. -SORBET_INLINE -VALUE sorbet_rb_array_each_with_object_withBlock(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, - BlockFFIType blk, const struct rb_captured_block *captured, - VALUE closure, int numPositionalArgs) { - rb_check_arity(argc, 1, 1); - - VALUE object = argv[0]; - VALUE block_argv[2]; - block_argv[1] = object; - - // must push a frame for the captured block - sorbet_pushBlockFrame(captured); - - for (long i = 0; i < RARRAY_LEN(recv); ++i) { - VALUE val = RARRAY_AREF(recv, i); - block_argv[0] = val; - blk(val, closure, 2, &block_argv[0], Qnil); - } - - sorbet_popFrame(); - - return object; -} - -// This is the no-block version of rb_ary_select: https://github.com/ruby/ruby/blob/ruby_2_7/array.c#L3212-L3226 -// In that version, the `RETURN_SIZED_ENUMERATOR` macro is what causes the early return when a block is not passed. In -// this case, we know that the block wasn't passed, so we always return an enumerator -SORBET_INLINE -VALUE sorbet_rb_array_select(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 0, 0); - return rb_enumeratorize_with_size(recv, ID2SYM(fun), argc, argv, sorbet_array_enum_length); -} - -// This is the block version of rb_ary_select: https://github.com/ruby/ruby/blob/ruby_2_7/array.c#L3212-L3226 -// In that version the for loop uses `rb_yield`, whereas we call the block function pointer directly. -SORBET_INLINE -VALUE sorbet_rb_array_select_withBlock(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - const struct rb_captured_block *captured, VALUE closure, int numPositionalArgs) { - rb_check_arity(argc, 0, 0); - VALUE result = rb_ary_new2(RARRAY_LEN(recv)); - - // must push a frame for the captured block - sorbet_pushBlockFrame(captured); - - for (long i = 0; i < RARRAY_LEN(recv); ++i) { - VALUE val = RARRAY_AREF(recv, i); - VALUE ret = blk(val, closure, 1, &val, Qnil); - if (RTEST(ret)) { - rb_ary_push(result, val); - } - } - - sorbet_popFrame(); - - return result; -} - -// This is the no-block version of rb_ary_reject: https://github.com/ruby/ruby/blob/ruby_2_7/array.c#L3609-L3618 -// In that version, the `RETURN_SIZED_ENUMERATOR` macro is what causes the early return when a block is not passed. In -// this case, we know that the block wasn't passed, so we always return an enumerator -SORBET_INLINE -VALUE sorbet_rb_array_reject(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - sorbet_ensure_arity(argc, 0); - return rb_enumeratorize_with_size(recv, ID2SYM(fun), argc, argv, sorbet_array_enum_length); -} - -// This is the block version of rb_ary_reject: https://github.com/ruby/ruby/blob/ruby_2_7/array.c#L3609-L3618 -// In that version the for loop uses `rb_yield`, whereas we call the block function pointer directly. -SORBET_INLINE -VALUE sorbet_rb_array_reject_withBlock(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - const struct rb_captured_block *captured, VALUE closure, int numPositionalArgs) { - sorbet_ensure_arity(argc, 0); - VALUE result = rb_ary_new2(RARRAY_LEN(recv)); - - // must push a frame for the captured block - sorbet_pushBlockFrame(captured); - - // inlining ary_reject - for (long i = 0; i < RARRAY_LEN(recv); ++i) { - VALUE val = RARRAY_AREF(recv, i); - VALUE ret = blk(val, closure, 1, &val, Qnil); - if (!RTEST(ret)) { - rb_ary_push(result, val); - } - } - - sorbet_popFrame(); - - return result; -} - -// This is the no-block version of rb_ary_reject_bang: https://github.com/ruby/ruby/blob/ruby_2_7/array.c#L3588-L3594 -SORBET_INLINE -VALUE sorbet_rb_array_reject_bang(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - sorbet_ensure_arity(argc, 0); - return rb_enumeratorize_with_size(recv, ID2SYM(fun), argc, argv, sorbet_array_enum_length); -} - -// Borrowed from https://github.com/ruby/ruby/blob/ruby_2_7/array.c#L3228-L3231, extended with blk and closure. -struct sorbet_select_bang_arg { - VALUE ary; - long len[2]; - BlockFFIType blk; - VALUE closure; -}; - -// Borrowed from https://github.com/ruby/ruby/blob/ruby_2_7/array.c#L3545-L3561 -static VALUE sorbet_reject_bang_i(VALUE a) { - volatile struct sorbet_select_bang_arg *arg = (void *)a; - VALUE ary = arg->ary; - BlockFFIType blk = arg->blk; - VALUE closure = arg->closure; - long i1, i2; - - for (i1 = i2 = 0; i1 < RARRAY_LEN(ary); arg->len[0] = ++i1) { - VALUE v = RARRAY_AREF(ary, i1); - if (RTEST(blk(v, closure, 1, &v, Qnil))) { - continue; - } - if (i1 != i2) { - rb_ary_store(ary, i2, v); - } - arg->len[1] = ++i2; - } - return (i1 == i2) ? Qnil : ary; -} - -// Borrowed from https://github.com/ruby/ruby/blob/ruby_2_7/array.c#L3251-L3270 -static VALUE sorbet_select_bang_ensure(VALUE a) { - volatile struct sorbet_select_bang_arg *arg = (void *)a; - VALUE ary = arg->ary; - long len = RARRAY_LEN(ary); - long i1 = arg->len[0], i2 = arg->len[1]; - - if (i2 < len && i2 < i1) { - long tail = 0; - if (i1 < len) { - tail = len - i1; - RARRAY_PTR_USE_TRANSIENT(ary, ptr, { MEMMOVE(ptr + i2, ptr + i1, VALUE, tail); }); - } - // NOTE: This was originally ARY_SET_LEN, which is naturally inlined but whose definition is local to array.c. - // I think wrapping this in a function is reasonable since it will only fire in the exceptional case. - sorbet_rb_ary_set_len(ary, i2 + tail); - } - return ary; -} - -// This is the block version of rb_ary_reject_bang: https://github.com/ruby/ruby/blob/ruby_2_7/array.c#L3588-L3594 -// In that version the for loop uses `rb_yield`, whereas we call the block function pointer directly. -SORBET_INLINE -VALUE sorbet_rb_array_reject_bang_withBlock(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, - BlockFFIType blk, const struct rb_captured_block *captured, VALUE closure, - int numPositionalArgs) { - sorbet_ensure_arity(argc, 0); - - // must push a frame for the captured block - sorbet_pushBlockFrame(captured); - - rb_ary_modify(recv); - - // begin inline of ary_reject_bang - struct sorbet_select_bang_arg args; - - // begin inline of rb_ary_modify_check - rb_check_frozen(recv); - // we skip the ary_verify call from rb_ary_modify_check because that's a no-op unless ARRAY_DEBUG is defined. - // end inline of rb_ary_modify_check - - args.ary = recv; - args.len[0] = 0; - args.len[1] = 0; - args.blk = blk; - args.closure = closure; - VALUE result = rb_ensure(sorbet_reject_bang_i, (VALUE)&args, sorbet_select_bang_ensure, (VALUE)&args); - // end inline of ary_reject_bang - - sorbet_popFrame(); - - return result; -} - -// This is the non-block version of rb_enum_find: https://github.com/ruby/ruby/blob/ruby_2_7/enum.c#L292-L309 -// In that version, the `RETURN_ENUMERATOR` macro is what causes the early return when a block is not passed. In -// this case, we know that the block wasn't passed, so we always return an enumerator -SORBET_INLINE -VALUE sorbet_rb_array_find(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 1, 1); - return rb_enumeratorize_with_size(recv, ID2SYM(fun), argc, argv, sorbet_array_enum_length); -} - -// This is the block version of rb_enum_find: https://github.com/ruby/ruby/blob/ruby_2_7/enum.c#L292-L309 -// That version uses `rb_block_call`, whereas we call the block function pointer directly. -SORBET_INLINE -VALUE sorbet_rb_array_find_withBlock(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - const struct rb_captured_block *captured, VALUE closure, int numPositionalArgs) { - rb_check_arity(argc, 0, 1); - VALUE result = Qnil; - VALUE if_none = argc == 1 ? argv[0] : Qnil; - - // must push a frame for the captured block - sorbet_pushBlockFrame(captured); - - for (long i = 0; i < RARRAY_LEN(recv); ++i) { - VALUE val = RARRAY_AREF(recv, i); - VALUE ret = blk(val, closure, 1, &val, Qnil); - if (RTEST(ret)) { - result = val; - break; - } - } - - sorbet_popFrame(); - - if (NIL_P(result) && !NIL_P(if_none)) { - return rb_funcallv(if_none, idCall, 0, 0); - } - - return result; -} - -// This is the no-block version of rb_ary_collect: https://github.com/ruby/ruby/blob/ruby_2_7/array.c#L3056-L3068 -// In that version, the `RETURN_SIZED_ENUMERATOR` macro is what causes the early return when a block is not passed. In -// this case, we know that the block wasn't passed, so we always return an enumerator -SORBET_INLINE -VALUE sorbet_rb_array_collect(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 0, 0); - return rb_enumeratorize_with_size(recv, ID2SYM(fun), argc, argv, sorbet_array_enum_length); -} - -// This is the no-block version of rb_ary_collect: https://github.com/ruby/ruby/blob/ruby_2_7/array.c#L3056-L3068 -// In that version the for loop uses `rb_yield`, whereas we call the block function pointer directly. -SORBET_INLINE -VALUE sorbet_rb_array_collect_withBlock(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, - BlockFFIType blk, const struct rb_captured_block *captured, VALUE closure, - int numPositionalArgs) { - rb_check_arity(argc, 0, 0); - - // must push a frame for the captured block - sorbet_pushBlockFrame(captured); - - // We can't coalesce the RARRAY_LEN calls because the loop needs to be responsive - // to changes in the array's length. - VALUE collect = rb_ary_new2(RARRAY_LEN(recv)); - - for (long i = 0; i < RARRAY_LEN(recv); ++i) { - VALUE val = RARRAY_AREF(recv, i); - rb_ary_push(collect, blk(val, closure, 1, &val, Qnil)); - } - - sorbet_popFrame(); - - return collect; -} - -// This is the no-block version of rb_ary_collect_bang: https://github.com/ruby/ruby/blob/ruby_2_7/array.c#L3092-L3103 -SORBET_INLINE -VALUE sorbet_rb_array_collect_bang(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 0, 0); - return rb_enumeratorize_with_size(recv, ID2SYM(fun), argc, argv, sorbet_array_enum_length); -} - -// This is the block version of rb_ary_collect_bang: https://github.com/ruby/ruby/blob/ruby_2_7/array.c#L3092-L3103 -SORBET_INLINE -VALUE sorbet_rb_array_collect_bang_withBlock(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, - BlockFFIType blk, const struct rb_captured_block *captured, VALUE closure, - int numPositionalArgs) { - rb_check_arity(argc, 0, 0); - - // must push a frame for the captured block - sorbet_pushBlockFrame(captured); - - rb_ary_modify(recv); - for (long i = 0; i < RARRAY_LEN(recv); i++) { - VALUE val = RARRAY_AREF(recv, i); - rb_ary_store(recv, i, blk(val, closure, 1, &val, Qnil)); - } - - sorbet_popFrame(); - - return recv; -} - -// This is the no-block version of rb_ary_any_p: https://github.com/ruby/ruby/blob/ruby_2_7/array.c#L6338-L6364 -SORBET_INLINE -VALUE sorbet_rb_array_any(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 0, 1); - long len = RARRAY_LEN(recv); - if (argc == 1) { - for (long i = 0; i < len; ++i) { - if (RTEST(rb_funcall(argv[0], idEqq, 1, RARRAY_AREF(recv, i)))) { - return Qtrue; - } - } - } else { - for (long i = 0; i < len; ++i) { - if (RTEST(RARRAY_AREF(recv, i))) { - return Qtrue; - } - } - } - return Qfalse; -} - -// This is the block version of rb_ary_any_p: https://github.com/ruby/ruby/blob/ruby_2_7/array.c#L6338-L6364 -// In that version the for loop uses `rb_yield`, whereas we call the block function pointer directly. -SORBET_INLINE -VALUE sorbet_rb_array_any_withBlock(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - const struct rb_captured_block *captured, VALUE closure, int numPositionalArgs) { - rb_check_arity(argc, 0, 0); - - // must push a frame for the captured block - sorbet_pushBlockFrame(captured); - - for (long i = 0; i < RARRAY_LEN(recv); ++i) { - VALUE val = RARRAY_AREF(recv, i); - VALUE ret = blk(val, closure, 1, &val, Qnil); - if (RTEST(ret)) { - sorbet_popFrame(); - return Qtrue; - } - } - - sorbet_popFrame(); - - return Qfalse; -} - -// This is the no-block version of rb_ary_all_p: https://github.com/ruby/ruby/blob/ruby_2_7/array.c#L6374-L6400 -SORBET_INLINE -VALUE sorbet_rb_array_all(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 0, 1); - long len = RARRAY_LEN(recv); - if (argc == 1) { - for (long i = 0; i < len; ++i) { - if (!RTEST(rb_funcall(argv[0], idEqq, 1, RARRAY_AREF(recv, i)))) { - return Qfalse; - } - } - } else { - for (long i = 0; i < len; ++i) { - if (!RTEST(RARRAY_AREF(recv, i))) { - return Qfalse; - } - } - } - return Qtrue; -} - -// This is the block version of rb_ary_all_p: https://github.com/ruby/ruby/blob/ruby_2_7/array.c#L6374-L6400 -// In that version the for loop uses `rb_yield`, whereas we call the block function pointer directly. -SORBET_INLINE -VALUE sorbet_rb_array_all_withBlock(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - const struct rb_captured_block *captured, VALUE closure, int numPositionalArgs) { - rb_check_arity(argc, 0, 0); - - // must push a frame for the captured block - sorbet_pushBlockFrame(captured); - - for (long i = 0; i < RARRAY_LEN(recv); ++i) { - VALUE val = RARRAY_AREF(recv, i); - VALUE ret = blk(val, closure, 1, &val, Qnil); - if (!RTEST(ret)) { - sorbet_popFrame(); - return Qfalse; - } - } - - sorbet_popFrame(); - - return Qtrue; -} - -// This is an inlinable version of rb_ary_compact_bang https://github.com/ruby/ruby/blob/ruby_2_7/array.c#L5088-L5094 -SORBET_INLINE -VALUE sorbet_rb_array_compact_bang(VALUE ary, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - return rb_ary_compact_bang_forwarder(ary); -} - -// This is an inlinable version of rb_ary_compact https://github.com/ruby/ruby/blob/ruby_2_7/array.c#L5088-L5094 -SORBET_INLINE -VALUE sorbet_rb_array_compact(VALUE ary, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - ary = rb_ary_dup(ary); - rb_ary_compact_bang_forwarder(ary); - return ary; -} - -// This is the no-block version of rb_ary_to_h https://github.com/ruby/ruby/blob/ruby_2_7/array.c#L2496-L2518 -SORBET_INLINE -VALUE sorbet_rb_array_to_h(VALUE ary, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - // the uses of RARRAY_LEN in rb_ary_to_h can be coalesced because there's no block passed in this case - long len = RARRAY_LEN(ary); - VALUE hash = rb_hash_new_with_size(len); - - for (long i = 0; i < len; i++) { - const VALUE elt = RARRAY_AREF(ary, i); - const VALUE key_value_pair = rb_check_array_type(elt); - if (NIL_P(key_value_pair)) { - rb_raise(rb_eTypeError, "wrong element type %" PRIsVALUE " at %ld (expected array)", rb_obj_class(elt), i); - } - if (RARRAY_LEN(key_value_pair) != 2) { - rb_raise(rb_eArgError, "wrong array length at %ld (expected 2, was %ld)", i, RARRAY_LEN(key_value_pair)); - } - rb_hash_aset(hash, RARRAY_AREF(key_value_pair, 0), RARRAY_AREF(key_value_pair, 1)); - } - - return hash; -} - -// This is an adjusted version of the intrinsic from the ruby vm. The major change is that instead of handling the case -// where a range is used as the key, we defer back to the VM. -// https://github.com/ruby/ruby/blob/ruby_2_6/array.c#L1980-L2005 -SORBET_INLINE -VALUE sorbet_rb_array_square_br_eq(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - long offset, beg, len; - - if (UNLIKELY(argc == 3)) { - goto range; - } - rb_check_arity(argc, 2, 2); - rb_check_frozen(recv); - if (LIKELY(FIXNUM_P(argv[0]))) { - offset = FIX2LONG(argv[0]); - goto fixnum; - } - if (UNLIKELY(rb_range_beg_len(argv[0], &beg, &len, RARRAY_LEN(recv), 1))) { - range: - return rb_funcallv(recv, fun, argc, argv); - } - offset = NUM2LONG(argv[0]); -fixnum: - rb_ary_store(recv, offset, argv[1]); - return argv[1]; -} - -SORBET_INLINE -VALUE sorbet_rb_array_empty(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 0, 0); - if (RARRAY_LEN(recv) == 0) { - return Qtrue; - } - return Qfalse; -} - -// This is the block version of rb_ary_uniq: https://github.com/ruby/ruby/blob/ruby_2_7/array.c#L5018-L5041 -SORBET_INLINE -VALUE sorbet_rb_array_uniq_withBlock(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - const struct rb_captured_block *captured, VALUE closure, int numPositionalArgs) { - sorbet_ensure_arity(argc, 0); - VALUE ary = recv; - - // must push a frame for the captured block - sorbet_pushBlockFrame(captured); - - VALUE hash, uniq; - - if (RARRAY_LEN(ary) <= 1) { - hash = 0; - uniq = rb_ary_dup(ary); - } else { - // inline of ary_tmp_hash_new - long size = RARRAY_LEN(ary); - hash = rb_hash_new_with_size(size); - - RBASIC_CLEAR_CLASS(hash); - - // inline of ary_make_hash_by - for (int i = 0; i < RARRAY_LEN(ary); ++i) { - // inline of rb_ary_elt, with bounds checking omitted (because of the for loop we're in, we can be sure i - // is in bounds at this point) - VALUE v = RARRAY_AREF(ary, i); - VALUE k = blk(v, closure, 1, &v, Qnil); - rb_hash_add_new_element(hash, k, v); - } - uniq = rb_hash_values(hash); - } - RBASIC_SET_CLASS(uniq, rb_obj_class(ary)); - if (hash) { - sorbet_ary_recycle_hash(hash); - } - - sorbet_popFrame(); - - return uniq; -} - -SORBET_INLINE -VALUE sorbet_rb_hash_square_br(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 1, 1); - return rb_hash_aref(recv, argv[0]); -} - -SORBET_INLINE -VALUE sorbet_rb_hash_square_br_eq(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 2, 2); - return rb_hash_aset(recv, argv[0], argv[1]); -} - -// This is the no-block version of rb_hash_delete_m https://github.com/ruby/ruby/blob/ruby_2_7/hash.c#L2390-L2409 -SORBET_INLINE -VALUE sorbet_rb_hash_delete_m(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - sorbet_ensure_arity(argc, 1); - - VALUE val; - VALUE key = argv[0]; - - // begin inline of rb_hash_modify_check - rb_check_frozen(recv); - // end inline of rb_hash_modify_check - - val = rb_hash_delete_entry(recv, key); - - if (val != Qundef) { - return val; - } else { - return Qnil; - } -} - -// This is the block version of rb_hash_delete_m https://github.com/ruby/ruby/blob/ruby_2_7/hash.c#L2390-L2409 -SORBET_INLINE -VALUE sorbet_rb_hash_delete_m_withBlock(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, - BlockFFIType blk, const struct rb_captured_block *captured, VALUE closure, - int numPositionalArgs) { - sorbet_ensure_arity(argc, 1); - - VALUE val; - VALUE key = argv[0]; - - // begin inline of rb_hash_modify_check - rb_check_frozen(recv); - // end inline of rb_hash_modify_check - - val = rb_hash_delete_entry(recv, key); - - if (val != Qundef) { - return val; - } else { - sorbet_pushBlockFrame(captured); - VALUE ret = blk(key, closure, 1, &key, Qnil); - sorbet_popFrame(); - return ret; - } -} - -// From rb_hash_empty_p: https://github.com/ruby/ruby/blob/ruby_2_7/hash.c#L2976-L2980 -SORBET_INLINE -VALUE sorbet_rb_hash_empty_p(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - sorbet_ensure_arity(argc, 0); - return RHASH_EMPTY_P(recv) ? Qtrue : Qfalse; -} - -SORBET_INLINE -VALUE sorbet_rb_int_plus(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - sorbet_ensure_arity(argc, 1); - VALUE y = argv[0]; - if (LIKELY(FIXNUM_P(recv))) { - if (LIKELY(FIXNUM_P(y))) { - return rb_fix_plus_fix(recv, y); - } - } - return sorbet_rb_int_plus_slowpath(recv, y); -} - -SORBET_INLINE -VALUE sorbet_rb_int_minus(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - sorbet_ensure_arity(argc, 1); - // optimized version from numeric.c - VALUE y = argv[0]; - if (LIKELY(FIXNUM_P(recv))) { - if (LIKELY(FIXNUM_P(y))) { - return rb_fix_minus_fix(recv, y); - } - } - return sorbet_rb_int_minus_slowpath(recv, y); -} - -SORBET_INLINE -VALUE sorbet_rb_int_mul(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - sorbet_ensure_arity(argc, 1); - return rb_int_mul(recv, argv[0]); -} - -SORBET_INLINE -VALUE sorbet_rb_int_div(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - sorbet_ensure_arity(argc, 1); - return rb_int_div(recv, argv[0]); -} - -SORBET_INLINE -VALUE sorbet_rb_int_lt(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - sorbet_ensure_arity(argc, 1); - VALUE y = argv[0]; - if (LIKELY(FIXNUM_P(recv))) { - if (LIKELY(FIXNUM_P(y))) { - if (FIX2LONG(recv) < FIX2LONG(y)) { - return Qtrue; - } - return Qfalse; - } - } - return sorbet_rb_int_lt_slowpath(recv, y); -} - -SORBET_INLINE -VALUE sorbet_rb_int_gt(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - sorbet_ensure_arity(argc, 1); - VALUE y = argv[0]; - if (LIKELY(FIXNUM_P(recv))) { - if (LIKELY(FIXNUM_P(y))) { - if (FIX2LONG(recv) > FIX2LONG(y)) { - return Qtrue; - } - return Qfalse; - } - } - return sorbet_rb_int_gt_slowpath(recv, y); -} - -SORBET_INLINE -VALUE sorbet_rb_int_le(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - sorbet_ensure_arity(argc, 1); - VALUE y = argv[0]; - if (LIKELY(FIXNUM_P(recv))) { - if (LIKELY(FIXNUM_P(y))) { - if (FIX2LONG(recv) <= FIX2LONG(y)) { - return Qtrue; - } - return Qfalse; - } - } - return sorbet_rb_int_le_slowpath(recv, y); -} - -SORBET_INLINE -VALUE sorbet_rb_int_ge(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - sorbet_ensure_arity(argc, 1); - VALUE y = argv[0]; - if (LIKELY(FIXNUM_P(recv))) { - if (LIKELY(FIXNUM_P(y))) { - if (FIX2LONG(recv) >= FIX2LONG(y)) { - return Qtrue; - } - return Qfalse; - } - } - return sorbet_rb_int_ge_slowpath(recv, y); -} - -SORBET_INLINE -VALUE sorbet_rb_int_equal(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - sorbet_ensure_arity(argc, 1); - return rb_int_equal(recv, argv[0]); -} - -SORBET_INLINE -VALUE sorbet_rb_int_neq(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - sorbet_ensure_arity(argc, 1); - return sorbet_boolToRuby(rb_int_equal(recv, argv[0]) == RUBY_Qfalse); -} - -SORBET_INLINE -VALUE sorbet_rb_int_to_s(VALUE x, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, VALUE closure) { - int base; - - rb_check_arity(argc, 0, 1); - if (argc == 1) { - base = NUM2INT(argv[0]); - } else { - base = 10; - } - if (LIKELY(FIXNUM_P(x))) { - return rb_fix2str(x, base); - } - if (RB_TYPE_P(x, T_BIGNUM)) { - return rb_big2str(x, base); - } - - return rb_any_to_s(x); -} - -static VALUE sorbet_int_dotimes_size(VALUE num, VALUE args, VALUE eobj) { - if (FIXNUM_P(num)) { - if (NUM2LONG(num) <= 0) - return INT2FIX(0); - } else { - if (RTEST(rb_funcall(num, '<', 1, INT2FIX(0)))) - return INT2FIX(0); - } - return num; -} - -// This is the enumerator version of `rb_int_dotimes`: https://github.com/ruby/ruby/blob/ruby_2_7/numeric.c#L5196-L5219 -// `RETURN_SIZED_ENUMERATOR` is what causes the early return in that version when no block is passed, but here we know -// that no block is passed and unconditionally make the enumerator. -SORBET_INLINE -VALUE sorbet_rb_int_dotimes(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - return rb_enumeratorize_with_size(recv, ID2SYM(fun), argc, argv, sorbet_int_dotimes_size); -} - -// This is the looping version of `rb_int_dotimes`: https://github.com/ruby/ruby/blob/ruby_2_7/numeric.c#L5196-L5219 -// The implementation is very similar, but we call the block function directly instead of using `rb_yield` to aid -// inlining. -SORBET_INLINE -VALUE sorbet_rb_int_dotimes_withBlock(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - const struct rb_captured_block *captured, VALUE closure, int numPositionalArgs) { - rb_check_arity(argc, 0, 0); - - sorbet_pushBlockFrame(captured); - - if (LIKELY(FIXNUM_P(recv))) { - long i, end; - - end = FIX2LONG(recv); - for (i = 0; i < end; i++) { - VALUE val = LONG2FIX(i); - blk(val, closure, 1, &val, Qnil); - } - } else { - VALUE i = INT2FIX(0); - - for (;;) { - if (!RTEST(rb_funcall(i, '<', 1, recv))) - break; - VALUE val = LONG2FIX(i); - blk(val, closure, 1, &val, Qnil); - i = rb_funcall(i, '+', 1, INT2FIX(1)); - } - } - - sorbet_popFrame(); - - return recv; -} - -static VALUE sorbet_hash_enum_size(VALUE recv, VALUE args, VALUE eobj) { - return rb_hash_size(recv); -} - -// This is the no-block version of rb_hash_each_pair: https://github.com/ruby/ruby/blob/ruby_2_7/hash.c#L3088-L3097 -// In that version, the `RETURN_SIZED_ENUMERATOR` macro is what causes the early return when a block is not passed. In -// this case, we know that the block wasn't passed, so we always return an enumerator -SORBET_INLINE -VALUE sorbet_rb_hash_each_pair(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 0, 0); - return rb_enumeratorize_with_size(recv, ID2SYM(fun), argc, argv, sorbet_hash_enum_size); -} - -struct sorbet_rb_hash_each_closure { - BlockFFIType fun; - VALUE toplevel_closure; -}; - -static int sorbet_rb_hash_each_fun_fast(VALUE key, VALUE value, VALUE closure) { - VALUE argv[2]; - argv[0] = key; - argv[1] = value; - struct sorbet_rb_hash_each_closure *c = (struct sorbet_rb_hash_each_closure *)closure; - c->fun(key, c->toplevel_closure, 2, &argv[0], Qnil); - return ST_CONTINUE; -} - -static int sorbet_rb_hash_each_fun_slow(VALUE key, VALUE value, VALUE closure) { - VALUE array = rb_assoc_new(key, value); - struct sorbet_rb_hash_each_closure *c = (struct sorbet_rb_hash_each_closure *)closure; - c->fun(key, c->toplevel_closure, 1, &array, Qnil); - return ST_CONTINUE; -} - -// This is the block version of rb_hash_each_pair: https://github.com/ruby/ruby/blob/ruby_2_7/hash.c#L3088-L3097 -// In that version the for loop uses `rb_yield`, whereas we call the block function pointer directly. -SORBET_INLINE -VALUE sorbet_rb_hash_each_pair_withBlock(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, - BlockFFIType blk, const struct rb_captured_block *captured, VALUE closure, - int numPositionalArgs) { - rb_check_arity(argc, 0, 0); - - // must push a frame for the captured block - sorbet_pushBlockFrame(captured); - - struct sorbet_rb_hash_each_closure passthrough; - passthrough.fun = blk; - passthrough.toplevel_closure = closure; - rb_hash_foreach(recv, numPositionalArgs > 1 ? sorbet_rb_hash_each_fun_fast : sorbet_rb_hash_each_fun_slow, - (VALUE)&passthrough); - - sorbet_popFrame(); - - return recv; -} - -// This is the no-block version of enum_each_with_object: -// https://github.com/ruby/ruby/blob/b0b7751f3b94e7983d124e43102f76ff598caabd/enum.c#L2749-L2757 In that version, the -// `RETURN_SIZED_ENUMERATOR` macro is what causes the early return when a block is not passed. In this case, we know -// that the block wasn't passed, so we always return an enumerator -SORBET_INLINE -VALUE sorbet_rb_hash_each_with_object(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 1, 1); - return rb_enumeratorize_with_size(recv, ID2SYM(fun), argc, argv, sorbet_hash_enum_size); -} - -struct sorbet_rb_hash_each_with_object_closure { - BlockFFIType fun; - VALUE toplevel_closure; - // We use this to pass to the block function; argv[0] is going to be rewritten - // on each iteration with the [k, v] array. argv[1] will be written once outside - // of the iteration with the object to be passed. - VALUE argv[2]; -}; - -static int sorbet_rb_hash_each_with_object_fun(VALUE key, VALUE value, VALUE closure) { - // TODO: it would be nice if we could statically analyze the block that each_with_object - // is being called with and determine that we could safely re-use an array on every - // iteration,. - VALUE array = rb_assoc_new(key, value); - struct sorbet_rb_hash_each_with_object_closure *c = (struct sorbet_rb_hash_each_with_object_closure *)closure; - c->argv[0] = array; - c->fun(array, c->toplevel_closure, 2, &c->argv[0], Qnil); - return ST_CONTINUE; -} - -// This is the block version of Hash#each_with_object -- which doesn't exist as a separate C function in the VM. -// In the no-block version, above, the code uses `rb_yield`, whereas we call the block function pointer directly. -SORBET_INLINE -VALUE sorbet_rb_hash_each_with_object_withBlock(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, - BlockFFIType blk, const struct rb_captured_block *captured, - VALUE closure, int numPositionalArgs) { - rb_check_arity(argc, 1, 1); - - VALUE object = argv[0]; - - // must push a frame for the captured block - sorbet_pushBlockFrame(captured); - - struct sorbet_rb_hash_each_with_object_closure passthrough; - passthrough.fun = blk; - passthrough.toplevel_closure = closure; - passthrough.argv[1] = object; - rb_hash_foreach(recv, sorbet_rb_hash_each_with_object_fun, (VALUE)&passthrough); - - sorbet_popFrame(); - - return object; -} - -// This is the no-block version of `rb_hash_transform_values` -// https://github.com/sorbet/ruby/blob/12d8c43330278a744d6e45135d1a8735f23f5afe/hash.c#L3190-L3221 -SORBET_INLINE -VALUE sorbet_rb_hash_transform_values(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 0, 0); - return rb_enumeratorize_with_size(recv, ID2SYM(fun), argc, argv, sorbet_hash_enum_size); -} - -static int sorbet_transform_values_foreach_func(VALUE key, VALUE value, VALUE argp, int error) { - return ST_REPLACE; -} - -struct sorbet_rb_transform_values_closure { - BlockFFIType fun; - VALUE toplevel_closure; - VALUE hash; -}; - -static int sorbet_transform_values_foreach_replace(VALUE *key, VALUE *value, VALUE argp, int existing) { - struct sorbet_rb_transform_values_closure *passthrough = (struct sorbet_rb_transform_values_closure *)argp; - - VALUE new_value = passthrough->fun((VALUE)*value, passthrough->toplevel_closure, 1, value, Qnil); - RB_OBJ_WRITE(passthrough->hash, value, new_value); - return ST_CONTINUE; -} - -// This is the block version of `rb_hash_transform_values` -// https://github.com/sorbet/ruby/blob/12d8c43330278a744d6e45135d1a8735f23f5afe/hash.c#L3190-L3221 -SORBET_INLINE -VALUE sorbet_rb_hash_transform_values_withBlock(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, - BlockFFIType blk, const struct rb_captured_block *captured, - VALUE closure, int numPositionalArgs) { - // hash_copy isn't exported from the vm. rb_hash_dup does a little more work, so it might be worth re-exporting - // hash_copy at some point. - VALUE result = rb_hash_dup(recv); - - // This is the inlined version of SET_DEFAULT(result, Qnil); - FL_UNSET_RAW(result, RHASH_PROC_DEFAULT); - RHASH_SET_IFNONE(result, Qnil); - - if (!RHASH_EMPTY_P(recv)) { - struct sorbet_rb_transform_values_closure passthrough; - passthrough.fun = blk; - passthrough.toplevel_closure = closure; - passthrough.hash = result; - - sorbet_pushBlockFrame(captured); - rb_hash_stlike_foreach_with_replace(result, sorbet_transform_values_foreach_func, - sorbet_transform_values_foreach_replace, (VALUE)&passthrough); - sorbet_popFrame(); - } - - return result; -} - -struct sorbet_rb_hash_any_closure { - BlockFFIType fun; - VALUE toplevel_closure; - VALUE retval; -}; - -extern VALUE sorbet_rb_hash_any_forwarder(int argc, VALUE *argv, VALUE hash); - -// no-block rb_hash_any_p: https://github.com/ruby/ruby/blob/67f1cd20bfb97ff6e5a15d27c8ef06cdb97ed37a/hash.c#L4354-L4381 -SORBET_INLINE -VALUE sorbet_rb_hash_any(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - return sorbet_rb_hash_any_forwarder(argc, (VALUE *)argv, recv); -} - -static int sorbet_rb_hash_any_fun_fast(VALUE key, VALUE value, VALUE arg) { - VALUE argv[2]; - argv[0] = key; - argv[1] = value; - struct sorbet_rb_hash_any_closure *c = (struct sorbet_rb_hash_any_closure *)arg; - VALUE ret = c->fun(key, c->toplevel_closure, 2, argv, Qnil); - if (RTEST(ret)) { - c->retval = Qtrue; - return ST_STOP; - } - return ST_CONTINUE; -} - -static int sorbet_rb_hash_any_fun_slow(VALUE key, VALUE value, VALUE arg) { - VALUE array = rb_assoc_new(key, value); - struct sorbet_rb_hash_any_closure *c = (struct sorbet_rb_hash_any_closure *)arg; - VALUE ret = c->fun(key, c->toplevel_closure, 1, &array, Qnil); - if (RTEST(ret)) { - c->retval = Qtrue; - return ST_STOP; - } - return ST_CONTINUE; -} - -// block rb_hash_any_p: https://github.com/ruby/ruby/blob/67f1cd20bfb97ff6e5a15d27c8ef06cdb97ed37a/hash.c#L4354-L4381 -SORBET_INLINE -VALUE sorbet_rb_hash_any_withBlock(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - const struct rb_captured_block *captured, VALUE closure, int numPositionalArgs) { - // TODO: do we need to handle argc == 1? - rb_check_arity(argc, 0, 1); - - // correctness first - if (UNLIKELY(argc == 1)) { - return sorbet_rb_hash_any_forwarder(argc, (VALUE *)argv, recv); - } - - if (RHASH_EMPTY_P(recv)) { - return Qfalse; - } - - // must push a frame for the captured block - sorbet_pushBlockFrame(captured); - - struct sorbet_rb_hash_any_closure passthrough; - passthrough.fun = blk; - passthrough.toplevel_closure = closure; - passthrough.retval = Qfalse; - // Hash#any is a little weird: the VM will dynamically check the arity of the - // block and pass a different number of args as appropriate. This mimics the - // array unpacking that blocks would normally do, except that we can do it - // faster in the caller because we don't have to allocate the array each time. - rb_hash_foreach(recv, numPositionalArgs > 1 ? sorbet_rb_hash_any_fun_fast : sorbet_rb_hash_any_fun_slow, - (VALUE)&passthrough); - - sorbet_popFrame(); - - return passthrough.retval; -} - -VALUE sorbet_rb_hash_keys(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - sorbet_ensure_arity(argc, 0); - return rb_hash_keys(recv); -} - -VALUE sorbet_rb_hash_values(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - sorbet_ensure_arity(argc, 0); - return rb_hash_values(recv); -} - -struct sorbet_rb_hash_update_withBlock_i_args { - VALUE hash; - VALUE closure; - BlockFFIType blk; -}; - -// Block version of Hash#update: https://github.com/ruby/ruby/blob/ruby_2_7/hash.c#L3869-L3886 -SORBET_INLINE -VALUE sorbet_rb_hash_update_withBlock(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - const struct rb_captured_block *captured, VALUE closure, int numPositionalArgs) { - int i; - - // This line inlined from rb_hash_modify: - rb_check_frozen(recv); - - // must push a frame for the captured block - sorbet_pushBlockFrame(captured); - - for (i = 0; i < argc; i++) { - VALUE hash = rb_to_hash_type(argv[i]); - struct sorbet_rb_hash_update_withBlock_i_args args = {recv, closure, blk}; - rb_hash_foreach(hash, sorbet_rb_hash_update_withBlock_i, (VALUE)&args); - } - - sorbet_popFrame(); - - return recv; -} - -// No-block version of Hash#merge: -// https://github.com/ruby/ruby/blob/ac7c2754c004cdb3618738e315d2e2cb5f68a3a8/hash.c#L3973-L3977 -SORBET_INLINE -VALUE sorbet_rb_hash_merge(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - return sorbet_rb_hash_update(rb_hash_dup(recv), fun, argc, argv, blk, closure); -} - -// Block version of Hash#merge: -// https://github.com/ruby/ruby/blob/ac7c2754c004cdb3618738e315d2e2cb5f68a3a8/hash.c#L3973-L3977 -SORBET_INLINE -VALUE sorbet_rb_hash_merge_withBlock(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - const struct rb_captured_block *captured, VALUE closure, int numPositionalArgs) { - return sorbet_rb_hash_update_withBlock(rb_hash_dup(recv), fun, argc, argv, blk, captured, closure, - numPositionalArgs); -} - -// This is the no-block version of rb_hash_select: https://github.com/ruby/ruby/blob/ruby_2_7/hash.c#L2694-L2705 -// In that version, the `RETURN_SIZED_ENUMERATOR` macro is what causes the early return when a block is not passed. In -// this case, we know that the block wasn't passed, so we always return an enumerator -SORBET_INLINE -VALUE sorbet_rb_hash_select(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - sorbet_ensure_arity(argc, 0); - return rb_enumeratorize_with_size(recv, ID2SYM(fun), argc, argv, sorbet_hash_enum_size); -} - -struct sorbet_select_i_args { - VALUE result; - BlockFFIType blk; - VALUE closure; -}; - -static int sorbet_select_i(VALUE key, VALUE value, VALUE argsv) { - struct sorbet_select_i_args *args = (struct sorbet_select_i_args *)argsv; - VALUE block_argv[2] = {key, value}; - if (RTEST(args->blk(key, args->closure, 2, &block_argv[0], Qnil))) { - rb_hash_aset(args->result, key, value); - } - return ST_CONTINUE; -} - -// This is the block version of rb_hash_select: https://github.com/ruby/ruby/blob/ruby_2_7/hash.c#L2694-L2705 -SORBET_INLINE -VALUE sorbet_rb_hash_select_withBlock(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - const struct rb_captured_block *captured, VALUE closure, int numPositionalArgs) { - sorbet_ensure_arity(argc, 0); - - // must push a frame for the captured block - sorbet_pushBlockFrame(captured); - - struct sorbet_select_i_args args = {rb_hash_new(), blk, closure}; - if (!RHASH_EMPTY_P(recv)) { - rb_hash_foreach(recv, sorbet_select_i, (VALUE)&args); - } - - sorbet_popFrame(); - - return args.result; -} - -// **** -// **** Name Based Intrinsics -// **** - -SORBET_INLINE -VALUE sorbet_magic_toHashDup(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - sorbet_ensure_arity(argc, 1); - - // if this is too slow, we can inline the type conversion code - VALUE hash = rb_to_hash_type(argv[0]); - - return rb_hash_dup(hash); -} - -SORBET_INLINE -VALUE sorbet_magic_toHashNoDup(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - sorbet_ensure_arity(argc, 1); - - // if this is too slow, we can inline the type conversion code - return rb_to_hash_type(argv[0]); -} - -SORBET_INLINE -VALUE sorbet_magic_mergeHash(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - sorbet_ensure_arity(argc, 2); - - return sorbet_magic_mergeHashHelper(argv[0], argv[1]); -} - -SORBET_INLINE -VALUE sorbet_magic_mergeHashValues(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - if (UNLIKELY((argc % 2) != 1)) { - sorbet_raiseArity(argc, 1, UNLIMITED_ARGUMENTS); - } - - VALUE hash = argv[0]; - rb_hash_bulk_insert(argc - 1, argv + 1, hash); - return hash; -} - -SORBET_INLINE -VALUE sorbet_splatIntrinsic(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - sorbet_ensure_arity(argc, 1); - return sorbet_vm_splatIntrinsic(argv[0]); -} - -SORBET_INLINE -VALUE sorbet_expandSplatIntrinsic(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - sorbet_ensure_arity(argc, 3); - return sorbet_vm_expandSplatIntrinsic(argv[0], argv[1], argv[2]); -} - -SORBET_INLINE -VALUE sorbet_buildHashIntrinsic(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - return sorbet_hashBuild(argc, argv); -} - -SORBET_INLINE -VALUE sorbet_buildArrayIntrinsic(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - if (argc == 0) { - return rb_ary_new(); - } - return rb_ary_new_from_values(argc, argv); -} - -SORBET_INLINE -VALUE sorbet_buildRangeIntrinsic(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - sorbet_ensure_arity(argc, 3); - - VALUE start = argv[0]; - VALUE end = argv[1]; - VALUE excludeEnd = argv[2]; - return rb_range_new(start, end, excludeEnd); -} - -SORBET_INLINE -VALUE sorbet_check_match_array(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - sorbet_ensure_arity(argc, 2); - return sorbet_vm_check_match_array(GET_EC(), argv[0], argv[1]); -} - -SORBET_INLINE -VALUE sorbet_int_bool_true(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - return Qtrue; -} - -SORBET_INLINE -VALUE sorbet_int_bool_false(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - return Qfalse; -} - -SORBET_INLINE -VALUE sorbet_int_bool_and(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - sorbet_ensure_arity(argc, 1); - if (argv[0] != Qnil && argv[0] != Qfalse) { - return Qtrue; - } - return Qfalse; -} - -SORBET_INLINE -VALUE sorbet_int_bool_nand(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - sorbet_ensure_arity(argc, 1); - if (argv[0] != Qnil && argv[0] != Qfalse) { - return Qfalse; - } - return Qtrue; -} - -SORBET_INLINE -VALUE sorbet_nil_p(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, VALUE closure) { - return sorbet_isa_NilClass(recv) ? Qtrue : Qfalse; -} - -SORBET_INLINE -VALUE sorbet_returnRecv(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - return recv; -} - -SORBET_INLINE -VALUE sorbet_int_str_uplus(VALUE recv, ID fun, int argc, VALUE *argv, BlockFFIType blk, VALUE closure) { - rb_check_arity(argc, 0, 0); - return sorbet_vm_str_uplus(recv); -} - -// **** -// **** Calls -// **** - -SORBET_INLINE -_Bool sorbet_is_kwsplat_calling(void *callingp) { - struct rb_calling_info *calling = (struct rb_calling_info *)callingp; - return calling->kw_splat != 0; -} - -SORBET_INLINE -_Bool sorbet_is_kwarg_calldata(void *cdp) { - struct rb_call_data *cd = (struct rb_call_data *)cdp; - return (cd->ci.flag & VM_CALL_KWARG) != 0; -} - -SORBET_INLINE -_Bool sorbet_can_efficiently_parse_kwargs(VALUE maybeHash, void *callingp, void *cdp) { - // We have a kwargs hash, so take the slower path. - if (maybeHash != RUBY_Qundef) { - return false; - } - - return !sorbet_is_kwsplat_calling(callingp) && sorbet_is_kwarg_calldata(cdp); -} - -SORBET_INLINE -VALUE sorbet_kwarg_passed_value(void *cdp, ID kwarg, VALUE *kwargv) { - struct rb_kwarg_call_data *cd = (struct rb_kwarg_call_data *)cdp; - struct rb_call_info_kw_arg *ci_kw_arg = cd->ci_kw.kw_arg; - VALUE kwarg_sym = rb_id2sym(kwarg); - for (int i = 0, len = ci_kw_arg->keyword_len; i < len; ++i) { - if (kwarg_sym == ci_kw_arg->keywords[i]) { - return kwargv[i]; - } - } - - return RUBY_Qundef; -} - -// When no double-splat is present, only lookup entries in the keyword argument hash, don't delete them. -SORBET_INLINE -VALUE sorbet_getKWArg(VALUE maybeHash, VALUE key) { - if (maybeHash == RUBY_Qundef) { - return RUBY_Qundef; - } - - return rb_hash_lookup2(maybeHash, key, RUBY_Qundef); -} - -// When building up a double-splat, reuse the original hash for the double-splat arg by deleting the entries that we -// parse out of it. -SORBET_INLINE -VALUE sorbet_removeKWArg(VALUE maybeHash, VALUE key) { - if (maybeHash == RUBY_Qundef) { - return RUBY_Qundef; - } - - return rb_hash_delete_entry(maybeHash, key); -} - -// The compiler has intimate knowledge of the layout of this structure. -struct sorbet_kwsplat_arg_context { - int argc; - VALUE hashArgs; -}; - -// This function is called from function argument validation to determine if -// the function has been passed a kwsplat argument. -SORBET_INLINE -struct sorbet_kwsplat_arg_context sorbet_determineKwSplatArg(int argc, VALUE *argv, int minPositionalArgCount) { - const VALUE noHash = RUBY_Qundef; - - // If we definitely have enough arguments to fill out the minimum number - // of arguments, then the last argument might be a kwsplat hash for - // keyword args. We'll need to adjust the actual argument count for our - // caller, who will use the adjusted value to validate the function's - // arity (if appropriate). - - if (minPositionalArgCount < argc) { - int argcWithoutHashCount = argc - 1; - VALUE maybeHash = argv[argcWithoutHashCount]; - if (sorbet_isa_Hash(maybeHash)) { - struct sorbet_kwsplat_arg_context ctx = {argcWithoutHashCount, maybeHash}; - return ctx; - } - } - - struct sorbet_kwsplat_arg_context ctx = {argc, noHash}; - return ctx; -} - -SORBET_INLINE -void sorbet_assertAllRequiredKWArgs(VALUE missing) { - if (LIKELY(missing == RUBY_Qundef)) { - return; - } - - sorbet_raiseMissingKeywords(missing); -} - -SORBET_INLINE -VALUE sorbet_assertNoExtraKWArg(VALUE maybeHash, int requiredKwargs, int optionalParsed) { - if (maybeHash == RUBY_Qundef) { - return RUBY_Qundef; - } - - int size = rb_hash_size_num(maybeHash); - if (LIKELY((size - requiredKwargs) == optionalParsed)) { - return RUBY_Qundef; - } - - sorbet_raiseExtraKeywords(maybeHash); -} - -SORBET_INLINE -void sorbet_assertCallDataNoExtraKWArg(void *cdp, int requiredKwargs, int optionalParsed) { - struct rb_kwarg_call_data *cd = (struct rb_kwarg_call_data *)cdp; - struct rb_call_info_kw_arg *ci_kw_arg = cd->ci_kw.kw_arg; - if (ci_kw_arg->keyword_len == 0) { - return; - } - - if (LIKELY((ci_kw_arg->keyword_len - requiredKwargs) == optionalParsed)) { - return; - } - - // TODO: this is not quite right, since we're not specifying the particular - // keywords that are missing. - sorbet_raiseCallDataExtraKeywords(ci_kw_arg->keyword_len, &ci_kw_arg->keywords[0]); -} - -SORBET_INLINE -VALUE sorbet_readKWRestArgs(VALUE maybeHash) { - // This is similar to what the Ruby VM does: - // https://github.com/ruby/ruby/blob/37c2cd3fa47c709570e22ec4dac723ca211f423a/vm_args.c#L483-L487 - if (maybeHash == RUBY_Qundef) { - return rb_hash_new(); - } - return rb_hash_dup(maybeHash); -} - -SORBET_INLINE -VALUE sorbet_readRestArgs(int maxPositionalArgCount, int actualArgCount, VALUE *argArray) { - if (maxPositionalArgCount >= actualArgCount) { - return rb_ary_new(); - } - return rb_ary_new_from_values(actualArgCount - maxPositionalArgCount, argArray + maxPositionalArgCount); -} - -SORBET_INLINE -const VALUE **sorbet_getPc(rb_control_frame_t *cfp) { - return &cfp->pc; -} - -SORBET_INLINE -void sorbet_setLineNumber(int offset, VALUE *encoded, VALUE **storeLocation) { - // use pos+1 because PC should point at the next instruction - (*storeLocation) = encoded + offset + 1; -} - -// https://github.com/ruby/ruby/blob/a9a48e6a741f048766a2a287592098c4f6c7b7c7/vm_insnhelper.h#L123 -#define GET_PREV_EP(ep) ((VALUE *)((ep)[VM_ENV_DATA_INDEX_SPECVAL] & ~0x03)) - -// get sp from the cfp -SORBET_INLINE -VALUE **sorbet_get_sp(rb_control_frame_t *cfp) { - return &(cfp->sp); -} -KEEP_ALIVE(sorbet_get_sp); - -// Push an entry to the ruby value stack and return the new sp -SORBET_INLINE -VALUE *sorbet_pushValueStack(VALUE *sp, const VALUE val) { - *sp = val; - return (sp + 1); -} -KEEP_ALIVE(sorbet_pushValueStack); - -// https://github.com/ruby/ruby/blob/a9a48e6a741f048766a2a287592098c4f6c7b7c7/vm_insnhelper.c#L2919-L2928 -static const VALUE *vm_get_ep(const VALUE *const reg_ep, rb_num_t lv) { - rb_num_t i; - const VALUE *ep = reg_ep; - for (i = 0; i < lv; i++) { - ep = GET_PREV_EP(ep); - } - return ep; -} - -SORBET_INLINE -static int computeLocalIndex(long index) { - // Local offset calculation needs to take into account the fixed values that - // are present on the stack: - // https://github.com/ruby/ruby/blob/a9a48e6a741f048766a2a287592098c4f6c7b7c7/compile.c#L1509 - return index + VM_ENV_DATA_SIZE; -} - -// Read a value from the locals from this stack frame. -// -// * index - local var index -// * level - the number of blocks that need to be crossed to reach the -// outer-most stack frame. -SORBET_INLINE -VALUE sorbet_readLocal(rb_control_frame_t *cfp, long index, long level) { - int offset = computeLocalIndex(index); - return *(vm_get_ep(cfp->ep, level) - offset); -} - -// https://github.com/ruby/ruby/blob/a9a48e6a741f048766a2a287592098c4f6c7b7c7/vm_insnhelper.c#L361-L371 -SORBET_INLINE -static inline void vm_env_write(const VALUE *ep, int index, VALUE v) { - VALUE flags = ep[VM_ENV_DATA_INDEX_FLAGS]; - if (LIKELY((flags & VM_ENV_FLAG_WB_REQUIRED) == 0)) { - VM_STACK_ENV_WRITE(ep, index, v); - } else { - sorbet_vm_env_write_slowpath(ep, index, v); - } -} - -// Called after intrinsic implementations to run any cleanup actions that the vm would normally have done. -// TODO: This might be a good candidate for storing in the vm-payload, as RUBY_VM_CHECK_INTS will likely inline. -SORBET_INLINE -void sorbet_afterIntrinsic() { - rb_execution_context_t *ec = GET_EC(); - RUBY_VM_CHECK_INTS(ec); -} - -// Write a value into the locals from this stack frame. -// -// * index - local var index -// * level - the number of blocks that need to be crossed to reach the -// outer-most stack frame. -// * value - the value to write -SORBET_INLINE -void sorbet_writeLocal(rb_control_frame_t *cfp, long index, long level, VALUE value) { - int offset = computeLocalIndex(index); - vm_env_write(vm_get_ep(cfp->ep, level), -offset, value); -} - -SORBET_INLINE -VALUE sorbet_vmBlockHandlerNone() { - return VM_BLOCK_HANDLER_NONE; -} -KEEP_ALIVE(sorbet_vmBlockHandlerNone); - -static VALUE sorbet_iterMethod(VALUE obj) { - struct FunctionInlineCache *cache = (struct FunctionInlineCache *)obj; - - // In this case we know that a block handler was set, as we only emit calls to this payload function from sends that - // pass a block argument. - rb_execution_context_t *ec = GET_EC(); - VALUE bh = ec->passed_block_handler; - - // It's important that we clear out the passed block handler state in the execution context: - // https://github.com/ruby/ruby/blob/ruby_2_7/vm.c#L203-L210 - ec->passed_block_handler = VM_BLOCK_HANDLER_NONE; - - return sorbet_callFuncWithCache(cache, bh); -} - -static VALUE sorbet_iterSuperMethod(VALUE obj) { - struct FunctionInlineCache *cache = (struct FunctionInlineCache *)obj; - - // In this case we know that a block handler was set, as we only emit calls to this payload function from sends that - // pass a block argument. - rb_execution_context_t *ec = GET_EC(); - VALUE bh = ec->passed_block_handler; - - // It's important that we clear out the passed block handler state in the execution context: - // https://github.com/ruby/ruby/blob/ruby_2_7/vm.c#L203-L210 - ec->passed_block_handler = VM_BLOCK_HANDLER_NONE; - - return sorbet_callSuperFuncWithCache(cache, bh); -} - -SORBET_INLINE -const struct vm_ifunc *sorbet_buildBlockIfunc(BlockFFIType blockImpl, int blkMinArgs, int blkMaxArgs, VALUE closure) { - return rb_vm_ifunc_new(blockImpl, (void *)closure, blkMinArgs, blkMaxArgs); -} -KEEP_ALIVE(sorbet_buildBlockIfunc); - -SORBET_INLINE -VALUE sorbet_callFuncBlockWithCache(struct FunctionInlineCache *cache, const struct vm_ifunc *ifunc) { - return sorbet_rb_iterate(sorbet_iterMethod, (VALUE)cache, ifunc); -} -KEEP_ALIVE(sorbet_callFuncBlockWithCache); - -SORBET_INLINE -VALUE sorbet_callSuperFuncBlockWithCache(struct FunctionInlineCache *cache, const struct vm_ifunc *ifunc) { - return sorbet_rb_iterate(sorbet_iterSuperMethod, (VALUE)cache, ifunc); -} -KEEP_ALIVE(sorbet_callSuperFuncBlockWithCache); - -SORBET_INLINE -VALUE sorbet_callFuncBlockWithCache_noBreak(struct FunctionInlineCache *cache, const struct vm_ifunc *ifunc) { - rb_execution_context_t *ec = GET_EC(); - rb_control_frame_t *cfp = ec->cfp; - - // This is an inlined version of the block handler setup that `rb_iterate` performs. See the following two links for - // the use of `rb_vm_ifunc_proc_new` and the setup of the captured block handler. - // * https://github.com/ruby/ruby/blob/ruby_2_7/vm_eval.c#L1448 - // * https://github.com/ruby/ruby/blob/ruby_2_7/vm_eval.c#L1406-L1408 - struct rb_captured_block *captured = (struct rb_captured_block *)&cfp->self; - captured->code.ifunc = ifunc; - - // It's important that we clear out the passed block handler state in the execution context: - // https://github.com/ruby/ruby/blob/ruby_2_7/vm.c#L203-L210 - ec->passed_block_handler = VM_BLOCK_HANDLER_NONE; - - // We don't need to pass the block handler through ec->passed_block_handler in this case. - return sorbet_callFuncWithCache(cache, VM_BH_FROM_IFUNC_BLOCK(captured)); -} -KEEP_ALIVE(sorbet_callFuncBlockWithCache_noBreak); - -SORBET_INLINE -VALUE sorbet_callSuperFuncBlockWithCache_noBreak(struct FunctionInlineCache *cache, const struct vm_ifunc *ifunc) { - rb_execution_context_t *ec = GET_EC(); - rb_control_frame_t *cfp = ec->cfp; - - // This is an inlined version of the block handler setup that `rb_iterate` performs. See the following two links for - // the use of `rb_vm_ifunc_proc_new` and the setup of the captured block handler. - // * https://github.com/ruby/ruby/blob/ruby_2_7/vm_eval.c#L1448 - // * https://github.com/ruby/ruby/blob/ruby_2_7/vm_eval.c#L1406-L1408 - struct rb_captured_block *captured = (struct rb_captured_block *)&cfp->self; - captured->code.ifunc = ifunc; - - // It's important that we clear out the passed block handler state in the execution context: - // https://github.com/ruby/ruby/blob/ruby_2_7/vm.c#L203-L210 - ec->passed_block_handler = VM_BLOCK_HANDLER_NONE; - - // We don't need to pass the block handler through ec->passed_block_handler in this case. - return sorbet_callSuperFuncWithCache(cache, VM_BH_FROM_IFUNC_BLOCK(captured)); -} -KEEP_ALIVE(sorbet_callSuperFuncBlockWithCache_noBreak); - -SORBET_INLINE -VALUE sorbet_makeBlockHandlerProc(VALUE block) { - if (block == Qnil) { - return VM_BLOCK_HANDLER_NONE; - } - return rb_funcall(block, rb_intern2("to_proc", 7), 0); -} - -SORBET_INLINE -bool sorbet_isCachedMethod(struct FunctionInlineCache *cache, VALUE (*expectedFnPtr)(), VALUE recv) { - // Assumes that the cache is already up-to-date. If you haven't done this - // yourself, call: `sorbet_vmMethodSearch(cache, recv)` - - rb_method_definition_t *def = cache->cd.cc.me->def; - return (def->type == VM_METHOD_TYPE_CFUNC) && (def->body.cfunc.func == expectedFnPtr); -} - -SORBET_INLINE -VALUE sorbet_callFuncDirect(struct FunctionInlineCache *cache, rb_sorbet_func_t methodPtr, int argc, VALUE *argv, - VALUE recv, rb_iseq_t *iseq) { - // we need a method entry from the call data to be able to setup the stack correctly. - if (UNLIKELY(cache->cd.cc.me == NULL)) { - sorbet_vmMethodSearch(cache, recv); - } - - rb_control_frame_t *cfp = sorbet_pushCfuncFrame(cache, recv, iseq); - struct rb_calling_info calling; - calling.block_handler = VM_BLOCK_HANDLER_NONE; - calling.kw_splat = (cache->cd.ci_kw.ci.flag & VM_CALL_KW_SPLAT) > 0; - calling.recv = recv; - calling.argc = argc; - VALUE res = methodPtr(argc, argv, recv, cfp, &calling, &cache->cd); - sorbet_popFrame(); - return res; -} - -SORBET_INLINE -VALUE sorbet_callStaticInitDirect(rb_sorbet_func_t methodPtr, int argc, VALUE *argv, VALUE recv) { - rb_control_frame_t *cfp = sorbet_pushStaticInitFrame(recv); - struct rb_calling_info calling; - calling.block_handler = VM_BLOCK_HANDLER_NONE; - calling.kw_splat = 0; - calling.recv = recv; - calling.argc = argc; - // XXX we don't really have a call data here, but we shouldn't be parsing any kwargs either. - VALUE res = methodPtr(argc, argv, recv, cfp, &calling, NULL); - sorbet_popFrame(); - return res; -} - -struct sorbet_inlineIntrinsicEnv { - VALUE recv; - ID fun; - int argc; - VALUE *argv; - VALUE closure; -}; - -// This function should always inline, as it will be the body of the functions generated by the `CallCMethod` -// symbol-based intrinsic method when a block handling function is provided. The body will always be given the intrinsic -// and block function directly, so this will turn into a direct call to the intrinsic since LLVM will inline this -// function. -SORBET_INLINE -VALUE sorbet_inlineIntrinsicEnv_apply(VALUE value, BlockConsumerFFIType intrinsic, BlockFFIType blk, - int numPositionalArgs) { - // fetch the captured block so that it's available for the intrinsic to push a frame - rb_execution_context_t *ec = GET_EC(); - VALUE block_handler = ec->passed_block_handler; - const struct rb_captured_block *captured = VM_BH_TO_IFUNC_BLOCK(block_handler); - - // It's important that we clear out the passed block handler state in the execution context: - // https://github.com/ruby/ruby/blob/ruby_2_7/vm.c#L203-L210 - ec->passed_block_handler = VM_BLOCK_HANDLER_NONE; - - struct sorbet_inlineIntrinsicEnv *env = (struct sorbet_inlineIntrinsicEnv *)value; - return intrinsic(env->recv, env->fun, env->argc, env->argv, blk, captured, env->closure, numPositionalArgs); -} - -SORBET_INLINE -VALUE sorbet_callIntrinsicInlineBlock(VALUE (*body)(VALUE), VALUE recv, ID fun, int argc, VALUE *argv, - const struct vm_ifunc *ifunc, VALUE closure) { - struct sorbet_inlineIntrinsicEnv env; - env.recv = recv; - env.fun = fun; - env.argc = argc; - env.argv = argv; - env.closure = closure; - - // NOTE: we pass the block function to rb_iterate so that we ensure that the block handler is setup correctly. - // However it won't be called through the vm, as that would hide the direct call to the block function from the - // inliner. - return sorbet_rb_iterate(body, (VALUE)&env, ifunc); -} - -SORBET_INLINE -VALUE sorbet_callIntrinsicInlineBlock_noBreak(VALUE (*body)(VALUE), VALUE recv, ID fun, int argc, VALUE *argv, - const struct vm_ifunc *ifunc, VALUE closure) { - struct sorbet_inlineIntrinsicEnv env; - env.recv = recv; - env.fun = fun; - env.argc = argc; - env.argv = argv; - env.closure = closure; - - // In the case that we don't see `break` used in the block, it's OK to skip setting up the tag stack and instead - // directly allocate the resources needed to setup the block frame. The setup before the call to the `body` - // function is a combination of the conditional allocation from `rb_iterate` and the setup of the block handler - // when that allocation was successful. - - rb_execution_context_t *ec = GET_EC(); - rb_control_frame_t *cfp = ec->cfp; - - struct rb_captured_block *captured = (struct rb_captured_block *)&cfp->self; - captured->code.ifunc = ifunc; - VALUE blockHandler = VM_BH_FROM_IFUNC_BLOCK(captured); - ec->passed_block_handler = blockHandler; - - return body((VALUE)&env); -} - -SORBET_CONSTANT(unsigned int, sorbet_vmCallKwarg, VM_CALL_KWARG); -SORBET_CONSTANT(unsigned int, sorbet_vmCallArgsSimple, VM_CALL_ARGS_SIMPLE); -SORBET_CONSTANT(unsigned int, sorbet_vmCallArgsSplat, VM_CALL_ARGS_SPLAT); -SORBET_CONSTANT(unsigned int, sorbet_vmCallKwSplat, VM_CALL_KW_SPLAT); -SORBET_CONSTANT(unsigned int, sorbet_vmCallFCall, VM_CALL_FCALL); -SORBET_CONSTANT(unsigned int, sorbet_vmCallArgsBlockarg, VM_CALL_ARGS_BLOCKARG); - -// static struct rb_kwarg_call_data test_cd = {0}; - -SORBET_INLINE -const rb_control_frame_t *sorbet_setRubyStackFrame(_Bool isStaticInit, int iseq_type, rb_iseq_t *iseq) { - rb_execution_context_t *ec = GET_EC(); - rb_control_frame_t *cfp = ec->cfp; - - // Depending on what kind of iseq we're switching to, we need to push a frame on the ruby stack. - if (iseq_type == ISEQ_TYPE_RESCUE || iseq_type == ISEQ_TYPE_ENSURE) { - sorbet_setExceptionStackFrame(ec, cfp, iseq); - } else if (iseq_type == ISEQ_TYPE_BLOCK) { - cfp->iseq = iseq; - VM_ENV_FLAGS_UNSET(cfp->ep, VM_FRAME_FLAG_CFRAME); - } else if (isStaticInit) { - cfp->iseq = iseq; - - // NOTE: we unset CFRAME here to convince the VM that this is a method that has a valid iseq backing it, that - // can be used to reconstruct line numbers. This works in concert with setting the iseq for the cfp above, and - // also with sorbet_setLineNumber. - // - // NOTE: we unset FINISH here to avoid issues with exceptions bubbling out of a static-init context. - // Specifically, we need to ensure that we don't jump to a tag in - // https://github.com/ruby/ruby/blob/a9a48e6a741f048766a2a287592098c4f6c7b7c7/vm.c#L2112 - // as this context will never have a tag pushed for it, and jumping in the context of this frame would break - // assumptions that those handlers make about the ruby stacks. - VM_ENV_FLAGS_UNSET(cfp->ep, VM_FRAME_FLAG_FINISH); - - sorbet_setMethodStackFrame(ec, cfp, iseq); - } - - return cfp; -} - -SORBET_INLINE -void *sorbet_allocateParamInfo() { - rb_sorbet_param_t *info = ZALLOC(rb_sorbet_param_t); - // Make it a type that's easy to deal with in LLVM IR. - return (void *)info; -} - -// It's not worth building this into Ruby; all of these parameters are expected to -// be compile-time constants, and building the arguments for the call into Ruby -// would be about as expensive as doing all the stores below. Plus the compiler -// can fold the stores into body->param.flags together in this version. -SORBET_INLINE -void sorbet_setParamInfo(void *paramp, bool has_lead, bool has_opt, bool has_rest, bool has_post, bool has_kw, - bool has_kwrest, bool has_block, bool accepts_no_kwarg, int lead_num, int opt_num, - int rest_start, int post_start, int post_num, int block_start, unsigned int size) { - rb_sorbet_param_t *param = (rb_sorbet_param_t *)paramp; - param->flags.has_lead = has_lead; - param->flags.has_opt = has_opt; - param->flags.has_rest = has_rest; - param->flags.has_post = has_post; - param->flags.has_kw = has_kw; - param->flags.has_kwrest = has_kwrest; - param->flags.has_block = has_block; - param->flags.accepts_no_kwarg = accepts_no_kwarg; - - param->lead_num = lead_num; - param->opt_num = opt_num; - param->rest_start = rest_start; - param->post_start = post_start; - param->post_num = post_num; - param->block_start = block_start; - - param->size = size; - - // param->{opt_table, keyword} are set up elsewhere. -} - -// TODO(froydnj): we may want to put this in the VM payload. -SORBET_INLINE -void sorbet_setupParamPositional(void *paramp, int n_ids, ID *table) { - rb_sorbet_param_t *param = (rb_sorbet_param_t *)paramp; - - // This table is never freed. - ID *installed = ALLOC_N(ID, n_ids); - MEMCPY(installed, table, ID, n_ids); - param->pos_table = installed; -} - -SORBET_INLINE -void sorbet_setupParamKeywords(void *paramp, int kw_num, int required_num, int n_ids, ID *table) { - rb_sorbet_param_t *param = (rb_sorbet_param_t *)paramp; - - param->kw_num = kw_num; - param->kw_required_num = required_num; - - // kw_bits_start (rb_iseq_param_keyword::bits_start) is only cryptically - // documented as "keyword_bits" in the comment copied from vm_core.h that - // lives in Payload.cc. Reverse-engineering compile.c and consulting - // RubyVM::InstructionSequence.compile("...").to_a suggests that - // bits_start is the index just after the keyword args end. - param->kw_bits_start = param->lead_num + param->opt_num + kw_num; - // And, likewise, kw_rest_start (rb_iseq_param_keyword::rest_start) - // appears to be one more after that...assuming, of course, that we have - // **rest. - if (param->flags.has_kwrest) { - param->kw_rest_start = param->kw_bits_start + 1; - } - - // This table is never freed. - ID *kwtab = ALLOC_N(ID, n_ids); - MEMCPY(kwtab, table, ID, n_ids); - param->kw_table = kwtab; -} - -// **** -// **** Exceptions -// **** - -SORBET_INLINE -VALUE sorbet_getTRetry() { - static const char retry[] = "T::Private::Retry::RETRY"; - return sorbet_getConstant(retry, sizeof(retry)); -} - -__attribute__((__noreturn__)) VALUE sorbet_block_break(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, - BlockFFIType blk, VALUE closure) { - rb_iter_break_value(argv[0]); -} - -// Raise the exception value, unless it's nil. -SORBET_INLINE -void sorbet_raiseIfNotNil(VALUE exception) { - if (exception == RUBY_Qnil) { - return; - } - - rb_exc_raise(exception); -} - -// Ruby passes the RTLD_LAZY flag to the dlopen(3) call (which is supported by both macOS and Linux). -// That flag says, "Only resolve symbols as the code that references them is executed. If the symbol -// is never referenced, then it is never resolved." -// -// Thus, by putting our version check first before any other code in the C extension runs, and backing -// up the symbols our version check relies on with weak symbols, we can guarantee that the user never -// sees a symbol resolution error from loading a shared object when they shouldn't have. -SORBET_INLINE -void sorbet_ensureSorbetRuby(int compile_time_is_release_build, char *compile_time_build_scm_revision) { - if (!compile_time_is_release_build) { - // Skipping version check: This shared object was compiled by a non-release version of SorbetLLVM - return; - } - - const int runtime_is_release_build = sorbet_getIsReleaseBuild(); - if (!runtime_is_release_build) { - // Skipping version check: sorbet_ruby is a non-release version - return; - } - - const char *runtime_build_scm_revision = sorbet_getBuildSCMRevision(); - if (strcmp(compile_time_build_scm_revision, runtime_build_scm_revision) != 0) { - rb_raise(rb_eRuntimeError, - "SorbetLLVM runtime version mismatch: sorbet_ruby compiled with %s but shared object compiled with %s", - runtime_build_scm_revision, compile_time_build_scm_revision); - } -} - -// **** -// **** sorbet_ruby version information fallback -// **** - -// A strong version of these functions will be linked into libruby.so when Ruby is built as sorbet_ruby. -// When our compiled C extensions are loaded by sorbet_ruby, calls will resolve to the symbol inside libruby.so. -// When our compiled C extensions are loaded by a system Ruby or an rbenv-built Ruby, these weak symbols act as -// a fallback so that we can gracefully exit (Ruby exception) when not run under sorbet_ruby instead of -// ungracefully exit (dynamic symbol resolution error + corrupt Ruby VM). -const char *sorbet_getBuildSCMRevision() __attribute__((weak)) { - rb_raise(rb_eRuntimeError, - "sorbet_getBuildSCMRevision: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby."); -} - -const int sorbet_getIsReleaseBuild() __attribute__((weak)) { - rb_raise(rb_eRuntimeError, - "sorbet_getIsReleaseBuild: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby."); -} diff --git a/compiler/IREmitter/Payload/patches/BUILD b/compiler/IREmitter/Payload/patches/BUILD deleted file mode 100644 index f7fb7c5d6e..0000000000 --- a/compiler/IREmitter/Payload/patches/BUILD +++ /dev/null @@ -1,5 +0,0 @@ -filegroup( - name = "vm_append_files", - srcs = glob(["*.c"]), - visibility = ["//visibility:public"], -) diff --git a/compiler/IREmitter/Payload/patches/array.c b/compiler/IREmitter/Payload/patches/array.c deleted file mode 100644 index 171e6ecdac..0000000000 --- a/compiler/IREmitter/Payload/patches/array.c +++ /dev/null @@ -1,61 +0,0 @@ - -typedef VALUE (*BlockFFIType)(VALUE firstYieldedArg, VALUE closure, int argCount, const VALUE *args, VALUE blockArg); - -VALUE sorbet_rb_array_square_br_slowpath(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, - BlockFFIType blk, VALUE closure) { - VALUE ary = recv; - // Don't call rb_ary_aref directly because we already checked the arity. - if (argc == 2) { - return rb_ary_aref2(ary, argv[0], argv[1]); - } - // This is slightly inefficient, as we arrived in this function because we already - // handled the argc == 1 && FIXNUM_P(argv[0]) case. But it's less code duplication. - return rb_ary_aref1(ary, argv[0]); -} - -VALUE rb_ary_compact_bang_forwarder(VALUE ary) { - return rb_ary_compact_bang(ary); -} - -VALUE sorbet_ary_make_hash(VALUE ary) { - return ary_make_hash(ary); -} - -void sorbet_ary_recycle_hash(VALUE hash) { - ary_recycle_hash(hash); -} - -// This is the no-block version of rb_ary_uniq: https://github.com/ruby/ruby/blob/ruby_2_7/array.c#L5018-L5041 -VALUE sorbet_rb_array_uniq(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - rb_check_arity(argc, 0, 0); - VALUE ary = recv; - - VALUE hash, uniq; - - if (RARRAY_LEN(ary) <= 1) { - hash = 0; - uniq = rb_ary_dup(ary); - } else { - hash = ary_make_hash(ary); - uniq = rb_hash_values(hash); - } - RBASIC_SET_CLASS(uniq, rb_obj_class(ary)); - if (hash) { - ary_recycle_hash(hash); - } - - return uniq; -} - -VALUE (*sorbet_rb_ary_to_a_func(void))(VALUE) { - return rb_ary_to_a; -} - -VALUE sorbet_rb_ary_to_a(VALUE ary) { - return rb_ary_to_a(ary); -} - -void sorbet_rb_ary_set_len(VALUE ary, long n) { - ARY_SET_LEN(ary, n); -} diff --git a/compiler/IREmitter/Payload/patches/hash.c b/compiler/IREmitter/Payload/patches/hash.c deleted file mode 100644 index 6753c4fbb4..0000000000 --- a/compiler/IREmitter/Payload/patches/hash.c +++ /dev/null @@ -1,138 +0,0 @@ -typedef VALUE (*BlockFFIType)(VALUE firstYieldedArg, VALUE closure, int argCount, const VALUE *args, VALUE blockArg); - -// A faster version of merge!, under the assumption that we don't pass blocks, and only pass a single arg. -VALUE sorbet_magic_mergeHashHelper(VALUE self, VALUE hash) { - rb_hash_foreach(hash, rb_hash_update_i, self); - return self; -} - -/* expose any_hash so we can precompute hash values for strings */ -long sorbet_hash_string(VALUE a) { - /* we should not need other_func, so pass NULL */ - return any_hash(a, NULL); -} - -// The no-block version of rb_hash_any_p, with any logic conditional on a block -// being present removed. -VALUE sorbet_rb_hash_any_forwarder(int argc, VALUE *argv, VALUE hash) { - VALUE args[2]; - args[0] = Qfalse; - - rb_check_arity(argc, 0, 1); - if (RHASH_EMPTY_P(hash)) { - return Qfalse; - } - - if (argc) { - args[1] = argv[0]; - rb_hash_foreach(hash, any_p_i_pattern, (VALUE)args); - return args[0]; - } else { - /* yields pairs, never false */ - return Qtrue; - } -} - -// Avoid dispatch through the VM by calling the body of `rb_hash_update` directly. -void sorbet_hashUpdate(VALUE hash, VALUE other) { - rb_hash_update(1, &other, hash); -} - -VALUE (*sorbet_rb_hash_to_h_func(void))(VALUE) { - return rb_hash_to_h; -} - -// The no-block version of `rb_hash_to_h` -VALUE sorbet_rb_hash_to_h(VALUE hash) { - if (rb_obj_class(hash) != rb_cHash) { - const VALUE flags = RBASIC(hash)->flags; - hash = hash_dup(hash, rb_cHash, flags & RHASH_PROC_DEFAULT); - } - return hash; -} - -// no-block rb_hash_fetch_m: https://github.com/ruby/ruby/blob/ruby_2_7/hash.c#L2113-L2147 -VALUE sorbet_rb_hash_fetch_m(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - VALUE key; - st_data_t val; - - rb_check_arity(argc, 1, 2); - key = argv[0]; - - if (hash_stlike_lookup(recv, key, &val)) { - return (VALUE)val; - } else { - if (argc == 1) { - VALUE desc = rb_protect(rb_inspect, key, 0); - if (NIL_P(desc)) { - desc = rb_any_to_s(key); - } - desc = rb_str_ellipsize(desc, 65); - rb_key_err_raise(rb_sprintf("key not found: %" PRIsVALUE, desc), recv, key); - } else { - return argv[1]; - } - } -} - -// No-block version of Hash#update: https://github.com/ruby/ruby/blob/ruby_2_7/hash.c#L3869-L3886 -VALUE sorbet_rb_hash_update(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - int i; - - rb_hash_modify(recv); - for (i = 0; i < argc; i++) { - VALUE hash = to_hash(argv[i]); - rb_hash_foreach(hash, rb_hash_update_i, recv); - } - return recv; -} - -struct sorbet_rb_hash_update_withBlock_callback_args { - VALUE value; - VALUE closure; - BlockFFIType blk; -}; - -// Adapted from rb_hash_update_block_callback: https://github.com/ruby/ruby/blob/ruby_2_7/hash.c#L3800-L3817 -static int sorbet_rb_hash_update_withBlock_callback(st_data_t *key, st_data_t *value, struct update_arg *arg, - int existing) { - struct sorbet_rb_hash_update_withBlock_callback_args *callback_args = - (struct sorbet_rb_hash_update_withBlock_callback_args *)arg->arg; - VALUE newvalue = (VALUE)callback_args->value; - - if (existing) { - VALUE closure = callback_args->closure; - BlockFFIType blk = callback_args->blk; - VALUE block_argv[3] = {(VALUE)*key, (VALUE)*value, newvalue}; - newvalue = blk((VALUE)*key, closure, 3, &block_argv[0], Qnil); - arg->old_value = *value; - } else { - arg->new_key = *key; - } - arg->new_value = newvalue; - *value = newvalue; - return ST_CONTINUE; -} - -NOINSERT_UPDATE_CALLBACK(sorbet_rb_hash_update_withBlock_callback) - -struct sorbet_rb_hash_update_withBlock_i_args { - VALUE hash; - VALUE closure; - BlockFFIType blk; -}; - -// Adapted from rb_hash_update_block_i: https://github.com/ruby/ruby/blob/ruby_2_7/hash.c#L3819-L3824 -int sorbet_rb_hash_update_withBlock_i(VALUE key, VALUE value, VALUE argsv) { - struct sorbet_rb_hash_update_withBlock_i_args *args = (struct sorbet_rb_hash_update_withBlock_i_args *)argsv; - VALUE hash = args->hash; - struct sorbet_rb_hash_update_withBlock_callback_args callback_args = {value, args->closure, args->blk}; - RHASH_UPDATE(hash, key, sorbet_rb_hash_update_withBlock_callback, (VALUE)&callback_args); - return ST_CONTINUE; -} - -int sorbet_hash_stlike_lookup(VALUE hash, VALUE key, VALUE *val) { - return hash_stlike_lookup(hash, key, val); -} diff --git a/compiler/IREmitter/Payload/patches/numeric.c b/compiler/IREmitter/Payload/patches/numeric.c deleted file mode 100644 index 496d5e98d2..0000000000 --- a/compiler/IREmitter/Payload/patches/numeric.c +++ /dev/null @@ -1,71 +0,0 @@ - -VALUE sorbet_rb_int_plus_slowpath(VALUE recv, VALUE y) { - if (LIKELY(FIXNUM_P(recv))) { - if (LIKELY(FIXNUM_P(y))) { - return rb_fix_plus_fix(recv, y); - } else if (RB_TYPE_P(y, T_BIGNUM)) { - return rb_big_plus(y, recv); - } else if (RB_TYPE_P(y, T_FLOAT)) { - return DBL2NUM((double)FIX2LONG(recv) + RFLOAT_VALUE(y)); - } else if (RB_TYPE_P(y, T_COMPLEX)) { - return rb_complex_plus(y, recv); - } - // fall through to coerce - } else if (RB_TYPE_P(recv, T_BIGNUM)) { - return rb_big_plus(recv, y); - } - return rb_num_coerce_bin(recv, y, '+'); -} - -VALUE sorbet_rb_int_minus_slowpath(VALUE recv, VALUE y) { - if (LIKELY(FIXNUM_P(recv))) { - if (LIKELY(FIXNUM_P(y))) { - return rb_fix_minus_fix(recv, y); - } else if (RB_TYPE_P(y, T_BIGNUM)) { - VALUE x = rb_int2big(FIX2LONG(recv)); - return rb_big_minus(x, y); - } else if (RB_TYPE_P(y, T_FLOAT)) { - return DBL2NUM((double)FIX2LONG(recv) - RFLOAT_VALUE(y)); - } - // fall throught to coerece - } else if (RB_TYPE_P(recv, T_BIGNUM)) { - return rb_big_minus(recv, y); - } - return rb_num_coerce_bin(recv, y, '-'); -} - -VALUE sorbet_rb_int_gt_slowpath(VALUE recv, VALUE y) { - if (LIKELY(FIXNUM_P(recv))) { - return fix_gt(recv, y); - } else if (RB_TYPE_P(recv, T_BIGNUM)) { - return rb_big_gt(recv, y); - } - return rb_num_coerce_relop(recv, y, '>'); -} - -VALUE sorbet_rb_int_lt_slowpath(VALUE recv, VALUE y) { - if (LIKELY(FIXNUM_P(recv))) { - return fix_lt(recv, y); - } else if (RB_TYPE_P(recv, T_BIGNUM)) { - return rb_big_lt(recv, y); - } - return rb_num_coerce_relop(recv, y, '<'); -} - -VALUE sorbet_rb_int_ge_slowpath(VALUE recv, VALUE y) { - if (LIKELY(FIXNUM_P(recv))) { - return fix_ge(recv, y); - } else if (RB_TYPE_P(recv, T_BIGNUM)) { - return rb_big_ge(recv, y); - } - return rb_num_coerce_relop(recv, y, idGE); -} - -VALUE sorbet_rb_int_le_slowpath(VALUE recv, VALUE y) { - if (LIKELY(FIXNUM_P(recv))) { - return fix_le(recv, y); - } else if (RB_TYPE_P(recv, T_BIGNUM)) { - return rb_big_le(recv, y); - } - return rb_num_coerce_relop(recv, y, idLE); -} diff --git a/compiler/IREmitter/Payload/patches/object.c b/compiler/IREmitter/Payload/patches/object.c deleted file mode 100644 index 6df1fa8fb5..0000000000 --- a/compiler/IREmitter/Payload/patches/object.c +++ /dev/null @@ -1,128 +0,0 @@ - -typedef VALUE (*BlockFFIType)(VALUE firstYieldedArg, VALUE closure, int argCount, const VALUE *args, VALUE blockArg); - -// Trying to be a copy of rb_mod_const_get, specialized to a single id argument -VALUE sorbet_getConstantAt(VALUE mod, ID id) { - VALUE name; - rb_encoding *enc; - const char *pbeg, *p, *path, *pend; - int recur = 1; - int DISABLED_CODE = 0; - - name = rb_id2str(id); - enc = rb_enc_get(name); - path = rb_id2name(id); - - pbeg = p = path; - pend = path + strlen(path); - - if (DISABLED_CODE && (p >= pend || !*p)) { - wrong_name: - rb_raise(rb_eRuntimeError, "wrong constant name %" PRIsVALUE "%" PRIsVALUE, mod, name); - } - - if (DISABLED_CODE && (p + 2 < pend && p[0] == ':' && p[1] == ':')) { - mod = rb_cObject; - p += 2; - pbeg = p; - } - - while (p < pend) { - VALUE part; - long len, beglen; - - while (p < pend && *p != ':') - p++; - - if (pbeg == p) - goto wrong_name; - - id = rb_check_id_cstr(pbeg, len = p - pbeg, enc); - beglen = pbeg - path; - - if (p < pend && p[0] == ':') { - if (p + 2 >= pend || p[1] != ':') - goto wrong_name; - p += 2; - pbeg = p; - } - - if (!RB_TYPE_P(mod, T_MODULE) && !RB_TYPE_P(mod, T_CLASS)) { - rb_raise(rb_eTypeError, "%" PRIsVALUE " does not refer to class/module", name); - } - - if (!id) { - part = rb_str_subseq(name, beglen, len); - OBJ_FREEZE(part); - VALUE idConst_missing = rb_intern("const_missing"); - if (!rb_is_const_name(part)) { - name = part; - goto wrong_name; - } else if (!rb_method_basic_definition_p(CLASS_OF(mod), idConst_missing)) { - part = rb_str_intern(part); - mod = rb_const_missing(mod, part); - continue; - } else { - rb_mod_const_missing(mod, part); - } - } - if (!rb_is_const_id(id)) { - name = ID2SYM(id); - goto wrong_name; - } - if (!recur) { - mod = rb_const_get_at(mod, id); - } else if (beglen == 0) { - mod = rb_const_get(mod, id); - } else { - mod = rb_const_get_from(mod, id); - } - } - - return mod; -} - -VALUE sorbet_getConstant(const char *path, long pathLen) { - ID id = rb_intern2(path, pathLen); - return sorbet_getConstantAt(rb_cObject, id); -} - -void sorbet_setConstant(VALUE mod, const char *name, long nameLen, VALUE value) { - ID id = rb_intern2(name, nameLen); - return rb_const_set(mod, id, value); -} - -// This doesn't do exactly the right thing because that is done by the parser in Ruby. Ruby will return the String -// "expression" if the RHS is an expression. -VALUE sorbet_definedIntrinsic(VALUE recv, ID fun, int argc, const VALUE *const restrict argv, BlockFFIType blk, - VALUE closure) { - if (argc == 0) { - return RUBY_Qnil; - } - VALUE klass = rb_cObject; - for (int i = 0; i < argc; i++) { - VALUE str = argv[i]; - ID id = rb_intern(RSTRING_PTR(str)); - if (!rb_const_defined_at(klass, id)) { - return RUBY_Qnil; - } - klass = sorbet_getConstantAt(klass, id); - } - return rb_str_new2("constant"); -} - -VALUE sorbet_vm_class_alloc(VALUE klass) { - return rb_class_alloc(klass); -} - -VALUE (*sorbet_vm_Class_new_func(void))() { - return rb_class_s_new; -} - -VALUE (*sorbet_vm_Kernel_instance_variable_get_func(void))(VALUE obj, VALUE iv) { - return rb_obj_ivar_get; -} - -VALUE (*sorbet_vm_Kernel_instance_variable_set_func(void))(VALUE obj, VALUE iv, VALUE value) { - return rb_obj_ivar_set; -} diff --git a/compiler/IREmitter/Payload/patches/proc.c b/compiler/IREmitter/Payload/patches/proc.c deleted file mode 100644 index 44d1ef38e5..0000000000 --- a/compiler/IREmitter/Payload/patches/proc.c +++ /dev/null @@ -1,4 +0,0 @@ -/* method_owner, the backing function for Method#owner, is not exported. */ -VALUE sorbet_vm_method_owner(VALUE obj) { - return method_owner(obj); -} diff --git a/compiler/IREmitter/Payload/patches/st.c b/compiler/IREmitter/Payload/patches/st.c deleted file mode 100644 index 007a039ceb..0000000000 --- a/compiler/IREmitter/Payload/patches/st.c +++ /dev/null @@ -1,28 +0,0 @@ -/* A copy of st_lookup except that it can use a precomputed hash. */ -int st_lookup_with_precomputed_hash(st_table *tab, st_data_t key, st_data_t *value, st_hash_t hash) { - st_index_t bin; - -retry: - if (tab->bins == NULL) { - bin = find_entry(tab, hash, key); - if (EXPECT(bin == REBUILT_TABLE_ENTRY_IND, 0)) { - goto retry; - } - if (bin == UNDEFINED_ENTRY_IND) { - return 0; - } - } else { - bin = find_table_entry_ind(tab, hash, key); - if (EXPECT(bin == REBUILT_TABLE_ENTRY_IND, 0)) { - goto retry; - } - if (bin == UNDEFINED_ENTRY_IND) { - return 0; - } - bin -= ENTRY_BASE; - } - if (value != 0) { - *value = tab->entries[bin].record; - } - return 1; -} diff --git a/compiler/IREmitter/Payload/patches/string.c b/compiler/IREmitter/Payload/patches/string.c deleted file mode 100644 index 32b1c0d8a4..0000000000 --- a/compiler/IREmitter/Payload/patches/string.c +++ /dev/null @@ -1,18 +0,0 @@ - -// cf. rb_fstring_new, but always as utf-8 -VALUE sorbet_vm_fstring_new(const char *ptr, long len) { - struct RString fake_str; - return register_fstring(setup_fake_str(&fake_str, ptr, len, ENCINDEX_UTF_8)); -} - -VALUE sorbet_vm_str_uplus(VALUE str) { - return str_uplus(str); -} - -VALUE (*sorbet_rb_str_to_s_func(void))(VALUE) { - return rb_str_to_s; -} - -VALUE sorbet_rb_str_to_s(VALUE str) { - return rb_str_to_s(str); -} diff --git a/compiler/IREmitter/Payload/patches/variable.c b/compiler/IREmitter/Payload/patches/variable.c deleted file mode 100644 index 1d79d8cdec..0000000000 --- a/compiler/IREmitter/Payload/patches/variable.c +++ /dev/null @@ -1,15 +0,0 @@ -static VALUE rb_cvar_get_or_undef(VALUE klass, ID id) { - st_data_t value; - CVAR_LOOKUP(&value, return (VALUE)value); - return Qundef; -} - -int rb_cvar_lookup(VALUE klass, ID id, VALUE *value) { - // Compare rb_cvar_get/rb_cvar_defined. - VALUE v = rb_cvar_get_or_undef(klass, id); - if (v == Qundef) { - return 0; - } - *value = v; - return 1; -} diff --git a/compiler/IREmitter/Payload/patches/vm_insnhelper.c b/compiler/IREmitter/Payload/patches/vm_insnhelper.c deleted file mode 100644 index 00c6e4e433..0000000000 --- a/compiler/IREmitter/Payload/patches/vm_insnhelper.c +++ /dev/null @@ -1,1114 +0,0 @@ -#include - -// compiler is closely aware of layout of this struct -struct FunctionInlineCache { - // We use an `rb_kwarg_call_data` instead of `rb_call_data` as they contain the same data, and the kwarg variant - // only adds a single pointer's worth of additional space. - struct rb_kwarg_call_data cd; -}; - -void sorbet_setExceptionStackFrame(rb_execution_context_t *ec, rb_control_frame_t *cfp, const rb_iseq_t *iseq) { - // Self is the same in exception-handlers - VALUE self = cfp->self; - - // there is only ever one local for rescue/ensure frames, this mirrors the implementation in - // https://github.com/ruby/ruby/blob/a9a48e6a741f048766a2a287592098c4f6c7b7c7/vm.c#L2085-L2101 - VALUE *sp = cfp->sp + 1; - int num_locals = iseq->body->local_table_size - 1; - - VALUE blockHandler = VM_GUARDED_PREV_EP(cfp->ep); - VALUE me = 0; - - // write the exception value on the stack (as an argument to the rescue frame) - cfp->sp[0] = rb_errinfo(); - - // NOTE: there's no explicit check for stack overflow, because `vm_push_frame` will do that check - cfp = vm_push_frame(ec, iseq, VM_FRAME_MAGIC_RESCUE, self, blockHandler, me, iseq->body->iseq_encoded, sp, - num_locals, iseq->body->stack_max); - - // This mirrors what the Ruby VM does for rescue frames: - // https://github.com/ruby/ruby/blob/a9a48e6a741f048766a2a287592098c4f6c7b7c7/vm.c#L2094-L2105 - ec->tag->state = TAG_NONE; - ec->errinfo = Qnil; -} - -rb_control_frame_t *sorbet_pushStaticInitFrame(VALUE recv) { - rb_execution_context_t *ec = GET_EC(); - - VALUE cref = (VALUE)vm_cref_push(ec, recv, NULL, FALSE); // Qnil or T_IMEMO(cref) or T_IMEMO(ment) - VALUE frame_type = VM_FRAME_MAGIC_CLASS | VM_ENV_FLAG_LOCAL; - - // TODO(trevor) we could pass this in to supply a block - VALUE block_handler = VM_BLOCK_HANDLER_NONE; - - return vm_push_frame(ec, NULL, frame_type, recv, block_handler, cref, 0, ec->cfp->sp, 0, 0); -} - -rb_control_frame_t *sorbet_pushCfuncFrame(struct FunctionInlineCache *cache, VALUE recv, const rb_iseq_t *iseq) { - rb_execution_context_t *ec = GET_EC(); - - // NOTE: method search must be done to ensure that this field is not NULL - const rb_callable_method_entry_t *me = cache->cd.cc.me; - VALUE frame_type = VM_FRAME_MAGIC_CFUNC | VM_ENV_FLAG_LOCAL; - - // TODO(trevor) we could pass this in to supply a block - VALUE block_handler = VM_BLOCK_HANDLER_NONE; - - /* cf. vm_call_sorbet_with_frame_normal */ - return vm_push_frame(ec, iseq, frame_type, recv, block_handler, (VALUE)me, 0, ec->cfp->sp, - iseq->body->local_table_size, iseq->body->stack_max); -} - -void sorbet_pushBlockFrame(const struct rb_captured_block *captured) { - rb_execution_context_t *ec = GET_EC(); - vm_push_frame(ec, (const rb_iseq_t *)captured->code.ifunc, VM_FRAME_MAGIC_IFUNC | VM_FRAME_FLAG_CFRAME, - captured->self, VM_GUARDED_PREV_EP(captured->ep), (VALUE)NULL, 0, ec->cfp->sp, 0, 0); -} - -void sorbet_popFrame() { - rb_execution_context_t *ec = GET_EC(); - vm_pop_frame(ec, ec->cfp, ec->cfp->ep); -} - -// NOTE: this is marked noinline so that there's only ever one copy that lives in the ruby runtime, cutting down on the -// size of generated artifacts. If we decide that speed is more important, this could be marked alwaysinline to avoid -// the function call. -void sorbet_setMethodStackFrame(rb_execution_context_t *ec, rb_control_frame_t *cfp, const rb_iseq_t *iseq) { - // make sure that we have enough space to allocate locals - int local_size = iseq->body->local_table_size; - int stack_max = iseq->body->stack_max; - - CHECK_VM_STACK_OVERFLOW0(cfp, cfp->sp, local_size + stack_max); - - // save the current state of the stack - VALUE cref_or_me = cfp->ep[VM_ENV_DATA_INDEX_ME_CREF]; // -2 - VALUE prev_ep = cfp->ep[VM_ENV_DATA_INDEX_SPECVAL]; // -1 - VALUE type = cfp->ep[VM_ENV_DATA_INDEX_FLAGS]; // -0 - - // C frames push no locals, so the address we want to treat as the top of the stack lies at -2. - // NOTE: we're explicitly discarding the const qualifier from ep, because we need to write to the stack. - VALUE *sp = (VALUE *)&cfp->ep[-2]; - - // setup locals - for (int i = 0; i < local_size; ++i) { - *sp++ = RUBY_Qnil; - } - - // restore the previous state of the stack - *sp++ = cref_or_me; - *sp++ = prev_ep; - *sp = type; - - cfp->ep = sp; - cfp->__bp__ = sp; - cfp->sp = sp + 1; -} - -// Initialize a method send cache. The values passed in for the keys must all be symbol values, and argc includes -// num_kwargs. -void sorbet_setupFunctionInlineCache(struct FunctionInlineCache *cache, ID mid, unsigned int flags, int argc, - int num_kwargs, ...) { - va_list kw_ids; - va_start(kw_ids, num_kwargs); - struct rb_kwarg_call_data *cd = &cache->cd; - - cd->ci_kw.ci.mid = mid; - cd->ci_kw.ci.orig_argc = argc; - cd->ci_kw.ci.flag = flags; - - if (num_kwargs > 0) { - // The layout for struct_rb_call_info_with_kwarg has a 1-element array as the last field, so allocating - // additional space will extend that array's length. - struct rb_call_info_kw_arg *kw_arg = (struct rb_call_info_kw_arg *)rb_xmalloc_mul_add( - num_kwargs - 1, sizeof(VALUE), sizeof(struct rb_call_info_kw_arg)); - - kw_arg->keyword_len = num_kwargs; - for (int i = 0; i < num_kwargs; ++i) { - ID id = va_arg(kw_ids, ID); - kw_arg->keywords[i] = ID2SYM(id); - } - - cd->ci_kw.kw_arg = kw_arg; - } else { - cd->ci_kw.kw_arg = NULL; - } - va_end(kw_ids); -} - -void sorbet_vmMethodSearch(struct FunctionInlineCache *cache, VALUE recv) { - // we keep an `rb_kwarg_call_data` in FunctionInline cache. - struct rb_call_data *cd = (struct rb_call_data *)&cache->cd; - vm_search_method(cd, recv); -} - -// Send Support ******************************************************************************************************** - -// Wrapped version of vm_call_opt_call: -// https://github.com/ruby/ruby/blob/5445e0435260b449decf2ac16f9d09bae3cafe72/vm_insnhelper.c#L2683-L2691 -// The wrapper catches TAG_RETURN jumps (from return statements inside lambdas), and handles appropriately. -static VALUE sorbet_vm_call_opt_call(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, - struct rb_calling_info *calling, struct rb_call_data *cd) { - enum ruby_tag_type state; - VALUE retval; - rb_control_frame_t *const volatile save_cfp = ec->cfp; - - // Push a tag to the execution context's stack, so that subsequent longjmps (inside EC_JUMP_TAG) will know where to - // jump to. - EC_PUSH_TAG(ec); - - // If EC_EXEC_TAG() returns 0, the tag has just been set (including setjmp), and we continue with a call to - // vm_call_opt_call. - if ((state = EC_EXEC_TAG()) == 0) { - retval = vm_call_opt_call(ec, reg_cfp, calling, cd); - } - // If EC_EXEC_TAG() returns TAG_RETURN, we have caught a longjmp (via EC_JUMP_TAG) from someone below us in the - // call stack indicating they would like to do a non-local 'return'. We need to examine the vm_throw_data to decide - // whether the return should go to the current method's caller, or to something above it. - // - // If the returned state is something other than TAG_RETURN, _or_ the 'return' is not intended to be handled in - // this frame, then we will continue the transfer below via EC_JUMP_TAG. - else if (state == TAG_RETURN) { - const struct vm_throw_data *const err = (struct vm_throw_data *)ec->errinfo; - const rb_control_frame_t *const escape_cfp = THROW_DATA_CATCH_FRAME(err); - - // The use of RUBY_VM_PREVIOUS_CONTROL_FRAME here is strange, but I think it is correct. If we are calling a - // compiled lambda, and a return under the lambda resolves to return from the lambda, then the escape CFP will - // be the lambda's control frame, but the catch here is happening in the lambda's caller's frame's context. - // - // Note that interpreted lambdas called from here follow a different codepath: the rb_vm_exec for the lambda's - // block frame will catch the TAG_RETURN and simply return the value to us. - if (save_cfp == RUBY_VM_PREVIOUS_CONTROL_FRAME(escape_cfp)) { - rb_vm_rewind_cfp(ec, save_cfp); - - // Zero out state to indicate to the logic below that we have processed the return and do not need to - // re-raise. - state = 0; - - ec->tag->state = TAG_NONE; - ec->errinfo = Qnil; - retval = THROW_DATA_VAL(err); - } - } - - // If we have fallen through to this point (whether or not we caught an EC_JUMP_TAG), we will pop the tag that we - // pushed above. - EC_POP_TAG(); - - // If state is still nonzero, we did not handle the jump, so it must have been intended for someone above us. Thus - // we "re-raise" the jump. - if (state) { - EC_JUMP_TAG(ec, state); - } - - // If we get this far, this jump was ours to handle, so we just return the thrown retval. - return retval; -} - -// This is a version of vm_call_method_each_type that differs in the handling of refined methods. As we know that we're -// calling from a CFUNC context, using vm_env_cref will cause a runtime crash, so instead we inline the behavior of -// `vm_call0_body` for refined methods, -// https://github.com/ruby/ruby/blob/5445e0435260b449decf2ac16f9d09bae3cafe72/vm_insnhelper.c#L2911-L2993 -static VALUE sorbet_vm_call_method_each_type(rb_execution_context_t *ec, rb_control_frame_t *cfp, - struct rb_calling_info *calling, struct rb_call_data *cd) { - const struct rb_call_info *ci = &cd->ci; - struct rb_call_cache *cc = &cd->cc; - -// begin differences from vm_call_method_each_type -again: - // end differences from vm_call_method_each_type - - switch (cc->me->def->type) { - case VM_METHOD_TYPE_ISEQ: - CC_SET_FASTPATH(cc, vm_call_iseq_setup, TRUE); - return vm_call_iseq_setup(ec, cfp, calling, cd); - - case VM_METHOD_TYPE_NOTIMPLEMENTED: - case VM_METHOD_TYPE_CFUNC: - CC_SET_FASTPATH(cc, vm_call_cfunc, TRUE); - return vm_call_cfunc(ec, cfp, calling, cd); - - case VM_METHOD_TYPE_SORBET: - CC_SET_FASTPATH(cc, vm_call_sorbet, TRUE); - return vm_call_sorbet_maybe_setup_fastpath(ec, cfp, calling, cd); - - case VM_METHOD_TYPE_ATTRSET: - CALLER_SETUP_ARG(cfp, calling, ci); - if (calling->argc == 1 && calling->kw_splat && RHASH_EMPTY_P(cfp->sp[-1])) { - rb_warn_keyword_to_last_hash(ec, calling, ci, NULL); - } else { - CALLER_REMOVE_EMPTY_KW_SPLAT(cfp, calling, ci); - } - - rb_check_arity(calling->argc, 1, 1); - cc->aux.index = 0; - CC_SET_FASTPATH(cc, vm_call_attrset, !((ci->flag & VM_CALL_ARGS_SPLAT) || (ci->flag & VM_CALL_KWARG))); - return vm_call_attrset(ec, cfp, calling, cd); - - case VM_METHOD_TYPE_IVAR: - CALLER_SETUP_ARG(cfp, calling, ci); - CALLER_REMOVE_EMPTY_KW_SPLAT(cfp, calling, ci); - rb_check_arity(calling->argc, 0, 0); - cc->aux.index = 0; - CC_SET_FASTPATH(cc, vm_call_ivar, !(ci->flag & VM_CALL_ARGS_SPLAT)); - return vm_call_ivar(ec, cfp, calling, cd); - - case VM_METHOD_TYPE_MISSING: - cc->aux.method_missing_reason = 0; - CC_SET_FASTPATH(cc, vm_call_method_missing, TRUE); - return vm_call_method_missing(ec, cfp, calling, cd); - - case VM_METHOD_TYPE_BMETHOD: - CC_SET_FASTPATH(cc, vm_call_bmethod, TRUE); - return vm_call_bmethod(ec, cfp, calling, cd); - - case VM_METHOD_TYPE_ALIAS: - CC_SET_ME(cc, aliased_callable_method_entry(cc->me)); - VM_ASSERT(cc->me != NULL); - return sorbet_vm_call_method_each_type(ec, cfp, calling, cd); - - case VM_METHOD_TYPE_OPTIMIZED: - switch (cc->me->def->body.optimize_type) { - case OPTIMIZED_METHOD_TYPE_SEND: - CC_SET_FASTPATH(cc, vm_call_opt_send, TRUE); - return vm_call_opt_send(ec, cfp, calling, cd); - case OPTIMIZED_METHOD_TYPE_CALL: - // begin differences from vm_call_method_each_type - // We use a patched version of sorbet_vm_call_opt_call which catches and handles returns from - // lambdas. - CC_SET_FASTPATH(cc, sorbet_vm_call_opt_call, TRUE); - return sorbet_vm_call_opt_call(ec, cfp, calling, cd); - // end differences from vm_call_method_each_type - case OPTIMIZED_METHOD_TYPE_BLOCK_CALL: - CC_SET_FASTPATH(cc, vm_call_opt_block_call, TRUE); - return vm_call_opt_block_call(ec, cfp, calling, cd); - default: - rb_bug("vm_call_method: unsupported optimized method type (%d)", cc->me->def->body.optimize_type); - } - - case VM_METHOD_TYPE_UNDEF: - break; - - case VM_METHOD_TYPE_ZSUPER: - return vm_call_zsuper(ec, cfp, calling, cd, RCLASS_ORIGIN(cc->me->defined_class)); - - case VM_METHOD_TYPE_REFINED: { - // begin differences from vm_call_method_each_type - - // This is the case for refined methods in `vm_call0_body`, inlined into the implementation of - // `vm_call_method_each_type`, as that implementation assumed that it was called from an interpreted - // context, and attempts to project out the cref from the method entry, which does not have a valid cref in - // a compiled context. - // https://github.com/ruby/ruby/blob/5445e0435260b449decf2ac16f9d09bae3cafe72/vm_eval.c#L173-L198 - - if (cc->me->def->body.refined.orig_me) { - CC_SET_ME(cc, refined_method_callable_without_refinement(cc->me)); - goto again; - } - - VALUE super_class = RCLASS_SUPER(cc->me->defined_class); - if (super_class) { - CC_SET_ME(cc, rb_callable_method_entry(super_class, ci->mid)); - if (cc->me) { - RUBY_VM_CHECK_INTS(ec); - goto again; - } - } - - // We call vm_call_method_nome instead of `missing_method` like `vm_call0_body`. - // This matches the behavior of vm_call_method_each_type: - // https://github.com/ruby/ruby/blob/5445e0435260b449decf2ac16f9d09bae3cafe72/vm_insnhelper.c#L2989 - return vm_call_method_nome(ec, cfp, calling, cd); - - // end differences from vm_call_method_each_type - } - } - - rb_bug("vm_call_method: unsupported method type (%d)", cc->me->def->type); -} - -// This is a version of vm_call_method that dispatches to `sorbet_vm_call_method_each_type` instead. -// https://github.com/ruby/ruby/blob/5445e0435260b449decf2ac16f9d09bae3cafe72/vm_insnhelper.c#L3017-L3070 -static inline VALUE sorbet_vm_call_method(rb_execution_context_t *ec, rb_control_frame_t *cfp, - struct rb_calling_info *calling, struct rb_call_data *cd) { - const struct rb_call_info *ci = &cd->ci; - struct rb_call_cache *cc = &cd->cc; - - VM_ASSERT(callable_method_entry_p(cc->me)); - - if (cc->me != NULL) { - switch (METHOD_ENTRY_VISI(cc->me)) { - case METHOD_VISI_PUBLIC: /* likely */ - // begin differences from vm_call_method_each_type - return sorbet_vm_call_method_each_type(ec, cfp, calling, cd); - // begin differences from vm_call_method_each_type - - case METHOD_VISI_PRIVATE: - if (!(ci->flag & VM_CALL_FCALL)) { - enum method_missing_reason stat = MISSING_PRIVATE; - if (ci->flag & VM_CALL_VCALL) - stat |= MISSING_VCALL; - - cc->aux.method_missing_reason = stat; - CC_SET_FASTPATH(cc, vm_call_method_missing, TRUE); - return vm_call_method_missing(ec, cfp, calling, cd); - } - // begin differences from vm_call_method_each_type - return sorbet_vm_call_method_each_type(ec, cfp, calling, cd); - // end differences from vm_call_method_each_type - - case METHOD_VISI_PROTECTED: - if (!(ci->flag & VM_CALL_OPT_SEND)) { - if (!rb_obj_is_kind_of(cfp->self, cc->me->defined_class)) { - cc->aux.method_missing_reason = MISSING_PROTECTED; - return vm_call_method_missing(ec, cfp, calling, cd); - } else { - /* caching method info to dummy cc */ - VM_ASSERT(cc->me != NULL); - if (ci->flag & VM_CALL_KWARG) { - struct rb_kwarg_call_data *kcd = (void *)cd; - struct rb_kwarg_call_data cd_entry = *kcd; - // begin differences from vm_call_method_each_type - return sorbet_vm_call_method_each_type(ec, cfp, calling, (void *)&cd_entry); - // end differences from vm_call_method_each_type - } else { - struct rb_call_data cd_entry = *cd; - // begin differences from vm_call_method_each_type - return sorbet_vm_call_method_each_type(ec, cfp, calling, &cd_entry); - // end differences from vm_call_method_each_type - } - } - } - // begin differences from vm_call_method_each_type - return sorbet_vm_call_method_each_type(ec, cfp, calling, cd); - // end differences from vm_call_method_each_type - - default: - rb_bug("unreachable"); - } - } else { - return vm_call_method_nome(ec, cfp, calling, cd); - } -} - -// This is a version of `vm_sendish` that's always called from a CFUNC context. We also assume that MJIT is disabled, -// and inline some behavior as a result. -// https://github.com/ruby/ruby/blob/5445e0435260b449decf2ac16f9d09bae3cafe72/vm_insnhelper.c#L3998-L4056 -static inline VALUE sorbet_vm_sendish(struct rb_execution_context_struct *ec, struct rb_control_frame_struct *reg_cfp, - struct rb_call_data *cd, VALUE block_handler) { - CALL_INFO ci = &cd->ci; - CALL_CACHE cc = &cd->cc; - VALUE val; - int argc = ci->orig_argc; - VALUE recv = TOPN(argc); - struct rb_calling_info calling; - - calling.block_handler = block_handler; - calling.kw_splat = IS_ARGS_KW_SPLAT(ci) > 0; - calling.recv = recv; - calling.argc = argc; - - // inlined instead of called via vm_search_method_wrap - vm_search_method(cd, recv); - - // We need to avoid using `vm_call_general`, and instead call `sorbet_vm_call_general`. See the comments in - // `sorbet_vm_call_method_each_type` for more information. - // - // Uses UNLIKELY to make the fast path of "call cache hit" faster - if (UNLIKELY(cc->call == vm_call_general)) { - val = sorbet_vm_call_method(ec, GET_CFP(), &calling, cd); - } else { - val = cc->call(ec, GET_CFP(), &calling, cd); - } - - if (val != Qundef) { - return val; /* CFUNC normal return */ - } else { - RESTORE_REGS(); /* CFP pushed in cc->call() */ - } - - return Qundef; -} - -static inline VALUE sorbet_vm_sendish_super(struct rb_execution_context_struct *ec, - struct rb_control_frame_struct *reg_cfp, struct rb_call_data *cd, - VALUE block_handler) { - CALL_INFO ci = &cd->ci; - CALL_CACHE cc = &cd->cc; - VALUE val; - int argc = ci->orig_argc; - VALUE recv = TOPN(argc); - struct rb_calling_info calling; - - calling.block_handler = block_handler; - calling.kw_splat = IS_ARGS_KW_SPLAT(ci) > 0; - calling.recv = recv; - calling.argc = argc; - - // inlined instead of called via vm_search_method_wrap - vm_search_super_method(reg_cfp, cd, recv); - - // We need to avoid using `vm_call_general`, and instead call `sorbet_vm_call_general`. See the comments in - // `sorbet_vm_call_method_each_type` for more information. - // - // Uses UNLIKELY to make the fast path of "call cache hit" faster - if (UNLIKELY(cc->call == vm_call_general)) { - val = sorbet_vm_call_method(ec, GET_CFP(), &calling, cd); - } else { - val = cc->call(ec, GET_CFP(), &calling, cd); - } - - if (val != Qundef) { - return val; /* CFUNC normal return */ - } else { - RESTORE_REGS(); /* CFP pushed in cc->call() */ - } - - return Qundef; -} - -// This send primitive assumes that all argumenst have been pushed to the ruby stack, and will invoke the vm machinery -// to execute the send. -VALUE sorbet_callFuncWithCache(struct FunctionInlineCache *cache, VALUE bh) { - rb_execution_context_t *ec = GET_EC(); - rb_control_frame_t *cfp = ec->cfp; - - VALUE val = sorbet_vm_sendish(ec, cfp, (struct rb_call_data *)&cache->cd, bh); - if (val == Qundef) { - VM_ENV_FLAGS_SET(ec->cfp->ep, VM_FRAME_FLAG_FINISH); - - // false here because we don't want to consider jit frames - val = rb_vm_exec(ec, false); - } - - return val; -} - -// This send primitive assumes that all argumenst have been pushed to the ruby stack, and will invoke the vm machinery -// to execute the send. -VALUE sorbet_callSuperFuncWithCache(struct FunctionInlineCache *cache, VALUE bh) { - rb_execution_context_t *ec = GET_EC(); - rb_control_frame_t *cfp = ec->cfp; - - VALUE val = sorbet_vm_sendish_super(ec, cfp, (struct rb_call_data *)&cache->cd, bh); - if (val == Qundef) { - VM_ENV_FLAGS_SET(ec->cfp->ep, VM_FRAME_FLAG_FINISH); - - // false here because we don't want to consider jit frames - val = rb_vm_exec(ec, false); - } - - return val; -} - -// This should only be called from a context where we know that a block handler has been passed. -VALUE sorbet_getPassedBlockHandler() { - // this is an inlined version of PASS_PASSED_BLOCK_HANDLER() - rb_execution_context_t *ec = GET_EC(); - const VALUE *ep = ec->cfp->ep; - while (!VM_ENV_LOCAL_P(ep)) { - ep = (void *)(ep[VM_ENV_DATA_INDEX_SPECVAL] & ~0x03); - } - VALUE block_handler = VM_ENV_BLOCK_HANDLER(ep); - vm_block_handler_verify(block_handler); - return block_handler; -} - -void sorbet_vm_env_write_slowpath(const VALUE *ep, int index, VALUE v) { - return vm_env_write_slowpath(ep, index, v); -} - -VALUE sorbet_vm_splatIntrinsic(VALUE thing) { - const VALUE duplicate_input_arrays = Qtrue; - return vm_splat_array(duplicate_input_arrays, thing); -} - -VALUE sorbet_vm_check_match_array(rb_execution_context_t *ec, VALUE target, VALUE pattern) { - int flag = VM_CHECKMATCH_ARRAY | VM_CHECKMATCH_TYPE_CASE; - return vm_check_match(ec, target, pattern, flag); -} - -VALUE sorbet_vm_getivar(VALUE obj, ID id, IVC ic) { - /* cf. getinstancevariable in insns.def and vm_getinstancevariable */ - struct rb_call_cache *cc = 0; - int is_attr = 0; - return vm_getivar(obj, id, ic, cc, is_attr); -} - -void sorbet_vm_setivar(VALUE obj, ID id, VALUE val, IVC ic) { - /* cf. setinstancevariable in insns.def and vm_setinstancevariable */ - struct rb_call_cache *cc = 0; - int is_attr = 0; - vm_setivar(obj, id, val, ic, cc, is_attr); -} - -void sorbet_throwReturn(rb_execution_context_t *ec, VALUE retval) { - VALUE v = vm_throw(ec, ec->cfp, TAG_RETURN, retval); - - ec->errinfo = v; - EC_JUMP_TAG(ec, ec->tag->state); -} - -struct rfb_status { - VALUE return_value; - bool was_thrown; -}; - -struct rfb_status sorbet_vm_return_from_block_wrapper(int argc, VALUE *argv, VALUE recv, - rb_control_frame_t *volatile cfp, void *calling, void *cd, - rb_sorbet_func_t wrapped) { - enum ruby_tag_type state; - rb_execution_context_t *volatile ec = GET_EC(); - struct rfb_status status; - status.return_value = Qundef; - status.was_thrown = false; - - EC_PUSH_TAG(ec); - - if ((state = EC_EXEC_TAG()) == 0) { - status.return_value = wrapped(argc, argv, recv, cfp, calling, cd); - status.was_thrown = false; - } else { - if (state == TAG_RETURN) { - const struct vm_throw_data *const err = (struct vm_throw_data *)ec->errinfo; - const rb_control_frame_t *const escape_cfp = err->catch_frame; - - if (cfp == escape_cfp) { - rb_vm_rewind_cfp(ec, cfp); - state = TAG_NONE; - ec->errinfo = Qnil; - status.return_value = err->throw_obj; - status.was_thrown = true; - } - } - } - - EC_POP_TAG(); - - if (state != TAG_NONE) { - rb_ec_tag_jump(ec, state); - } - - return status; -} - -// The functions for reading and writing locals are intentionally duplicated here -// from the codegen payload, but are marked static so the codegen payload's versions -// are always used for the shared objects generated by the compiler. -#define SORBET_INLINE __attribute__((always_inline)) - -SORBET_INLINE -static int computeLocalIndex(long index) { - // Local offset calculation needs to take into account the fixed values that - // are present on the stack: - // https://github.com/ruby/ruby/blob/a9a48e6a741f048766a2a287592098c4f6c7b7c7/compile.c#L1509 - return index + VM_ENV_DATA_SIZE; -} - -SORBET_INLINE -static VALUE sorbet_readLocal(rb_control_frame_t *cfp, long index, long level) { - int offset = computeLocalIndex(index); - return *(vm_get_ep(cfp->ep, level) - offset); -} -SORBET_INLINE -static void sorbet_writeLocal(rb_control_frame_t *cfp, long index, long level, VALUE value) { - int offset = computeLocalIndex(index); - vm_env_write(vm_get_ep(cfp->ep, level), -offset, value); -} - -typedef VALUE (*ExceptionFFIType)(VALUE **pc, VALUE closure, rb_control_frame_t *); - -SORBET_INLINE -static void sorbet_raiseIfNotNil(VALUE exception) { - if (exception == RUBY_Qnil) { - return; - } - - rb_exc_raise(exception); -} -VALUE sorbet_run_exception_handling(rb_execution_context_t *volatile ec, volatile ExceptionFFIType body, - VALUE **volatile pc, - // The locals offset for the body. - volatile VALUE methodClosure, rb_control_frame_t *volatile cfp, - // May be nullptr. - volatile ExceptionFFIType handlers, - // May be nullptr. - volatile ExceptionFFIType elseClause, - // May be nullptr. - volatile ExceptionFFIType ensureClause, - // The special value indicating that we need to retry. - volatile VALUE retrySingleton, volatile long exceptionValueIndex, - volatile long exceptionValueLevel) { - // `volatile` is not used in polite C programming, but here it's very important: - // it ensures that the requisite variables are stored in memory across the setjmp - // below and therefore will still be valid upon a return via longjmp. All such - // variables that need to be live "on both sides" of the setjmp need to be - // declared with `volatile`. - // - // Temporary variables used on the "longjmp side" only can be declared as normal. - volatile VALUE executionResult = Qundef; - volatile VALUE previousException = ec->errinfo; - volatile VALUE bodyException = Qnil; - volatile VALUE handlerException = Qnil; - volatile enum { - RunningBody, - RunningHandlers, - } state = RunningBody; - volatile enum ruby_tag_type nleType; - - EC_PUSH_TAG(ec); - - if ((nleType = EC_EXEC_TAG()) == TAG_NONE) { - execute_body: - state = RunningBody; - // tag.state will have been reset appropriately if we got here via `retry`, - // but not this variable. - nleType = TAG_NONE; - - // Clear out the local variable shared across exception handling regions - // where we store the current exception value. Note that we do not pull - // out a pointer to the local variable and use that throughout this function, - // as it's possible that the locals may shift from the Ruby stack to the - // heap during the execution of the exception-handling region. - sorbet_writeLocal(cfp, exceptionValueIndex, exceptionValueLevel, Qnil); - - // We're also done with whatever exceptions might have gotten thrown along the way. - bodyException = Qnil; - handlerException = Qnil; - - // Likewise with any results we accumulated along the way. - executionResult = Qundef; - - // Run the body. - executionResult = body(pc, methodClosure, cfp); - - // If we get to this point, then we know the body has succeeded without throwing - // an exception. We may need to run the `else` handler if the body didn't - // return a value that we need to propagate. - if (executionResult == Qundef) { - state = RunningHandlers; - - executionResult = elseClause(pc, methodClosure, cfp); - } - rb_set_errinfo(previousException); - } else { - // If we get here, setjmp has returned a non-zero value. - - // This case is the "obvious" case: something in the body threw; we need to handle - // exceptions directly here. - if (state == RunningBody) { - // We're handling things very similarly to rb_rescue2/rb_vrescue2: - // - // - // The significant difference from that function is that we're handling all - // the non-local exits directly. - - // Whatever kind of non-local exit we have, we need to make sure that - // the Ruby control frame for the handler (or the ensure) we're going - // to run lives directly under whatever frame we started this process with. - rb_vm_rewind_cfp(ec, cfp); - - if (nleType != TAG_RAISE) { - // Any other kind of non-local exit will skip the rescue/else handlers. - goto execute_ensure; - } - - // rb_rescue2/rb_vrescue2 would check ec->errinfo here to determine if it - // was the "right" kind of error. Sorbet has already generated code to check - // the exception value for us, we can dispatch directly to the handlers here, - // which avoids a little bit of overhead. But we do need to tell the - // handlers what the exception value *is*. - bodyException = ec->errinfo; - - // Any exception that got thrown needs to be set for the handler. - sorbet_writeLocal(cfp, exceptionValueIndex, exceptionValueLevel, bodyException); - - ExceptionFFIType handler = (bodyException != Qnil) ? handlers : elseClause; - - // Indicate that we are running the appropriate handler. - nleType = TAG_NONE; - state = RunningHandlers; - - executionResult = handler(pc, methodClosure, cfp); - - // If execution has reached this point, the handler did not throw any - // kind of non-local exit. - if (bodyException != Qnil && executionResult == retrySingleton) { - goto execute_body; - } - } else { - // This case is the non-obvious case: something in the handler we were executing - // threw. We need to clean up and execute the ensure. - - rb_vm_rewind_cfp(ec, cfp); - - // TODO(froydnj): this is where we would handle TAG_RETRY if we were implementing - // `retry` in terms of Ruby's non-local exit handling rather than our current - // retry singleton mechanism. - - if (nleType != TAG_RAISE) { - goto execute_ensure; - } - - handlerException = ec->errinfo; - sorbet_writeLocal(cfp, exceptionValueIndex, exceptionValueLevel, handlerException); - } - - // We need to determine what the value of the "current" exception is for the - // ensure handler. There are three possibilities, in order of preference: - // - // 1. An exception raised by the rescue block. - // 2. The original exception raised by the body, that wasn't handled by the rescue block. - // 3. The ambient previous exception present prior to exception handling. - VALUE postRescueExceptionContext; - - if (handlerException != Qnil) { - // Case 1. - postRescueExceptionContext = handlerException; - } else { - // The exception value local was set for the purposes of the rescue - // handler (it will be `nil` if the body didn't raise an exception). - // The rescue handler will have nil'd out this variable if an - // appropriate handler was run. - VALUE bodyException = sorbet_readLocal(cfp, exceptionValueIndex, exceptionValueLevel); - if (bodyException != Qnil) { - // Case 2. - postRescueExceptionContext = bodyException; - } else { - // Case 3. - postRescueExceptionContext = previousException; - } - } - - rb_set_errinfo(postRescueExceptionContext); - } - -execute_ensure: - // However we arrived at this state, we are done with our entry on the tag stack. - EC_POP_TAG(); - - // Ruby's running of ensure handlers passes in rb_errinfo() as an argument on the - // stack ; ensure handlers then exit via the bytecode instruction `throw 0 local[0]`, - // which eventually winds its way into vm_throw_continue. We don't codegen things - // that way -- perhaps we should, but this emulates that behavior. - VALUE pendingException = ec->errinfo; - - // Running the ensure handler will push an VM_FRAME_MAGIC_RESCUE control frame - // and also have the side effect of clearing out ec->errinfo and ec->tag->state. - VALUE ensureResult = ensureClause(pc, methodClosure, cfp); - - // Anything the ensure returned takes precedence over a pending non-local exit. - if (ensureResult != Qundef) { - return ensureResult; - } - - // Put the error back. We already have nleType, which was ec->tag->state prior - // to running the ensure handler, for resetting ec->tag->state if appropriate. - ec->errinfo = pendingException; - - // If we had a non-local exit from the region above, propagating that takes precedence - // over whatever the ensure handler did. - // - // cf. EC_JUMP_TAG for the code here. - if (nleType != TAG_NONE) { - ec->tag->state = nleType; - RUBY_LONGJMP(ec->tag->buf, 1); - } - - // executionResult is the result from running either the body or the handlers, - // depending on what path we took through the code above. - - sorbet_raiseIfNotNil(handlerException); - sorbet_raiseIfNotNil(sorbet_readLocal(cfp, exceptionValueIndex, exceptionValueLevel)); - - return executionResult; -} - -VALUE sorbet_vm_callBlock(rb_control_frame_t *cfp, int argc, const VALUE *const restrict argv, int kw_splat) { - // cf. https://github.com/ruby/ruby/blob/ruby_2_7/vm_insnhelper.c#L3990-L3995 - if (UNLIKELY(VM_CF_BLOCK_HANDLER(cfp) == VM_BLOCK_HANDLER_NONE)) { - rb_vm_localjump_error("no block given (yield)", Qnil, 0); - } - return rb_yield_values_kw(argc, argv, kw_splat); -} - -// This is a version of rb_iterate specialized to the case where we know the block is non-null and its arity. -VALUE sorbet_rb_iterate(VALUE (*it_proc)(VALUE), VALUE data1, const struct vm_ifunc *ifunc) { - rb_execution_context_t *ec = GET_EC(); - rb_control_frame_t *const cfp = ec->cfp; - - enum ruby_tag_type state; - volatile VALUE retval = Qnil; - - EC_PUSH_TAG(ec); - state = EC_EXEC_TAG(); - if (state == 0) { - // clang-format off -iter_retry: - - { - VALUE block_handler; - - struct rb_captured_block *captured = VM_CFP_TO_CAPTURED_BLOCK(cfp); - captured->code.ifunc = ifunc; - block_handler = VM_BH_FROM_IFUNC_BLOCK(captured); - vm_passed_block_handler_set(ec, block_handler); - } - - retval = (*it_proc)(data1); - // clang-format on - } else if (state == TAG_BREAK || state == TAG_RETRY) { - const struct vm_throw_data *const err = (struct vm_throw_data *)ec->errinfo; - const rb_control_frame_t *const escape_cfp = THROW_DATA_CATCH_FRAME(err); - - if (cfp == escape_cfp) { - rb_vm_rewind_cfp(ec, cfp); - - state = 0; - ec->tag->state = TAG_NONE; - ec->errinfo = Qnil; - - if (state == TAG_RETRY) - goto iter_retry; - retval = THROW_DATA_VAL(err); - } else if (0) { - SDR(); - fprintf(stderr, "%p, %p\n", (void *)cfp, (void *)escape_cfp); - } - } - EC_POP_TAG(); - - if (state) { - EC_JUMP_TAG(ec, state); - } - return retval; -} - -/* cf. vm_opt_aref */ -VALUE sorbet_vm_aref(rb_control_frame_t *reg_cfp, struct FunctionInlineCache *cache, VALUE recv, VALUE arg) { - if (!SPECIAL_CONST_P(recv)) { - if (RBASIC_CLASS(recv) == rb_cHash && BASIC_OP_UNREDEFINED_P(BOP_AREF, HASH_REDEFINED_OP_FLAG)) { - return rb_hash_aref(recv, arg); - } - } - VALUE *sp = reg_cfp->sp; - *(sp + 0) = recv; - *(sp + 1) = arg; - reg_cfp->sp += 2; - return sorbet_callFuncWithCache(cache, VM_BLOCK_HANDLER_NONE); -} - -/* cf. vm_opt_plus */ -VALUE sorbet_vm_plus(rb_control_frame_t *reg_cfp, struct FunctionInlineCache *cache, VALUE recv, VALUE arg) { - if (FIXNUM_2_P(recv, arg)) { - return rb_fix_plus_fix(recv, arg); - } - VALUE *sp = reg_cfp->sp; - *(sp + 0) = recv; - *(sp + 1) = arg; - reg_cfp->sp += 2; - return sorbet_callFuncWithCache(cache, VM_BLOCK_HANDLER_NONE); -} - -/* cf. vm_opt_minus */ -VALUE sorbet_vm_minus(rb_control_frame_t *reg_cfp, struct FunctionInlineCache *cache, VALUE recv, VALUE arg) { - if (FIXNUM_2_P(recv, arg)) { - return rb_fix_minus_fix(recv, arg); - } - VALUE *sp = reg_cfp->sp; - *(sp + 0) = recv; - *(sp + 1) = arg; - reg_cfp->sp += 2; - return sorbet_callFuncWithCache(cache, VM_BLOCK_HANDLER_NONE); -} - -/* TODO(froydnj): We did the easiest thing here, but the VM has more cases for strings - * and floats in these comparison functions, which we might want to add as well. - */ - -/* cf. vm_opt_eq */ -VALUE sorbet_vm_eqeq(rb_control_frame_t *reg_cfp, struct FunctionInlineCache *cache, VALUE recv, VALUE arg) { - if (FIXNUM_2_P(recv, arg)) { - return recv == arg ? Qtrue : Qfalse; - } - VALUE *sp = reg_cfp->sp; - *(sp + 0) = recv; - *(sp + 1) = arg; - reg_cfp->sp += 2; - return sorbet_callFuncWithCache(cache, VM_BLOCK_HANDLER_NONE); -} - -/* cf. vm_opt_neq */ -VALUE sorbet_vm_neq(rb_control_frame_t *reg_cfp, struct FunctionInlineCache *cache, VALUE recv, VALUE arg) { - if (FIXNUM_2_P(recv, arg)) { - return recv != arg ? Qtrue : Qfalse; - } - VALUE *sp = reg_cfp->sp; - *(sp + 0) = recv; - *(sp + 1) = arg; - reg_cfp->sp += 2; - return sorbet_callFuncWithCache(cache, VM_BLOCK_HANDLER_NONE); -} - -/* cf. vm_opt_leq */ -VALUE sorbet_vm_leq(rb_control_frame_t *reg_cfp, struct FunctionInlineCache *cache, VALUE recv, VALUE arg) { - if (FIXNUM_2_P(recv, arg)) { - return (SIGNED_VALUE)recv <= (SIGNED_VALUE)arg ? Qtrue : Qfalse; - } - VALUE *sp = reg_cfp->sp; - *(sp + 0) = recv; - *(sp + 1) = arg; - reg_cfp->sp += 2; - return sorbet_callFuncWithCache(cache, VM_BLOCK_HANDLER_NONE); -} - -/* cf. vm_opt_lt */ -VALUE sorbet_vm_lt(rb_control_frame_t *reg_cfp, struct FunctionInlineCache *cache, VALUE recv, VALUE arg) { - if (FIXNUM_2_P(recv, arg)) { - return (SIGNED_VALUE)recv < (SIGNED_VALUE)arg ? Qtrue : Qfalse; - } - VALUE *sp = reg_cfp->sp; - *(sp + 0) = recv; - *(sp + 1) = arg; - reg_cfp->sp += 2; - return sorbet_callFuncWithCache(cache, VM_BLOCK_HANDLER_NONE); -} - -/* cf. vm_opt_geq */ -VALUE sorbet_vm_geq(rb_control_frame_t *reg_cfp, struct FunctionInlineCache *cache, VALUE recv, VALUE arg) { - if (FIXNUM_2_P(recv, arg)) { - return (SIGNED_VALUE)recv >= (SIGNED_VALUE)arg ? Qtrue : Qfalse; - } - VALUE *sp = reg_cfp->sp; - *(sp + 0) = recv; - *(sp + 1) = arg; - reg_cfp->sp += 2; - return sorbet_callFuncWithCache(cache, VM_BLOCK_HANDLER_NONE); -} - -/* cf. vm_opt_gt */ -VALUE sorbet_vm_gt(rb_control_frame_t *reg_cfp, struct FunctionInlineCache *cache, VALUE recv, VALUE arg) { - if (FIXNUM_2_P(recv, arg)) { - return (SIGNED_VALUE)recv > (SIGNED_VALUE)arg ? Qtrue : Qfalse; - } - VALUE *sp = reg_cfp->sp; - *(sp + 0) = recv; - *(sp + 1) = arg; - reg_cfp->sp += 2; - return sorbet_callFuncWithCache(cache, VM_BLOCK_HANDLER_NONE); -} - -/* See the patched object.c. */ -extern VALUE sorbet_vm_class_alloc(VALUE klass); -extern VALUE (*sorbet_vm_Class_new_func(void))(); - -VALUE sorbet_maybeAllocateObjectFastPath(VALUE recv, struct FunctionInlineCache *newCache) { - sorbet_vmMethodSearch(newCache, recv); - rb_method_definition_t *newDef = newCache->cd.cc.me->def; - if (newDef->type != VM_METHOD_TYPE_CFUNC) { - return Qundef; - } - if (newDef->body.cfunc.func != sorbet_vm_Class_new_func()) { - return Qundef; - } - - // OK, we verified that we're calling the relevant C function. This is going - // to perform a ridiculously expensive search for the allocation function, - // perhaps someday we should try to cache this. - return sorbet_vm_class_alloc(recv); -} - -/* See the patched object.c. */ -extern VALUE (*sorbet_vm_Kernel_instance_variable_get_func(void))(VALUE obj, VALUE iv); -extern VALUE (*sorbet_vm_Kernel_instance_variable_set_func(void))(VALUE obj, VALUE iv, VALUE value); - -VALUE sorbet_vm_instance_variable_get(struct FunctionInlineCache *getCache, IVC varCache, rb_control_frame_t *reg_cfp, - VALUE recv, ID var) { - sorbet_vmMethodSearch(getCache, recv); - rb_method_definition_t *getDef = getCache->cd.cc.me->def; - if (getDef->type == VM_METHOD_TYPE_CFUNC && - getDef->body.cfunc.func == sorbet_vm_Kernel_instance_variable_get_func()) { - return sorbet_vm_getivar(recv, var, varCache); - } - - VALUE *sp = reg_cfp->sp; - *(sp + 0) = recv; - *(sp + 1) = rb_id2sym(var); - reg_cfp->sp += 2; - return sorbet_callFuncWithCache(getCache, VM_BLOCK_HANDLER_NONE); -} - -VALUE sorbet_vm_instance_variable_set(struct FunctionInlineCache *setCache, IVC varCache, rb_control_frame_t *reg_cfp, - VALUE recv, ID var, VALUE value) { - sorbet_vmMethodSearch(setCache, recv); - rb_method_definition_t *setDef = setCache->cd.cc.me->def; - if (setDef->type == VM_METHOD_TYPE_CFUNC && - setDef->body.cfunc.func == sorbet_vm_Kernel_instance_variable_set_func()) { - sorbet_vm_setivar(recv, var, value, varCache); - return value; - } - - VALUE *sp = reg_cfp->sp; - *(sp + 0) = recv; - *(sp + 1) = rb_id2sym(var); - *(sp + 2) = value; - reg_cfp->sp += 3; - return sorbet_callFuncWithCache(setCache, VM_BLOCK_HANDLER_NONE); -} - -VALUE sorbet_vm_class(struct FunctionInlineCache *classCache, rb_control_frame_t *reg_cfp, VALUE recv) { - sorbet_vmMethodSearch(classCache, recv); - rb_method_definition_t *classDef = classCache->cd.cc.me->def; - // We know that this function is the definition of Kernel#class. - if (classDef->type == VM_METHOD_TYPE_CFUNC && classDef->body.cfunc.func == rb_obj_class) { - return rb_obj_class(recv); - } - VALUE *sp = reg_cfp->sp; - *(sp + 0) = recv; - reg_cfp->sp += 1; - return sorbet_callFuncWithCache(classCache, VM_BLOCK_HANDLER_NONE); -} - -VALUE sorbet_vm_bang(struct FunctionInlineCache *bangCache, rb_control_frame_t *reg_cfp, VALUE recv) { - if (recv == Qnil || recv == Qfalse) { - return Qtrue; - } - if (recv == Qtrue) { - return Qfalse; - } - sorbet_vmMethodSearch(bangCache, recv); - rb_method_definition_t *bangDef = bangCache->cd.cc.me->def; - // cf. vm_opt_not - if (bangDef->type == VM_METHOD_TYPE_CFUNC && bangDef->body.cfunc.func == rb_obj_not) { - return RTEST(recv) ? Qfalse : Qtrue; - } - VALUE *sp = reg_cfp->sp; - *(sp + 0) = recv; - reg_cfp->sp += 1; - return sorbet_callFuncWithCache(bangCache, VM_BLOCK_HANDLER_NONE); -} - -VALUE sorbet_vm_isa_p(struct FunctionInlineCache *isaCache, rb_control_frame_t *reg_cfp, VALUE recv, VALUE klass) { - sorbet_vmMethodSearch(isaCache, recv); - rb_method_definition_t *isaDef = isaCache->cd.cc.me->def; - if (isaDef->type == VM_METHOD_TYPE_CFUNC && isaDef->body.cfunc.func == rb_obj_is_kind_of) { - return rb_obj_is_kind_of(recv, klass); - } - VALUE *sp = reg_cfp->sp; - *(sp + 0) = recv; - *(sp + 1) = klass; - reg_cfp->sp += 2; - return sorbet_callFuncWithCache(isaCache, VM_BLOCK_HANDLER_NONE); -} - -VALUE sorbet_vm_freeze(struct FunctionInlineCache *freezeCache, rb_control_frame_t *reg_cfp, VALUE recv) { - sorbet_vmMethodSearch(freezeCache, recv); - rb_method_definition_t *freezeDef = freezeCache->cd.cc.me->def; - if (freezeDef->type == VM_METHOD_TYPE_CFUNC && freezeDef->body.cfunc.func == rb_obj_freeze) { - return rb_obj_freeze(recv); - } - - VALUE *sp = reg_cfp->sp; - *(sp + 0) = recv; - reg_cfp->sp += 1; - return sorbet_callFuncWithCache(freezeCache, VM_BLOCK_HANDLER_NONE); -} diff --git a/compiler/IREmitter/Payload/serialize-runtime.c b/compiler/IREmitter/Payload/serialize-runtime.c deleted file mode 100644 index 2ca30cde82..0000000000 --- a/compiler/IREmitter/Payload/serialize-runtime.c +++ /dev/null @@ -1,1367 +0,0 @@ -#include "id.h" -#include "internal.h" -#include "ruby.h" -#include "vm_core.h" - -extern VALUE rb_mT; -extern VALUE rb_mT_Configuration; -extern VALUE rb_mT_Props; -extern VALUE rb_mT_Props_CustomType; -extern VALUE rb_mT_Props_Serializable; -extern VALUE rb_mT_Props_Utils; -extern VALUE rb_mT_Types; -extern VALUE rb_cT_Types_Enum; -extern VALUE rb_cT_Types_Intersection; -extern VALUE rb_cT_Types_Simple; -extern VALUE rb_cT_Types_TypedArray; -extern VALUE rb_cT_Types_TypedHash; -extern VALUE rb_cT_Types_TypedSet; -extern VALUE rb_cT_Types_Union; -extern VALUE rb_mT_Utils; -extern VALUE rb_mT_Utils_Nilable; - -extern VALUE sorbet_vm_getivar(VALUE, ID, struct iseq_inline_iv_cache_entry *); -extern void sorbet_vm_setivar(VALUE, ID, VALUE, struct iseq_inline_iv_cache_entry *); - -/* in our patched hash.c */ -extern long sorbet_hash_string(VALUE a); - -/* in our patched st.c */ -extern int st_lookup_with_precomputed_hash(st_table *tab, st_data_t key, st_data_t *value, st_index_t hash); - -/* TODO: add caching for returned modules */ -#define rb_mod_const_get(mod, name) sorbet_getConstantAt(mod, rb_intern(name)) - -extern VALUE sorbet_getConstantAt(VALUE mod, ID id); - -/* Ruby's object representation is a little strange. Arrays (resp. hashes) have the - * tag T_ARRAY (resp. T_HASH), which is fairly standard. The unusual thing is that - * *subclasses* of Array (resp. Hash) *also* have tag T_ARRAY. So checking - * RB_TYPE_P(obj, T_ARRAY) is not enough to know "is this object of type (exactly) - * Array"; we also need to check the class of the object as well. - * - * We do all of this because we want to know whether it's "safe" to call the - * underlying C functions for Array (resp. Hash) directly. Note that we assume - * that one is not redefining methods on Array (resp. Hash), but subclassing Array - * (resp. Hash) and overriding methods is fine -- we'll just do regular method - * dispatch in such cases. - */ -static __attribute__((always_inline)) _Bool can_directly_call_array_functions(VALUE); -static __attribute__((always_inline)) _Bool can_directly_call_hash_functions(VALUE); - -static _Bool can_directly_call_array_functions(VALUE obj) { - return LIKELY(RB_TYPE_P(obj, T_ARRAY)) && LIKELY(RBASIC(obj)->klass == rb_cArray); -} - -static _Bool can_directly_call_hash_functions(VALUE obj) { - return LIKELY(RB_TYPE_P(obj, T_HASH)) && LIKELY(RBASIC(obj)->klass == rb_cHash); -} - -/* "Transforms" are translated from T::Props::Private::SerdeTransform.generate: - * - * https://github.com/sorbet/sorbet/blob/a3dd6e828748c0a8d44cb4995ca72233bbcea837/gems/sorbet-runtime/lib/types/props/private/serde_transform.rb#L27-L152 - * - * step_for_type, below, is the translation of the above function into C. Each - * transform_* function roughly represents a when clause in the above method. - * transform_step encapsulates both the function and (via its closure member) the - * result of a recursive call to SerdeTransform.generate. - * - */ -struct transform_step; - -struct transform_ctx { - /* Copied from the prop_descs_vec that we are dealing with. */ - size_t n_modules; - VALUE *modules; - - struct rb_call_data *caches; - - /* Propagating down the strict parameter of the top-level call. */ - VALUE strict; -}; -/* VALUE comes first so we can abuse the C calling convention and use e.g. rb_ary_dup - * or rb_hash_dup in cases where that works. */ -typedef VALUE (*transform_fun)(VALUE, struct transform_ctx *, struct transform_step *); - -/* This structure could be smaller: - * - * - fun should be an enum that selects the appropriate transform at runtime. - * ideally this should be faster and cut down on indirect call overhead. - * an enum for all the various transforms will also be smaller than a full - * pointer. - * - idx should be 32-bit only, because we don't need 8 bytes and it packs better - * with making fun an enum. Likewise for cache. - * - there are probably ways to make the closure fields smaller, but maybe those - * are less necessary? - */ -struct transform_step { - transform_fun fun; - /* Index into a list of module names for when we're dealing with custom types or - * subtypes of T::Props::Serializable. */ - size_t idx; - size_t cache; - struct transform_step *closure; - /* only necessary for transforming hashes when keys and values are not passthrough */ - struct transform_step *closure2; -}; - -static VALUE transform_passthrough(VALUE x, struct transform_ctx *ctx, struct transform_step *self) { - return x; -} - -static VALUE transform_dup_array(VALUE x, struct transform_ctx *ctx, struct transform_step *self) { - if (UNLIKELY(!can_directly_call_array_functions(x))) { - return rb_funcallv(x, rb_intern("dup"), 0, NULL); - } - - return rb_ary_dup(x); -} - -struct transform_block_closure { - struct transform_ctx *ctx; - struct transform_step *self; -}; - -static VALUE map_nonarray_block(VALUE block_arg, VALUE data, int argc, const VALUE *argv, VALUE block_as_proc) { - struct transform_block_closure *closure = (struct transform_block_closure *)data; - return closure->self->fun(argv[0], closure->ctx, closure->self); -} - -static VALUE transform_map_array(VALUE x, struct transform_ctx *ctx, struct transform_step *self) { - /* subclasses might have overridden #[] */ - if (UNLIKELY(!can_directly_call_array_functions(x))) { - struct transform_block_closure closure; - closure.ctx = ctx; - closure.self = self->closure; - - return rb_block_call(x, rb_intern("map"), 0, NULL, map_nonarray_block, (VALUE)&closure); - } - - long len = RARRAY_LEN(x); - VALUE mapped = rb_ary_new2(len); - struct transform_step *inner = self->closure; - - for (long i = 0; i < len; ++i) { - rb_ary_push(mapped, inner->fun(RARRAY_AREF(x, i), ctx, inner)); - } - - return mapped; -} - -static VALUE transform_dup_set(VALUE x, struct transform_ctx *ctx, struct transform_step *self) { - return rb_funcallv(x, rb_intern("dup"), 0, NULL); -} - -static VALUE map_set_block(VALUE block_arg, VALUE data, int argc, const VALUE *argv, VALUE block_as_proc) { - struct transform_block_closure *closure = (struct transform_block_closure *)data; - return closure->self->fun(argv[0], closure->ctx, closure->self); -} - -static VALUE transform_map_set(VALUE x, struct transform_ctx *ctx, struct transform_step *self) { - /* TODO: cache the lookup of Set */ - VALUE Set = rb_mod_const_get(rb_cObject, "Set"); - - struct transform_block_closure closure; - closure.ctx = ctx; - closure.self = self->closure; - - return rb_block_call(Set, rb_intern("new"), 1, &x, map_set_block, (VALUE)&closure); -} - -/* TODO: consider implementing these via rb_hash_foreach to avoid block allocation - * and calling overhead. - */ -static VALUE each_hash_block(VALUE block_arg, VALUE data, int argc, const VALUE *argv, VALUE block_as_proc) { - struct transform_block_closure *closure = (struct transform_block_closure *)data; - VALUE ary = argv[0]; - VALUE k = RARRAY_AREF(ary, 0); - VALUE v = RARRAY_AREF(ary, 1); - VALUE h = argv[1]; - struct transform_step *s1 = closure->self->closure; - struct transform_step *s2 = closure->self->closure2; - return rb_hash_aset(h, s1->fun(k, closure->ctx, s1), s2->fun(v, closure->ctx, s2)); -} - -static VALUE transform_each_hash(VALUE x, struct transform_ctx *ctx, struct transform_step *self) { - // If we are transforming a hash, we can at least try to size it correctly. - VALUE transformed; - - if (LIKELY(can_directly_call_hash_functions(x))) { - transformed = rb_hash_new_with_size(RHASH_SIZE(x)); - } else { - transformed = rb_hash_new(); - } - - /* We are explicitly passing `self` here rather than `self->closure`, because we - * want both `self->closure` and `self->closure2` to be available in `each_hash_block`. - */ - struct transform_block_closure closure; - closure.ctx = ctx; - closure.self = self; - - return rb_block_call(x, rb_intern("each_with_object"), 1, &transformed, each_hash_block, (VALUE)&closure); -} - -static VALUE keys_block(VALUE block_arg, VALUE data, int argc, const VALUE *argv, VALUE block_as_proc) { - struct transform_block_closure *closure = (struct transform_block_closure *)data; - return closure->self->fun(argv[0], closure->ctx, closure->self); -} - -static VALUE transform_keys_hash(VALUE x, struct transform_ctx *ctx, struct transform_step *self) { - struct transform_block_closure closure; - closure.ctx = ctx; - closure.self = self->closure; - - return rb_block_call(x, rb_intern("transform_keys"), 0, NULL, keys_block, (VALUE)&closure); -} - -static VALUE values_nonhash_block(VALUE block_arg, VALUE data, int argc, const VALUE *argv, VALUE block_as_proc) { - struct transform_block_closure *closure = (struct transform_block_closure *)data; - return closure->self->fun(argv[0], closure->ctx, closure->self); -} - -struct transform_values_closure { - struct transform_ctx *ctx; - struct transform_step *self; - VALUE hash; -}; - -/* This function is called for each entry in the hashtable to determine whether - * we want to replace some aspect of the entry. Since we do, we return ST_REPLACE. - */ -static int transform_values_foreach_check(st_data_t key, st_data_t value, st_data_t argp, int error) { - return ST_REPLACE; -} - -/* This function is called for each entry in the hashtable to change the entry - * in some way. For transforming values, we want to modify the value stored in - * the hashtable, and then return ST_CONTINUE to indicate that we should continuing - * iterating over other entries in the table. - */ -static int transform_values_foreach_replace(st_data_t *key, st_data_t *value, st_data_t argp, int error) { - struct transform_values_closure *closure = (struct transform_values_closure *)argp; - VALUE new_val = closure->self->fun(*value, closure->ctx, closure->self); - RB_OBJ_WRITE(closure->hash, value, new_val); - return ST_CONTINUE; -} - -static VALUE transform_values_hash(VALUE x, struct transform_ctx *ctx, struct transform_step *self) { - if (UNLIKELY(!can_directly_call_hash_functions(x))) { - struct transform_block_closure closure; - closure.ctx = ctx; - closure.self = self->closure; - - return rb_block_call(x, rb_intern("transform_values"), 0, NULL, values_nonhash_block, (VALUE)&closure); - } - - VALUE ret = rb_hash_dup(x); - - struct transform_values_closure closure; - closure.ctx = ctx; - closure.self = self->closure; - closure.hash = ret; - - rb_hash_stlike_foreach_with_replace(ret, transform_values_foreach_check, transform_values_foreach_replace, - (st_data_t)&closure); - - return ret; -} - -static VALUE transform_dup_hash(VALUE x, struct transform_ctx *ctx, struct transform_step *inner) { - if (UNLIKELY(!can_directly_call_hash_functions(x))) { - return rb_funcallv(x, rb_intern("dup"), 0, NULL); - } - - return rb_hash_dup(x); -} - -static VALUE transform_to_float(VALUE x, struct transform_ctx *ctx, struct transform_step *inner) { - /* TODO: pick off the easy Float, Integer, String cases before doing a full dispatch */ - return rb_funcallv(x, idTo_f, 0, NULL); -} - -static VALUE transform_with_nil_check(VALUE x, struct transform_ctx *ctx, struct transform_step *self) { - VALUE nil_p = rb_funcallv(x, idNilP, 0, NULL); - if (RTEST(nil_p)) { - return Qnil; - } - - struct transform_step *inner = self->closure; - return inner->fun(x, ctx, inner); -} - -static VALUE transform_call_serialize(VALUE x, struct transform_ctx *ctx, struct transform_step *self) { - return rb_funcallv(x, rb_intern("serialize"), 1, &ctx->strict); -} - -static VALUE transform_call_checked_serialize(VALUE x, struct transform_ctx *ctx, struct transform_step *self) { - return rb_funcallv(rb_mT_Props_CustomType, rb_intern("checked_serialize"), 1, &x); -} - -static VALUE transform_deep_clone_object(VALUE x, struct transform_ctx *ctx, struct transform_step *self) { - return rb_funcall(rb_mT_Props_Utils, rb_intern("deep_clone_object"), 1, x); -} - -static VALUE transform_call_from_hash(VALUE x, struct transform_ctx *ctx, struct transform_step *self) { - return rb_funcallv_with_cc(&ctx->caches[self->cache], ctx->modules[self->idx], rb_intern("from_hash"), 1, &x); -} - -static VALUE transform_call_deserialize(VALUE x, struct transform_ctx *ctx, struct transform_step *self) { - return rb_funcallv_with_cc(&ctx->caches[self->cache], ctx->modules[self->idx], rb_intern("deserialize"), 1, &x); -} - -/* This struct stores data we accumulate while we're deciding how to serialize/deserialize - * individual types. - */ -struct serde_ctx { - /* Types that we need to record for serde on serializable and custom types - * and later pass into the actual serde process. - */ - size_t module_index; - VALUE module_array; - - size_t call_cache_index; - /* IDs (as symbols) representing the method to be called for the particular - * call cache being reserved. - */ - VALUE call_cache_array; -}; - -static struct transform_step *step_new_priv(transform_fun fun, size_t idx, size_t cache, struct transform_step *closure, - struct transform_step *closure2) { - struct transform_step *step = malloc(sizeof(*step)); - step->fun = fun; - step->idx = idx; - step->cache = cache; - step->closure = closure; - step->closure2 = closure2; - return step; -} - -static struct transform_step *step_new2(transform_fun fun, struct transform_step *closure, - struct transform_step *closure2) { - return step_new_priv(fun, 0, 0, closure, closure2); -} - -static struct transform_step *step_new(transform_fun fun, struct transform_step *closure) { - return step_new_priv(fun, 0, 0, closure, NULL); -} - -static struct transform_step *step_new_indexed(transform_fun fun, size_t idx, size_t cache) { - return step_new_priv(fun, idx, cache, NULL, NULL); -} - -static void step_free(struct transform_step *step) { - if (step->closure != NULL) { - step_free(step->closure); - } - if (step->closure2 != NULL) { - step_free(step->closure2); - } - free(step); -} - -static _Bool step_is_passthrough(struct transform_step *step) { - return step->fun == transform_passthrough; -} - -/* When generating Ruby code, sorbet-runtime can just compare the string representations - * of transforms on particular values to determine whether the transforms are equalish. - * Note that the comparison is not necessarily checking whether identical types are - * being transformed: integers and strings both receive nil transforms in - * sorbet-runtime -- transform_passthough here -- but would generally compare as equal - * in the places where equalish matters. - * - * We don't have strings available, so we have to do something slightly different. - */ -static _Bool step_equalish(struct serde_ctx *ctx, struct transform_step *s1, struct transform_step *s2) { - if (s1 == NULL && s2 == NULL) { - return true; - } - - if (s1 == NULL || s2 == NULL) { - return false; - } - - if (s1->fun != s2->fun) { - return false; - } - - /* We also need to compare the modules used by s1 and s2: deserializing different - * custom types (transform_call_deserialize) should compare as different despite - * using the same transform function. (In sorbet-runtime, the module names would - * appear in the string representation of the transform, so they would obviously - * compare differently.) - * - * We don't need to concern ourselves deciding if s1 and s2 will actually use a - * module at runtime: - * - * - if s1 doesn't, then s1->idx will be 0 and so will s2->idx. - * - if s1 does, then s1->idx will be equal to s2->idx *or* they refer to the - * the same module in the module array. Since modules compare equal by pointer - * equality, we can compare them thus here. - * - * TODO: this is a bit clunky; we should really ensure that any given module - * is only recorded once in serde_ctx. - */ - if (s1->idx != s2->idx) { - if (rb_ary_entry(ctx->module_array, s1->idx) != rb_ary_entry(ctx->module_array, s2->idx)) { - return false; - } - } - - /* We do not need to compare call caches, since those are essentially private - * runtime details. - */ - - return step_equalish(ctx, s1->closure, s2->closure) && step_equalish(ctx, s1->closure2, s2->closure2); -} - -enum StepMode { - Serialize, - Deserialize, -}; - -static size_t push_module(struct serde_ctx *ctx, VALUE klass) { - rb_ary_push(ctx->module_array, klass); - return ctx->module_index++; -} - -static size_t reserve_call_cache(struct serde_ctx *ctx, ID id) { - rb_ary_push(ctx->call_cache_array, rb_id2sym(id)); - return ctx->call_cache_index++; -} - -/* type: Module */ -static struct transform_step *handle_serializable_subtype(struct serde_ctx *ctx, VALUE type, enum StepMode mode) { - switch (mode) { - case Serialize: - return step_new(transform_call_serialize, NULL); - case Deserialize: { - size_t idx = push_module(ctx, type); - size_t cache = reserve_call_cache(ctx, rb_intern("from_hash")); - return step_new_indexed(transform_call_from_hash, idx, cache); - } - } -} - -/* type: Module */ -static struct transform_step *handle_custom_type(struct serde_ctx *ctx, VALUE type, enum StepMode mode) { - switch (mode) { - case Serialize: - return step_new(transform_call_checked_serialize, NULL); - case Deserialize: { - size_t idx = push_module(ctx, type); - size_t cache = reserve_call_cache(ctx, rb_intern("deserialize")); - return step_new_indexed(transform_call_deserialize, idx, cache); - } - } -} - -/* This function closely mirrors T::Props::Private::SerdeTransform.generate. - * Comments throughout the function attempt to orient the reader to where they - * are within `generate`. - * - * type: T::Types::Base - */ -static struct transform_step *step_for_type(struct serde_ctx *ctx, VALUE type, enum StepMode mode) { - if (RTEST(rb_obj_is_kind_of(type, rb_cT_Types_TypedArray))) { - VALUE inner_type = rb_funcallv(type, rb_intern("type"), 0, NULL); - struct transform_step *inner_step = step_for_type(ctx, inner_type, mode); - - if (step_is_passthrough(inner_step)) { - /*assert(inner_step->closure == NULL);*/ - inner_step->fun = transform_dup_array; - return inner_step; - } - - return step_new(transform_map_array, inner_step); - } - - if (RTEST(rb_obj_is_kind_of(type, rb_cT_Types_TypedSet))) { - VALUE inner_type = rb_funcallv(type, rb_intern("type"), 0, NULL); - struct transform_step *inner_step = step_for_type(ctx, inner_type, mode); - - if (step_is_passthrough(inner_step)) { - /*assert(inner_step->closure == NULL);*/ - inner_step->fun = transform_dup_set; - return inner_step; - } - - return step_new(transform_map_set, inner_step); - } - - if (RTEST(rb_obj_is_kind_of(type, rb_cT_Types_TypedHash))) { - VALUE keys = rb_funcallv(type, rb_intern("keys"), 0, NULL); - VALUE values = rb_funcallv(type, rb_intern("values"), 0, NULL); - struct transform_step *keys_step = step_for_type(ctx, keys, mode); - struct transform_step *values_step = step_for_type(ctx, values, mode); - _Bool passthrough_keys = step_is_passthrough(keys_step); - _Bool passthrough_values = step_is_passthrough(values_step); - - if (!passthrough_keys && !passthrough_values) { - return step_new2(transform_each_hash, keys_step, values_step); - } - - if (!passthrough_keys) { - step_free(values_step); - return step_new(transform_keys_hash, keys_step); - } - - if (!passthrough_values) { - step_free(keys_step); - return step_new(transform_values_hash, values_step); - } - - /*assert(keys_step->closure == NULL);*/ - keys_step->fun = transform_dup_hash; - step_free(values_step); - - return keys_step; - } - - if (RTEST(rb_obj_is_kind_of(type, rb_cT_Types_Simple))) { - VALUE raw = rb_funcallv(type, rb_intern("raw_type"), 0, NULL); - /* NO_TRANSFORM_TYPES.any? {|cls| raw <= cls} */ - if (RTEST(rb_class_inherited_p(raw, rb_cTrueClass)) || RTEST(rb_class_inherited_p(raw, rb_cFalseClass)) || - RTEST(rb_class_inherited_p(raw, rb_cNilClass)) || RTEST(rb_class_inherited_p(raw, rb_cSymbol)) || - RTEST(rb_class_inherited_p(raw, rb_cString))) { - return step_new(transform_passthrough, NULL); - } - - /* raw <= Float */ - if (RTEST(rb_class_inherited_p(raw, rb_cFloat))) { - switch (mode) { - case Deserialize: - return step_new(transform_to_float, NULL); - case Serialize: - return step_new(transform_passthrough, NULL); - } - } - - /* raw <= Numeric */ - if (RTEST(rb_class_inherited_p(raw, rb_cNumeric))) { - return step_new(transform_passthrough, NULL); - } - - /* raw < T::Props::Serializable */ - if (raw != rb_mT_Props_Serializable && RTEST(rb_class_inherited_p(raw, rb_mT_Props_Serializable))) { - return handle_serializable_subtype(ctx, raw, mode); - } - - /* raw < T::Props::CustomType */ - if (raw != rb_mT_Props_CustomType && - RTEST(rb_class_inherited_p(rb_singleton_class(raw), rb_mT_Props_CustomType))) { - return handle_custom_type(ctx, raw, mode); - } - - /* T::Configuration.scalar_types.include?(raw.name) */ - VALUE raw_name = rb_funcallv(raw, rb_intern("name"), 0, NULL); - VALUE scalar_types = rb_funcallv(rb_mT_Configuration, rb_intern("scalar_types"), 0, NULL); - VALUE includes_p = rb_funcallv(scalar_types, rb_intern("include?"), 1, &raw_name); - if (RTEST(includes_p)) { - return step_new(transform_passthrough, NULL); - } - - /* else */ - return step_new(transform_deep_clone_object, NULL); - } - - if (RTEST(rb_obj_is_kind_of(type, rb_cT_Types_Union))) { - VALUE non_nil_type = rb_funcall(rb_mT_Utils, rb_intern("unwrap_nilable"), 1, type); - if (RTEST(non_nil_type)) { - struct transform_step *inner = step_for_type(ctx, non_nil_type, mode); - if (step_is_passthrough(inner)) { - return inner; - } - - return step_new(transform_with_nil_check, inner); - } - - /* type.types.all? {|t| generate(t, mode, varname).nil?} */ - VALUE types = rb_funcallv(type, rb_intern("types"), 0, NULL); - bool all = true; - for (long i = 0; i < RARRAY_LEN(types); ++i) { - VALUE t = RARRAY_AREF(types, i); - struct transform_step *inner = step_for_type(ctx, t, mode); - all = step_is_passthrough(inner); - step_free(inner); - - if (!all) { - break; - } - } - - /* all types are passthrough, so the union is passthrough */ - if (all) { - return step_new(transform_passthrough, NULL); - } - - /* else */ - return step_new(transform_deep_clone_object, NULL); - } - - if (RTEST(rb_obj_is_kind_of(type, rb_cT_Types_Intersection))) { - VALUE types = rb_funcallv(type, rb_intern("types"), 0, NULL); - struct transform_step *uniq = NULL; - - for (long i = 0; i < RARRAY_LEN(types); ++i) { - VALUE t = RARRAY_AREF(types, i); - struct transform_step *inner = step_for_type(ctx, t, mode); - - /* reject {|t| t == dynamic_fallback} */ - if (inner->fun == transform_deep_clone_object) { - step_free(inner); - continue; - } - - if (uniq == NULL) { - uniq = inner; - continue; - } - - /* Now check to see if some intermediate step is equalish to the - * first non-dynamic fallback step we had. If they are, then we - * effectively still have only one (`uniq`) transform for all - * the members of the intersection. If they are not, then we - * have two different cases and should stop immediately. - */ - if (step_equalish(ctx, uniq, inner)) { - step_free(inner); - continue; - } - - /* We have multiple cases and they weren't consistent. */ - step_free(uniq); - step_free(inner); - return step_new(transform_deep_clone_object, NULL); - } - - /* We couldn't tell what to do, just fallback. */ - if (uniq == NULL) { - return step_new(transform_deep_clone_object, NULL); - } - - /* This is effectively the inner_known.first case. */ - return uniq; - } - - if (RTEST(rb_obj_is_kind_of(type, rb_cT_Types_Enum))) { - VALUE lifted = rb_funcallv(rb_mT_Utils, rb_intern("lift_enum"), 1, &type); - - return step_for_type(ctx, lifted, mode); - } - - return step_new(transform_deep_clone_object, NULL); -} - -/* This function is meant to implement the same logic as: - * - * https://github.com/sorbet/sorbet/blob/a3dd6e828748c0a8d44cb4995ca72233bbcea837/gems/sorbet-runtime/lib/types/props/private/serializer_generator.rb#L29-L39 - * https://github.com/sorbet/sorbet/blob/a3dd6e828748c0a8d44cb4995ca72233bbcea837/gems/sorbet-runtime/lib/types/props/private/deserializer_generator.rb#L34-L44 - */ -static void validate_prop(VALUE prop, VALUE rules, VALUE *hash_key_ptr, ID *ivar_id_ptr) { - VALUE T_Props_Decorator_SAFE_NAME = rb_mod_const_get(rb_cObject, "T::Props::Decorator::SAFE_NAME"); - VALUE prop_to_s = rb_funcallv(prop, idTo_s, 0, NULL); - - VALUE matched = rb_funcallv(T_Props_Decorator_SAFE_NAME, rb_intern("match"), 1, &prop_to_s); - if (!RTEST(matched)) { - rb_raise(rb_eRuntimeError, "invalid property name"); - } - - VALUE hash_key = rb_hash_fetch(rules, RB_ID2SYM(rb_intern("serialized_form"))); - matched = rb_funcallv(T_Props_Decorator_SAFE_NAME, rb_intern("match"), 1, &hash_key); - if (!RTEST(matched)) { - rb_raise(rb_eRuntimeError, "invalid serialized_form"); - } - - /* Note that {de,}serializer_generator.rb uses to_s on the accessor_key to obtain - * something that works as Ruby source. We use the symbol (actually, the ID of - * the symbol) directly during the {de,}serialization process, but we implement - * the checks on the string representation. - */ - VALUE accessor_key = rb_hash_fetch(rules, RB_ID2SYM(rb_intern("accessor_key"))); - /* Defend against sorbet-runtime representation changes. */ - if (!RB_SYMBOL_P(accessor_key)) { - rb_raise(rb_eRuntimeError, "expected accessor_key to be a symbol"); - } - - VALUE ivar_name_str = rb_funcallv(accessor_key, idTo_s, 0, NULL); - VALUE at_str = rb_str_new_static("@", 1); - VALUE starts_with = rb_funcallv(ivar_name_str, rb_intern("start_with?"), 1, &at_str); - if (!RTEST(starts_with)) { - rb_raise(rb_eRuntimeError, "invalid accessor_key"); - } - - const int exclude_end = 0; - VALUE range = rb_range_new(LONG2FIX(1), LONG2FIX(-1), exclude_end); - VALUE ivar_subseq = rb_funcallv(ivar_name_str, rb_intern("[]"), 1, &range); - matched = rb_funcallv(T_Props_Decorator_SAFE_NAME, rb_intern("match"), 1, &ivar_subseq); - if (!RTEST(matched)) { - rb_raise(rb_eRuntimeError, "invalid accessor_key"); - } - - *hash_key_ptr = hash_key; - /* Safe because we checked RB_SYMBOL_P above. */ - *ivar_id_ptr = RB_SYM2ID(accessor_key); -} - -struct prop_desc; -typedef VALUE (*nil_handler)(VALUE self, struct prop_desc *desc); - -/* A prop_desc contains all the information we need to serialize or deserialize a given - * prop. We will store separate arrays of prop_descs for serialization and deserialization - * in class variables. - */ -struct prop_desc { - /* the ID of the instance variable we are dealing with */ - ID ivar_id; - - /* instance variable access cache */ - struct iseq_inline_iv_cache_entry iv_cache; - - /* precomputed hash value for hash_key */ - st_index_t precomputed_hash; - - /* string key for the prop's serialized value */ - VALUE hash_key; - - /* how to transform a value, whether for serialization or deserialization */ - struct transform_step *transform; - - /* what to do in case of a nil when deserializing; NULL for serialization */ - nil_handler on_nil; - - /* the prop name we're dealing with, as a symbol */ - VALUE prop; - - /* default value for deserialization, if it matters */ - VALUE default_; - - /* holds the result of RTEST(rules[:fully_optional]) */ - bool fully_optional; -}; - -/* prop: Symbol - * default_: a Ruby literal if the prop has such a default, Qnil otherwise - * hash_key: String - */ -static void desc_new(struct prop_desc *desc, ID ivar_id, bool fully_optional, VALUE prop, VALUE default_, - VALUE hash_key, struct transform_step *transform, nil_handler handler) { - desc->ivar_id = ivar_id; - desc->fully_optional = fully_optional; - desc->prop = prop; - desc->default_ = default_; - desc->hash_key = hash_key; - desc->precomputed_hash = sorbet_hash_string(hash_key); - desc->transform = transform; - desc->on_nil = handler; -} - -struct prop_descs_vec { - /* Modules for custom deserialization or subtypes of Serialiazable. Transform - * steps for such types hold indices into this array. */ - size_t n_modules; - VALUE *modules; - - /* Call caches. Transform steps that call Ruby methods hold indices into - * this array. */ - size_t n_caches; - struct rb_call_data *caches; - - /* The actual property descriptors. */ - size_t n_descs; - struct prop_desc *descs; -}; - -/* Transfer accumulated context information such as the modules for custom - * serialization/deserialization or call caches from `ctx` into `vec`; we have already - * allocated the property descriptors for `vec`. - */ -static void transfer_context(struct serde_ctx *ctx, struct prop_descs_vec *vec) { - long len = RARRAY_LEN(ctx->module_array); - if (len == 0) { - vec->n_modules = 0; - vec->modules = NULL; - } else { - vec->n_modules = len; - vec->modules = calloc(sizeof(VALUE), len); - for (long i = 0; i < len; ++i) { - vec->modules[i] = RARRAY_AREF(ctx->module_array, i); - } - } - - if (ctx->call_cache_index == 0) { - vec->n_caches = 0; - vec->caches = NULL; - } else { - vec->n_caches = ctx->call_cache_index; - vec->caches = calloc(sizeof(*vec->caches), ctx->call_cache_index); - for (long i = 0; i < ctx->call_cache_index; ++i) { - vec->caches[i].ci.mid = rb_sym2id(RARRAY_AREF(ctx->call_cache_array, i)); - } - } -} - -/* This function and the interface it implements exists only to provide accounting - * for ObjectSpace (`require 'objspace'`); it does not interact with the GC in any - * way. Therefore, it does not need to be absolutely precise; we account here for - * the various vectors we track, but we do not attempt to measure the memory taken - * by the transform needed by each prop. - */ -static size_t prop_descs_vec_memsize(const void *data) { - const struct prop_descs_vec *vec = data; - const size_t self_size = sizeof(*vec); - const size_t modules_size = sizeof(*vec->modules) * vec->n_modules; - const size_t caches_size = sizeof(*vec->caches) * vec->n_caches; - const size_t descs_size = sizeof(*vec->descs) * vec->n_descs; - return self_size + modules_size + descs_size; -} - -static void prop_descs_vec_mark(void *data) { - struct prop_descs_vec *vec = data; - for (size_t i = 0; i < vec->n_descs; ++i) { - struct prop_desc *desc = &vec->descs[i]; - rb_gc_mark(desc->prop); - rb_gc_mark(desc->default_); - rb_gc_mark(desc->hash_key); - } - for (size_t i = 0; i < vec->n_modules; ++i) { - rb_gc_mark(vec->modules[i]); - } -} - -static void prop_descs_vec_free(void *data) { - struct prop_descs_vec *vec = data; - for (size_t i = 0; i < vec->n_descs; ++i) { - struct prop_desc *desc = &vec->descs[i]; - step_free(desc->transform); - } - free(vec->modules); - free(vec->caches); - free(vec->descs); - free(vec); -} - -static const rb_data_type_t prop_descs_vec_type = { - .wrap_struct_name = "prop_descs_vec", - .function = - { - .dmark = prop_descs_vec_mark, - .dfree = prop_descs_vec_free, - .dsize = prop_descs_vec_memsize, - }, - .data = NULL, - .flags = RUBY_TYPED_FREE_IMMEDIATELY, -}; - -struct serialize_descs_closure { - struct serde_ctx ctx; - struct prop_descs_vec *descs_array; - size_t i; -}; - -/* prop: Symbol - * rules: T::Hash[Symbol, T.untyped] - * arg: a `struct deserialize_desc_closure *` masquerading as a Ruby VALUE - */ -static int serialize_desc_for_prop(VALUE prop, VALUE rules, VALUE arg) { - struct serialize_descs_closure *closure = (struct serialize_descs_closure *)arg; - VALUE hash_key; - ID ivar_id; - - validate_prop(prop, rules, &hash_key, &ivar_id); - - VALUE type_object = rb_hash_fetch(rules, RB_ID2SYM(rb_intern("type_object"))); - VALUE underlying_type = rb_funcallv(rb_mT_Utils_Nilable, rb_intern("get_underlying_type_object"), 1, &type_object); - - struct transform_step *step = step_for_type(&closure->ctx, underlying_type, Serialize); - - VALUE default_doesnt_matter_for_serialize = Qnil; - nil_handler no_handler_for_serialize = NULL; - VALUE fully_optional = rb_hash_aref(rules, RB_ID2SYM(rb_intern("fully_optional"))); - struct prop_desc *desc = &closure->descs_array->descs[closure->i++]; - desc_new(desc, ivar_id, RTEST(fully_optional), prop, default_doesnt_matter_for_serialize, hash_key, step, - no_handler_for_serialize); - - return ST_CONTINUE; -} - -/* Implement - * - * https://github.com/sorbet/sorbet/blob/a3dd6e828748c0a8d44cb4995ca72233bbcea837/gems/sorbet-runtime/lib/types/props/private/deserializer_generator.rb#L105-L163 - * - * but in C, with individual functions standing in for the place of textual strings - * that are returned by the deserialization generator. - */ -static VALUE on_nil_raise_nil_deserialize_error(VALUE self, struct prop_desc *desc) { - VALUE klass = rb_funcallv(self, rb_intern("class"), 0, NULL); - VALUE decorator = rb_funcallv(klass, rb_intern("decorator"), 0, NULL); - VALUE inspected = rb_funcallv(desc->hash_key, rb_intern("inspect"), 0, NULL); - return rb_funcallv(decorator, rb_intern("raise_nil_deserialize_error"), 1, &inspected); -} - -static VALUE on_nil_literal_string(VALUE self, struct prop_desc *desc) { - return rb_str_resurrect(desc->default_); -} - -static VALUE on_nil_literal(VALUE self, struct prop_desc *desc) { - return desc->default_; -} - -static VALUE on_nil_fetch_default(VALUE self, struct prop_desc *desc) { - VALUE klass = rb_funcallv(self, rb_intern("class"), 0, NULL); - VALUE decorator = rb_funcallv(klass, rb_intern("decorator"), 0, NULL); - VALUE props_with_defaults = rb_funcallv(decorator, rb_intern("props_with_defaults"), 0, NULL); - VALUE fetched_prop = rb_funcallv(props_with_defaults, rb_intern("fetch"), 1, &desc->prop); - return rb_funcallv(fetched_prop, rb_intern("default"), 0, NULL); -} - -static VALUE on_nil_empty_array(VALUE self, struct prop_desc *desc) { - return rb_ary_new(); -} - -static VALUE on_nil_empty_hash(VALUE self, struct prop_desc *desc) { - return rb_hash_new(); -} - -static VALUE on_nil_required_prop_missing_from_deserialize(VALUE self, struct prop_desc *desc) { - return rb_funcallv(self, rb_intern("required_prop_missing_from_deserialize"), 1, &desc->prop); -} - -static VALUE on_nil_nil(VALUE self, struct prop_desc *desc) { - return Qnil; -} - -/* The actual logic of DeserializerGenerator.generate_nil_handler. - * - * default_: T.nilable(T::Props::Private::ApplyDefault) - * nilable_type: T::Boolean - * raise_on_nil_write:: T::Boolean - * literal_default: outparam for a defaulted prop with a literal default value - */ -static nil_handler generate_nil_handler(VALUE default_, VALUE nilable_type, VALUE raise_on_nil_write, - VALUE *literal_default) { - if (!RTEST(nilable_type)) { - /* when NilClass */ - if (default_ == Qnil) { - return on_nil_raise_nil_deserialize_error; - } - - /* when ApplyPrimitiveDefault */ - VALUE ApplyPrimitiveDefault = - sorbet_getConstantAt(rb_cObject, rb_intern("T::Props::Private::ApplyPrimitiveDefault")); - if (RTEST(rb_obj_is_kind_of(default_, ApplyPrimitiveDefault))) { - VALUE literal = rb_funcallv(default_, rb_intern("default"), 0, NULL); - /* when String, Integer, Symbol, Float, TrueClass, FalseClass, NilClass */ - if (RB_TYPE_P(literal, T_STRING)) { - *literal_default = literal; - return on_nil_literal_string; - } - - if (RB_INTEGER_TYPE_P(literal) || RB_SYMBOL_P(literal) || RB_FLOAT_TYPE_P(literal) || literal == Qtrue || - literal == Qfalse || literal == Qnil) { - *literal_default = literal; - return on_nil_literal; - } - - return on_nil_fetch_default; - } - - /* when ApplyEmptyArrayDefault */ - VALUE ApplyEmptyArrayDefault = - sorbet_getConstantAt(rb_cObject, rb_intern("T::Props::Private::ApplyEmptyArrayDefault")); - if (RTEST(rb_obj_is_kind_of(default_, ApplyEmptyArrayDefault))) { - return (nil_handler)rb_ary_new; - } - - /* when ApplyEmptyHashDefault */ - VALUE ApplyEmptyHashDefault = - sorbet_getConstantAt(rb_cObject, rb_intern("T::Props::Private::ApplyEmptyHashDefault")); - if (RTEST(rb_obj_is_kind_of(default_, ApplyEmptyHashDefault))) { - return (nil_handler)rb_hash_new; - } - - return on_nil_fetch_default; - } - - if (RTEST(raise_on_nil_write)) { - return on_nil_required_prop_missing_from_deserialize; - } - - return on_nil_nil; -} - -struct deserialize_desc_closure { - struct serde_ctx ctx; - VALUE defaults; - struct prop_descs_vec *descs_array; - size_t i; -}; - -/* prop: Symbol - * rules: T::Hash[Symbol, T.untyped] - * arg: a `struct deserialize_desc_closure *` masquerading as a Ruby VALUE - */ -static int deserialize_desc_for_prop(VALUE prop, VALUE rules, VALUE arg) { - struct deserialize_desc_closure *closure = (struct deserialize_desc_closure *)arg; - VALUE hash_key; - ID ivar_id; - - validate_prop(prop, rules, &hash_key, &ivar_id); - - VALUE type_object = rb_hash_fetch(rules, RB_ID2SYM(rb_intern("type_object"))); - VALUE underlying_type = rb_funcallv(rb_mT_Utils_Nilable, rb_intern("get_underlying_type_object"), 1, &type_object); - - struct transform_step *step = step_for_type(&closure->ctx, underlying_type, Deserialize); - - /* Determine the nil handler and the actual default to use. */ - VALUE default_ = rb_hash_aref(closure->defaults, prop); - VALUE nilable_type = rb_funcallv(rb_mT_Props_Utils, rb_intern("optional_prop?"), 1, &rules); - VALUE raise = rb_hash_aref(rules, RB_ID2SYM(rb_intern("raise_on_nil_write"))); - /* Modified if we have a literal default, value doesn't matter otherwise. */ - VALUE literal_default = Qnil; - nil_handler handler = generate_nil_handler(default_, nilable_type, raise, &literal_default); - - /* value of this doesn't matter for deserialization */ - bool fully_optional = false; - struct prop_desc *desc = &closure->descs_array->descs[closure->i++]; - desc_new(desc, ivar_id, fully_optional, prop, literal_default, hash_key, step, handler); - - return ST_CONTINUE; -} - -static VALUE props_reject_block(VALUE block_arg, VALUE data, int argc, const VALUE *argv, VALUE block_as_proc) { - return rb_hash_aref(argv[1], RB_ID2SYM(rb_intern("dont_store"))); -} - -static struct prop_descs_vec *prop_descs_vec_alloc(size_t length) { - struct prop_descs_vec *vec = calloc(sizeof(*vec), 1); - vec->n_descs = length; - vec->descs = calloc(sizeof(*vec->descs), length); - return vec; -} - -/* We use functions for these to reduce duplication between the generation bits and - * the bits that handle serialization/deserialization, since we need the identifiers - * to stay consistent between the two. We could have used `static const char[]` - * variables, but then we wouldn't benefit from the optimization in include/ruby/ruby.h - * for rb_intern with a constant character array literal argument. - */ -static inline ID serialize_descs_id() { - return rb_intern("@sorbet_serialize_descs"); -} -static inline ID deserialize_descs_id() { - return rb_intern("@sorbet_deserialize_descs"); -} -static inline ID serialize_method_id() { - return rb_intern("__t_props_generated_serialize"); -} -static inline ID deserialize_method_id() { - return rb_intern("__t_props_generated_deserialize"); -} - -/* self: Class - * props: T::Hash[Symbol, T::Hash[Symbol, T.untyped]] - */ -static struct prop_descs_vec *generate_serialize_descs(VALUE self, VALUE props) { - VALUE stored_props = rb_block_call(props, rb_intern("reject"), 0, NULL, props_reject_block, Qnil); - size_t n_props = rb_hash_size_num(stored_props); - struct prop_descs_vec *descs = prop_descs_vec_alloc(n_props); - - struct serialize_descs_closure closure; - closure.ctx.module_index = 0; - closure.ctx.module_array = rb_ary_new(); - closure.ctx.call_cache_index = 0; - closure.ctx.call_cache_array = rb_ary_new(); - closure.descs_array = descs; - closure.i = 0; - - rb_hash_foreach(stored_props, serialize_desc_for_prop, (VALUE)&closure); - - transfer_context(&closure.ctx, descs); - - return descs; -} - -/* self: the object being serialized - * strict: T::Boolean - * - * returns: T::Hash[String, T.untyped] - */ -static VALUE t_props_generated_serialize_impl(VALUE self, VALUE strict) { - VALUE descs_value = rb_ivar_get(rb_obj_class(self), serialize_descs_id()); - struct prop_descs_vec *vec; - TypedData_Get_Struct(descs_value, struct prop_descs_vec, &prop_descs_vec_type, vec); - long descs_length = vec->n_descs; - VALUE h = rb_hash_new_with_size(descs_length); - - struct transform_ctx ctx; - ctx.n_modules = vec->n_modules; - ctx.modules = vec->modules; - ctx.caches = vec->caches; - ctx.strict = strict; - - for (long i = 0; i < descs_length; ++i) { - struct prop_desc *desc = &vec->descs[i]; - VALUE ivar = sorbet_vm_getivar(self, desc->ivar_id, &desc->iv_cache); - VALUE nil_p = rb_funcallv(ivar, idNilP, 0, NULL); - - /* https://github.com/sorbet/sorbet/blob/a3dd6e828748c0a8d44cb4995ca72233bbcea837/gems/sorbet-runtime/lib/types/props/private/serializer_generator.rb#L47-L63 - */ - if (nil_p) { - if (RTEST(strict) && !desc->fully_optional) { - rb_funcallv(self, rb_intern("required_prop_missing_from_serialize"), 1, &desc->prop); - } - /* Don't serialize values that are nil to save space (both the nil - * value itself and the field name in the serialized BSON document). - */ - } else { - struct transform_step *step = desc->transform; - rb_hash_aset(h, desc->hash_key, step->fun(ivar, &ctx, step)); - } - } - - return h; -} - -/* self: Class - * props: T::Hash[Symbol, T::Hash[Symbol, T.untyped]] - * defaults: T::Hash[Symbol, T::Props::Private::ApplyDefault] - */ -static struct prop_descs_vec *generate_deserialize_descs(VALUE self, VALUE props, VALUE defaults) { - VALUE stored_props = rb_block_call(props, rb_intern("reject"), 0, NULL, props_reject_block, Qnil); - size_t n_props = rb_hash_size_num(stored_props); - struct prop_descs_vec *descs = prop_descs_vec_alloc(n_props); - - struct deserialize_desc_closure closure; - closure.ctx.module_index = 0; - closure.ctx.module_array = rb_ary_new(); - closure.ctx.call_cache_index = 0; - closure.ctx.call_cache_array = rb_ary_new(); - closure.defaults = defaults; - closure.descs_array = descs; - closure.i = 0; - - rb_hash_foreach(stored_props, deserialize_desc_for_prop, (VALUE)&closure); - - transfer_context(&closure.ctx, descs); - - return descs; -} - -struct deserialize_step_closure { - VALUE val; - struct transform_ctx *ctx; - struct transform_step *step; -}; - -static VALUE run_deserialize_step(VALUE closure) { - struct deserialize_step_closure *c = (struct deserialize_step_closure *)closure; - return c->step->fun(c->val, c->ctx, c->step); -} - -static VALUE catch_nomethod_error(VALUE x, VALUE exception) { - VALUE *errptr = (VALUE *)x; - *errptr = exception; - return Qundef; -} - -/* Represents the state of a prop in the hash for deserialization. */ -enum prop_existence { - NotPresent, - WasNil, - Present, -}; - -/* hash: Hash, not a subtype */ -static enum prop_existence retrieve_prop_from_hash_fast(VALUE hash, struct prop_desc *desc, VALUE *out) { - /* Use Qundef so we don't have to do a double lookup in the case of a - * not-present hash_key; we can also avoid a nil? send. - * cf. - * https://github.com/sorbet/sorbet/blob/a3dd6e828748c0a8d44cb4995ca72233bbcea837/gems/sorbet-runtime/lib/types/props/private/deserializer_generator.rb#L85-L93 - */ - VALUE val = rb_hash_lookup2(hash, desc->hash_key, Qundef); - *out = val; - - if (val == Qundef) { - return NotPresent; - } - - if (val == Qnil) { - return WasNil; - } - - return Present; -} - -/* hash: Hash, not a subtype, known to be !RHASH_AR_TABLE_P */ -static enum prop_existence retrieve_prop_from_hash_st_fast(VALUE hash, struct prop_desc *desc, VALUE *out) { - /* Use Qundef so we don't have to do a double lookup in the case of a - * not-present hash_key; we can also avoid a nil? send. - * cf. - * https://github.com/sorbet/sorbet/blob/a3dd6e828748c0a8d44cb4995ca72233bbcea837/gems/sorbet-runtime/lib/types/props/private/deserializer_generator.rb#L85-L93 - */ - VALUE val; - st_data_t stored; - if (st_lookup_with_precomputed_hash(RHASH_ST_TABLE(hash), desc->hash_key, &stored, desc->precomputed_hash)) { - val = (VALUE)stored; - } else { - val = Qundef; - } - *out = val; - - if (val == Qundef) { - return NotPresent; - } - - if (UNLIKELY(val == Qnil)) { - return WasNil; - } - - return Present; -} - -/* hash: a proper subtype of Hash or some other object responding to Hash's methods */ -static enum prop_existence retrieve_prop_from_hash_slow(VALUE hash, struct prop_desc *desc, VALUE *out) { - /* Some Stripe code passes in custom hashes to deserialization that rely on - * having their #[] method called. So we have to explicitly call that here, - * which means we also have to be somewhat slower in case we didn't find the - * prop in the hash. - */ - VALUE val = rb_funcallv(hash, rb_intern("[]"), 1, &desc->hash_key); - *out = val; - - if (val == Qnil) { - VALUE has_key_p = rb_funcallv(hash, rb_intern("key?"), 1, &desc->hash_key); - return RTEST(has_key_p) ? WasNil : NotPresent; - } - - return Present; -} - -static __attribute__((always_inline)) VALUE -run_deserialization(VALUE self, VALUE hash, struct prop_descs_vec *vec, - enum prop_existence (*retrieval_func)(VALUE, struct prop_desc *, VALUE *)) { - long descs_length = vec->n_descs; - long found = descs_length; - - struct transform_ctx ctx; - ctx.n_modules = vec->n_modules; - ctx.modules = vec->modules; - ctx.caches = vec->caches; - ctx.strict = Qfalse; - - for (long i = 0; i < descs_length; ++i) { - struct prop_desc *desc = &vec->descs[i]; - VALUE val; - enum prop_existence status = retrieval_func(hash, desc, &val); - switch (status) { - case NotPresent: - found -= 1; - /* fallthrough */ - case WasNil: - val = desc->on_nil(self, desc); - break; - case Present: { - struct transform_step *step = desc->transform; - /* avoid begin/rescue overhead if we don't have to do anything - * cf. - * https://github.com/sorbet/sorbet/blob/a3dd6e828748c0a8d44cb4995ca72233bbcea837/gems/sorbet-runtime/lib/types/props/private/deserializer_generator.rb#L51-L75 - */ - if (!step_is_passthrough(step)) { - struct deserialize_step_closure closure; - closure.val = val; - closure.ctx = &ctx; - closure.step = step; - VALUE err = Qundef; - - VALUE result = rb_rescue2(run_deserialize_step, (VALUE)&closure, catch_nomethod_error, (VALUE)&err, - rb_eNoMethodError, 0); - if (result == Qundef) { - rb_funcall(self, rb_intern("raise_deserialization_error"), 3, desc->prop, val, err); - /* Leave val alone in case the error handler returns */ - } else { - val = result; - } - } - break; - } - } - - sorbet_vm_setivar(self, desc->ivar_id, val, &desc->iv_cache); - } - - return LONG2FIX(found); -} - -static VALUE t_props_generated_deserialize_impl(VALUE self, VALUE hash) { - VALUE descs_value = rb_ivar_get(rb_obj_class(self), deserialize_descs_id()); - struct prop_descs_vec *vec; - TypedData_Get_Struct(descs_value, struct prop_descs_vec, &prop_descs_vec_type, vec); - - /* TODO: maybe we should look at HASH_REDEFINED_OP_FLAG too? */ - if (LIKELY(can_directly_call_hash_functions(hash))) { - if (RHASH_AR_TABLE_P(hash)) { - return run_deserialization(self, hash, vec, retrieve_prop_from_hash_fast); - } - - return run_deserialization(self, hash, vec, retrieve_prop_from_hash_st_fast); - } - - return run_deserialization(self, hash, vec, retrieve_prop_from_hash_slow); -} - -/* We do a little dance to be compatible with sorbet-runtime. When props are added - * to serializable things, a lazy method definition for each of - * __t_props_generated_serialize and __t_props_generated_deserialize gets enqueued: - * - * https://github.com/sorbet/sorbet/blob/a3dd6e828748c0a8d44cb4995ca72233bbcea837/gems/sorbet-runtime/lib/types/props/serializable.rb#L200-L207 - * - * Lazy definition enqueueing is defined here: - * - * https://github.com/sorbet/sorbet/blob/a3dd6e828748c0a8d44cb4995ca72233bbcea837/gems/sorbet-runtime/lib/types/props/has_lazily_specialized_methods.rb#L64-L77 - * - * The method is defined, on first call, to evaluate the string returned by the block - * in add_prop_definition, above. - * - * https://github.com/sorbet/sorbet/blob/a3dd6e828748c0a8d44cb4995ca72233bbcea837/gems/sorbet-runtime/lib/types/props/has_lazily_specialized_methods.rb#L51-L62 - * - * However, we don't have a source string that we can provide in any reasonable sense. - * We could define a secret method and then return a call to that method, letting the - * called secret method set up everything. But that seems like a bit too much indirection. - * - * Instead, we're going to put a configuration hook in sorbet-runtime for indicating - * whether to define serialization and deserialization methods via Ruby hooks (the - * status quo), or whether to define serialization and deserialization via these - * secret methods in the compiler. We can choose between these via a configuration - * option in sorbet-runtime. - * - * We define: - * - * T::Props::Private::SerializerGenerator.generate2(klass, props) - * T::Props::Private::DeserializerGenerator.generate2(klass, props, defaults) - * - * which point at these methods. If the appropriate configuration option is set, the - * lazy deserialization hooks will call the above methods, which will then install the - * appropriate __t_props_generated_{serialize,deserialize} method on klass. - */ -VALUE T_Props_Private_Serialize_generate2(VALUE recv, VALUE klass, VALUE props) { - struct prop_descs_vec *vec = generate_serialize_descs(klass, props); - - rb_ivar_set(klass, serialize_descs_id(), TypedData_Wrap_Struct(0, &prop_descs_vec_type, vec)); - - ID method = serialize_method_id(); - rb_define_method_id(klass, method, t_props_generated_serialize_impl, 1); - return RB_ID2SYM(method); -} - -VALUE T_Props_Private_Deserialize_generate2(VALUE self, VALUE klass, VALUE props, VALUE defaults) { - struct prop_descs_vec *vec = generate_deserialize_descs(klass, props, defaults); - - rb_ivar_set(klass, deserialize_descs_id(), TypedData_Wrap_Struct(0, &prop_descs_vec_type, vec)); - - ID method = deserialize_method_id(); - rb_define_method_id(klass, method, t_props_generated_deserialize_impl, 1); - return RB_ID2SYM(method); -} diff --git a/compiler/IREmitter/Payload/sorbet-t-modules.c b/compiler/IREmitter/Payload/sorbet-t-modules.c deleted file mode 100644 index 0ff0316bed..0000000000 --- a/compiler/IREmitter/Payload/sorbet-t-modules.c +++ /dev/null @@ -1,77 +0,0 @@ -#include "internal.h" -#include "ruby.h" - -VALUE rb_mT; -VALUE rb_mT_Configuration; -VALUE rb_mT_Props; -VALUE rb_mT_Props_CustomType; -VALUE rb_mT_Props_Private; -VALUE rb_mT_Props_Private_DeserializerGenerator; -VALUE rb_mT_Props_Private_SerializerGenerator; -VALUE rb_mT_Props_Serializable; -VALUE rb_mT_Props_Utils; -VALUE rb_mT_Types; -VALUE rb_cT_Types_Base; -VALUE rb_cT_Types_Enum; -VALUE rb_cT_Types_Intersection; -VALUE rb_cT_Types_Simple; -VALUE rb_cT_Types_TypedArray; -VALUE rb_cT_Types_TypedEnumerable; -VALUE rb_cT_Types_TypedHash; -VALUE rb_cT_Types_TypedSet; -VALUE rb_cT_Types_Union; -VALUE rb_mT_Utils; -VALUE rb_mT_Utils_Nilable; - -extern long sorbet_globalConstRegister(VALUE); - -#define GCABLE_VALUE(name, expr) \ - do { \ - name = expr; \ - sorbet_globalConstRegister(name); \ - } while (0) - -void Init_Sorbet_T() { - // Set up modules that are defined by the sorbet-runtime gem. Note that Ruby - // will complain about mismatches if we get e.g. the class inheritance - // structure wrong relative to how it is actually defined in sorbet-runtime. - - GCABLE_VALUE(rb_mT, rb_define_module("T")); - - GCABLE_VALUE(rb_mT_Configuration, rb_define_module_under(rb_mT, "Configuration")); - - GCABLE_VALUE(rb_mT_Props, rb_define_module_under(rb_mT, "Props")); - GCABLE_VALUE(rb_mT_Props_CustomType, rb_define_module_under(rb_mT_Props, "CustomType")); - GCABLE_VALUE(rb_mT_Props_Private, rb_define_module_under(rb_mT_Props, "Private")); - - GCABLE_VALUE(rb_mT_Props_Private_DeserializerGenerator, - rb_define_module_under(rb_mT_Props_Private, "DeserializerGenerator")); - GCABLE_VALUE(rb_mT_Props_Private_SerializerGenerator, - rb_define_module_under(rb_mT_Props_Private, "SerializerGenerator")); - - GCABLE_VALUE(rb_mT_Props_Serializable, rb_define_module_under(rb_mT_Props, "Serializable")); - GCABLE_VALUE(rb_mT_Props_Utils, rb_define_module_under(rb_mT_Props, "Utils")); - - GCABLE_VALUE(rb_mT_Types, rb_define_module_under(rb_mT, "Types")); - GCABLE_VALUE(rb_cT_Types_Base, rb_define_class_under(rb_mT_Types, "Base", rb_cObject)); - GCABLE_VALUE(rb_cT_Types_Enum, rb_define_class_under(rb_mT_Types, "Enum", rb_cT_Types_Base)); - GCABLE_VALUE(rb_cT_Types_Intersection, rb_define_class_under(rb_mT_Types, "Intersection", rb_cT_Types_Base)); - GCABLE_VALUE(rb_cT_Types_Simple, rb_define_class_under(rb_mT_Types, "Simple", rb_cT_Types_Base)); - GCABLE_VALUE(rb_cT_Types_TypedEnumerable, rb_define_class_under(rb_mT_Types, "TypedEnumerable", rb_cT_Types_Base)); - GCABLE_VALUE(rb_cT_Types_TypedArray, rb_define_class_under(rb_mT_Types, "TypedArray", rb_cT_Types_TypedEnumerable)); - GCABLE_VALUE(rb_cT_Types_TypedHash, rb_define_class_under(rb_mT_Types, "TypedHash", rb_cT_Types_TypedEnumerable)); - GCABLE_VALUE(rb_cT_Types_TypedSet, rb_define_class_under(rb_mT_Types, "TypedSet", rb_cT_Types_TypedEnumerable)); - GCABLE_VALUE(rb_cT_Types_Union, rb_define_class_under(rb_mT_Types, "Union", rb_cT_Types_Base)); - - GCABLE_VALUE(rb_mT_Utils, rb_define_module_under(rb_mT, "Utils")); - GCABLE_VALUE(rb_mT_Utils_Nilable, rb_define_module_under(rb_mT_Utils, "Nilable")); - - // Add new methods specific to C-based serialization. - - extern VALUE T_Props_Private_Deserialize_generate2(VALUE self, VALUE klass, VALUE props, VALUE defaults); - extern VALUE T_Props_Private_Serialize_generate2(VALUE self, VALUE klass, VALUE props); - rb_define_singleton_method(rb_mT_Props_Private_DeserializerGenerator, "generate2", - T_Props_Private_Deserialize_generate2, 3); - rb_define_singleton_method(rb_mT_Props_Private_SerializerGenerator, "generate2", - T_Props_Private_Serialize_generate2, 2); -} diff --git a/compiler/IREmitter/Payload/tools/generate_load_payload.cc b/compiler/IREmitter/Payload/tools/generate_load_payload.cc deleted file mode 100644 index f352e0eff4..0000000000 --- a/compiler/IREmitter/Payload/tools/generate_load_payload.cc +++ /dev/null @@ -1,26 +0,0 @@ -#include "common/FileOps.h" -#include "common/common.h" -#include -#include -#include -#include -#include -#include - -using namespace std; -int main(int argc, char **argv) { - { - ofstream out(argv[2], ios::trunc); - auto data = sorbet::FileOps::read(argv[1]); - out << "#include" << '\n' << "\nusing namespace std;\n"; - out << "namespace sorbet{" << '\n' << "namespace compiler{\n"; - out << "static char actualData[] = {"; - for (int i = 0; i < data.length(); i++) { - out << (int)data[i] << ", "; - } - out << "};\n string_view getDefaultModuleBitcode() {\n return string_view(&actualData[0], " << data.length() - << ");\n}\n};\n};\n"; - } - - return 0; -} diff --git a/compiler/IREmitter/Payload/tools/generate_payload_bc_orig_generator.sh b/compiler/IREmitter/Payload/tools/generate_payload_bc_orig_generator.sh deleted file mode 100755 index aae732b5a0..0000000000 --- a/compiler/IREmitter/Payload/tools/generate_payload_bc_orig_generator.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/bash - -set -euo pipefail -ar="$(pwd)/$1" -link="$(pwd)/$2" -output="$(pwd)/$3" -xxd="$(pwd)/$4" -afile="$(pwd)/$5" -workdir=$(mktemp -d) - -cd "$workdir" - -extract_bitcode() { - local object_file="$1" - local bc_file="$object_file.bc" - rm -rf "$object_file" - $ar -x "$afile" "$object_file" - - if [[ "$OSTYPE" == "linux-gnu" ]]; then - readelf -x .llvmbc "$object_file" > bitcode.dump - tail -n +3 < bitcode.dump | sed 's/\(.\{13\}\)\(.\{35\}\).*/\2/' > bitcode-xxd.dump - $xxd -r -p < bitcode-xxd.dump > "$bc_file" - elif [[ "$OSTYPE" == "darwin"* ]]; then - otool -s __LLVM __bitcode "$object_file" > bitcode.dump - tail -n +3 < bitcode.dump > bitcode-xxd.dump - hex=$(head -n 1 bitcode-xxd.dump | cut -f 1) - offset=$(( 16#$hex )) - $xxd -r --seek -$offset bitcode-xxd.dump "$bc_file" - fi -} - -# Extract bitcode from all objects from the archive -$ar -t "$afile" | grep '.o$' | while read -r ofile; do - extract_bitcode "$ofile" -done - -$link -o "$output" ./*.bc - -rm -rf "$workdir" diff --git a/compiler/IREmitter/Payload/tools/postprocess_payload.cc b/compiler/IREmitter/Payload/tools/postprocess_payload.cc deleted file mode 100644 index 675f36c36a..0000000000 --- a/compiler/IREmitter/Payload/tools/postprocess_payload.cc +++ /dev/null @@ -1,143 +0,0 @@ -#include - -#include -#include -#include -#include -#include -#include -#include - -using llvm::BitcodeWriter; -using llvm::LLVMContext; -using llvm::Module; -using llvm::SMDiagnostic; -using llvm::StringRef; - -namespace { - -// Mark all `sorbet_` functions defined in the payload with `internal` linkage, so that they are available for inlining, -// but are GC'd if they aren't used. -// -// NOTE: marking these functions as `internal` is the same as marking them `static` in codegen-payload.c. However, if we -// were to mark them all static, the compiler would prune them all out and we'd be left with an empty payload. We want -// these functions to be private to code-generation, and there's just not a great way to say that in the c source. -void setSorbetFunctionLinkage(Module &module) { - for (auto &fun : module.functions()) { - auto name = fun.getName(); - - if (fun.isDeclaration()) { - // When given ruby code like the following: - // if cond - // ABC.new(1) - // else - // DEF.new(1) - // end - // We emit something like the following: - // %1 = - // br i1 %1, label %BB1, label %BB2 - // BB1: - // class = sorbet_i_getRubyClass("ABC") - // sorbet_i_send(class, "new") - // BB2: - // class = sorbet_i_getRubyClass("DEF") - // sorbet_i_send(class, "new") - // LLVM notices that ABC and DEF are string constants of the same length, and replaces this with: - // class = sorbet_i_getRubyClass(phi "ABC", "DEF") - // sorbet_i_send(class, "new") - // This causes an issue, because sorbet_i_getRubyClass can only handle constants, and returns undef when - // it encounters the phi node. - // By marking sorbet_i_getRubyClass and sorbet_i_getRubyConstant as nomerge, we are telling LLVM not to - // attempt to dedup calls to them, and emit the original LLVM code instead. - if (name.equals("sorbet_i_getRubyClass") || name.equals("sorbet_i_getRubyConstant")) { - fun.addFnAttr(llvm::Attribute::NoMerge); - } - continue; - } - - // Keep the special keep-alive functions as external, as they will be explicitly removed by a pass late in - // the compiler's pipeline. - if (name.startswith("sorbet_exists_to_keep_alive_") && fun.hasExternalLinkage()) { - continue; - } - - // Set all of the `external` (default linkage) functions defined in the payload to `internal` linkage, as any - // other functions have more specific linkage set explicitly in the source. - if (name.startswith("sorbet_") && fun.hasExternalLinkage()) { - fun.setLinkage(llvm::GlobalValue::InternalLinkage); - } - } -} - -// Mark ruby constant globals explicitly as constants -- we know that values like `rb_cClass` etc are never going to -// change once the vm has initialized. -// -// quoting spec: -// > LLVM explicitly allows declarations of global variables to be marked constant, even if the final definition of the -// > global is not. This capability can be used to enable slightly better optimization of the program, but requires the -// > language definition to guarantee that optimizations based on the ‘constantness’ are valid for the translation units -// > that do not include the definition. -void markRubyConstants(Module &module) { - for (auto &cnst : module.globals()) { - auto name = cnst.getName(); - - // TODO: this should be expanded to `rb_e` and `rb_m` as well - if (name.startswith("rb_c") && cnst.getUnnamedAddr() == llvm::GlobalValue::UnnamedAddr::Local) { - cnst.setConstant(true); - } - - // See the definition of `SORBET_CONSTANT` in the codegen payload. - if (name.startswith("sorbet_") && - cnst.getVisibility() == llvm::GlobalValue::VisibilityTypes::HiddenVisibility) { - cnst.setVisibility(llvm::GlobalValue::VisibilityTypes::DefaultVisibility); - cnst.setLinkage(llvm::GlobalVariable::LinkageTypes::InternalLinkage); - } - } -} - -void clearModuleFlags(Module &module) { - // Remove the llvm.module.flags debug metadata, as it will raise verification errors when inlining functions defined - // in the payload if it's present. - if (auto *flags = module.getModuleFlagsMetadata()) { - flags->eraseFromParent(); - } - - // Remove the llvm.ident metadata as we don't really need to include information about the version of clang we used - // to generate the payload. - if (auto *ident = module.getNamedMetadata("llvm.ident")) { - ident->eraseFromParent(); - } -} - -} // namespace - -int main(int argc, char **argv) { - if (argc != 3) { - std::cerr << "Usage: postprocess_payload " << std::endl; - return 1; - } - - LLVMContext ctx; - SMDiagnostic errors; - auto module = llvm::parseIRFile(StringRef(argv[1]), errors, ctx); - - setSorbetFunctionLinkage(*module); - markRubyConstants(*module); - clearModuleFlags(*module); - - // We strip out information that makes debug info work in clearModuleFlags, so strip everything out as that will - // happen when the payload is loaded by sorbet. - llvm::StripDebugInfo(*module); - - // Sanity check the changes we make - if (llvm::verifyModule(*module, &llvm::errs())) { - return 1; - } - - std::error_code ec; - llvm::raw_fd_ostream out(argv[2], ec, llvm::sys::fs::OF_None); - llvm::WriteBitcodeToFile(*module, out); - out.flush(); - - return 0; -} diff --git a/compiler/IREmitter/Payload/vm-payload.c b/compiler/IREmitter/Payload/vm-payload.c deleted file mode 100644 index e1c2be84e0..0000000000 --- a/compiler/IREmitter/Payload/vm-payload.c +++ /dev/null @@ -1,572 +0,0 @@ -// -// payload.c -// -// Most of the APIs we're using here are normal Ruby C extension APIs. -// Suggested reading: -// -// - -// - -// -// You'll also find a lot of useful information from grepping the Ruby source code. -// - -#include "sorbet_version/sorbet_version.h" - -// These are public Ruby headers. Feel free to add more from the include/ruby -// directory -#include "ruby/encoding.h" // for rb_encoding - -// These are special "public" headers which don't live in include/ruby for some -// reason -#include "internal.h" -#include "ruby.h" -#include - -// This is probably a bad idea but is needed for so many things -#include "vm_core.h" - -// This is for the enum definition for YARV instructions -#include "insns.inc" - -#define SORBET_ATTRIBUTE(...) __attribute__((__VA_ARGS__)) - -#define SORBET_INLINE SORBET_ATTRIBUTE(always_inline) - -// Paul's and Dmitry's laptops have different attributes for this function in system libraries. -void abort(void) __attribute__((__cold__)) __attribute__((__noreturn__)); - -// Common definitions - -typedef VALUE (*BlockFFIType)(VALUE firstYieldedArg, VALUE closure, int argCount, const VALUE *args, VALUE blockArg); - -typedef VALUE (*ExceptionFFIType)(VALUE **pc, VALUE closure, rb_control_frame_t *cfp); - -// **** -// **** Internal Helper Functions -// **** - -const char *sorbet_dbg_pi(ID id) { - return rb_id2name(id); -} - -const char *sorbet_dbg_p(VALUE obj) { - char *ret = RSTRING_PTR(rb_sprintf("%" PRIsVALUE, obj)); - return ret; -} - -void sorbet_stopInDebugger() { - raise(SIGTRAP); -} - -// **** -// **** Constants, Classes and Modules -// **** - -VALUE sorbet_rb_cObject() { - return rb_cObject; -} - -static VALUE sorbet_constants; - -// Register a value with the `sorbet_constants` array, and return the index that it occupies in that array. This -// indirection enables us to register one address with the garbage collector as a GC root, rather than one address per -// global constant we plan to hold. The downside here is the indirection through a ruby array, however the benefit is -// that we don't grow the linked list in the garbage collector that defines additional gc roots. -long sorbet_globalConstRegister(VALUE val) { - if (UNLIKELY(sorbet_constants == 0)) { - sorbet_constants = rb_ary_new(); - rb_gc_register_address(&sorbet_constants); - } - - // NOTE: this is a big assumption about not running in a threaded context - long idx = RARRAY_LEN(sorbet_constants); - rb_ary_push(sorbet_constants, val); - - return idx; -} - -static VALUE sorbet_globalConstFetch(long idx) { - if (UNLIKELY(idx >= RARRAY_LEN(sorbet_constants))) { - rb_raise(rb_eIndexError, "%ld is out of bounds for the sorbet_constants array (%ld)\n", idx, - RARRAY_LEN(sorbet_constants)); - } - return RARRAY_AREF(sorbet_constants, idx); -} - -// Lookup a hash literal in the global constants array, and duplicate it. -VALUE sorbet_globalConstDupHash(long idx) { - VALUE hash = sorbet_globalConstFetch(idx); - return rb_hash_dup(hash); -} - -struct vm_ifunc *sorbet_globalConstFetchIfunc(long idx) { - return (struct vm_ifunc *)sorbet_globalConstFetch(idx); -} - -// **** -// **** Calls -// **** - -// defining a way to allocate storage for custom class: -// VALUE allocate(VALUE klass); -// rb_define_alloc_func(class, &allocate) -// - -VALUE sorbet_rb_arity_error_new(int argc, int min, int max) { - VALUE err_mess = 0; - if (min == max) { - err_mess = rb_sprintf("wrong number of arguments (given %d, expected %d)", argc, min); - } else if (max == UNLIMITED_ARGUMENTS) { - err_mess = rb_sprintf("wrong number of arguments (given %d, expected %d+)", argc, min); - } else { - err_mess = rb_sprintf("wrong number of arguments (given %d, expected %d..%d)", argc, min, max); - } - return rb_exc_new3(rb_eArgError, err_mess); -} - -__attribute__((__cold__, __noreturn__)) void sorbet_cast_failure(VALUE value, char *castMethod, char *type) { - // TODO: cargo cult more of - // https://github.com/sorbet/sorbet/blob/b045fb1ba12756c3760fe516dc315580d93f3621/gems/sorbet-runtime/lib/types/types/base.rb#L105 - // - // e.g. we need to teach the `got` part to do `T.class_of` - rb_raise(rb_eTypeError, "%s: Expected type %s, got type %s with value %+" PRIsVALUE, castMethod, type, - rb_obj_classname(value), value); -} - -__attribute__((__noreturn__)) void sorbet_raiseArity(int argc, int min, int max) { - rb_exc_raise(sorbet_rb_arity_error_new(argc, min, max)); -} - -VALUE sorbet_addMissingKWArg(VALUE missing, VALUE sym) { - if (UNLIKELY(missing == RUBY_Qundef)) { - missing = rb_ary_new(); - } - - rb_ary_push(missing, sym); - return missing; -} - -// from class.c -VALUE rb_keyword_error_new(const char *error, VALUE keys); - -__attribute__((__noreturn__)) void sorbet_raiseMissingKeywords(VALUE missing) { - rb_exc_raise(rb_keyword_error_new("missing", missing)); -} - -__attribute__((__noreturn__)) void sorbet_raiseCallDataExtraKeywords(int keyword_len, VALUE *keywords) { - // This is not quite right, but we can fix that up later. - VALUE missing = rb_ary_new(); - for (int i = 0; i < keyword_len; ++i) { - rb_ary_push(missing, keywords[i]); - } - sorbet_raiseMissingKeywords(missing); -} - -__attribute__((__noreturn__)) void sorbet_raiseExtraKeywords(VALUE hash) { - VALUE err_mess = rb_sprintf("unknown keywords: %" PRIsVALUE, rb_hash_keys(hash)); - rb_exc_raise(rb_exc_new3(rb_eArgError, err_mess)); -} - -__attribute__((__cold__)) VALUE sorbet_t_absurd(VALUE val) { - VALUE t = rb_const_get(rb_cObject, rb_intern("T")); - return rb_funcall(t, rb_intern("absurd"), 1, val); -} - -// **** -// **** Optimized versions of callFunc. -// **** Should use the same calling concention. -// **** Call it ending with `_no_type_guard` if implementation has a backed in slowpath -// **** -// **** - -// **** -// **** Control Frames -// **** - -/* From inseq.h */ -struct iseq_insn_info_entry { - int line_no; - rb_event_flag_t events; -}; - -void rb_iseq_insns_info_encode_positions(const rb_iseq_t *iseq); -/* End from inseq.h */ - -struct SorbetLineNumberInfo { - int iseq_size; - struct iseq_insn_info_entry *insns_info; - VALUE *iseq_encoded; -}; - -void sorbet_initLineNumberInfo(struct SorbetLineNumberInfo *info, VALUE *iseq_encoded, int numLines) { - // This is the table that tells us the hash entry for instruction types - const void *const *table = rb_vm_get_insns_address_table(); - VALUE nop = (VALUE)(table[YARVINSN_nop]); - - info->iseq_size = numLines; - info->insns_info = ALLOC_N(struct iseq_insn_info_entry, numLines); - info->iseq_encoded = iseq_encoded; - - for (int i = 0; i < numLines; i++) { - int lineno = i + 1; - info->insns_info[i].line_no = lineno; - - // we fill iseq_encoded with NOP instructions; it only exists because it - // has to match the length of insns_info. - info->iseq_encoded[i] = nop; - } -} - -// NOTE: parent is the immediate parent frame, so for the rescue clause of a -// top-level method the parent would be the method iseq, but for a rescue clause -// nested within a rescue clause, it would be the outer rescue iseq. -// -// https://github.com/ruby/ruby/blob/a9a48e6a741f048766a2a287592098c4f6c7b7c7/compile.c#L5669-L5671 -rb_iseq_t *sorbet_allocateRubyStackFrame(VALUE funcName, ID func, VALUE filename, VALUE realpath, unsigned char *parent, - int iseqType, int startLine, struct SorbetLineNumberInfo *info, ID *locals, - int numLocals, int stackMax) { - // DO NOT ALLOCATE RUBY LEVEL OBJECTS HERE. All objects that are passed to - // this function should be retained (for GC purposes) by something else. - - // ... but actually this line allocates and will not be retained by anyone else, - // so we pin this object right here. TODO: This leaks memory - rb_iseq_t *iseq = rb_iseq_new(0, funcName, filename, realpath, (rb_iseq_t *)parent, iseqType); - rb_gc_register_mark_object((VALUE)iseq); - - // The SorbetLineNumberInfo that we pass in, and the way we set up the encoded - // positions for the iseq, use absolute line numbers from the beginning of the - // file. This setup feeds in to tracking the PC in the control frame, and how - // Ruby determines line numbers etc. in backtraces. - // - // However, there is a separate set of information for the actual location of - // the function associated with the iseq, rb_iseq_constant_body.location. Ruby - // consults this information for things like Method#source_location. This - // information has been setup by rb_iseq_new, but we need to fixup the first - // line of the function. - iseq->body->location.first_lineno = INT2FIX(startLine); - - // NOTE: positions is freed by rb_iseq_insns_info_encode_positions - unsigned int *positions = ALLOC_N(unsigned int, info->iseq_size); - - for (int i = 0; i < info->iseq_size; i++) { - positions[i] = i; - } - - iseq->body->insns_info.body = info->insns_info; - iseq->body->insns_info.positions = positions; - iseq->body->iseq_size = info->iseq_size; - iseq->body->insns_info.size = info->iseq_size; - rb_iseq_insns_info_encode_positions(iseq); - - iseq->body->iseq_encoded = info->iseq_encoded; - - // if this is a rescue frame, we need to set up some local storage for - // exception values ($!). - if (iseqType == ISEQ_TYPE_RESCUE || iseqType == ISEQ_TYPE_ENSURE) { - // this is an inlined version of `iseq_set_exception_local_table` from - // https://github.com/ruby/ruby/blob/a9a48e6a741f048766a2a287592098c4f6c7b7c7/compile.c#L1390-L1404 - ID id_dollar_bang; - ID *ids = (ID *)ALLOC_N(ID, 1); - - CONST_ID(id_dollar_bang, "#$!"); - iseq->body->local_table_size = 1; - ids[0] = id_dollar_bang; - iseq->body->local_table = ids; - } - - if ((iseqType == ISEQ_TYPE_METHOD || iseqType == ISEQ_TYPE_CLASS || iseqType == ISEQ_TYPE_TOP) && numLocals > 0) { - // this is a simplified version of `iseq_set_local_table` from - // https://github.com/ruby/ruby/blob/a9a48e6a741f048766a2a287592098c4f6c7b7c7/compile.c#L1767-L1789 - - // allocate space for the names of the locals - ID *ids = (ID *)ALLOC_N(ID, numLocals); - - memcpy(ids, locals, numLocals * sizeof(ID)); - - iseq->body->local_table = ids; - iseq->body->local_table_size = numLocals; - } - - iseq->body->stack_max = stackMax; - - return iseq; -} - -const VALUE sorbet_readRealpath() { - VALUE realpath = rb_gv_get("$__sorbet_ruby_realpath"); - if (!RB_TYPE_P(realpath, T_STRING)) { - rb_raise(rb_eRuntimeError, "Invalid '$__sorbet_ruby_realpath' when loading compiled module"); - } - - rb_gv_set("$__sorbet_ruby_realpath", RUBY_Qnil); - return realpath; -} - -// **** -// **** Name Based Intrinsics -// **** - -VALUE sorbet_vm_expandSplatIntrinsic(VALUE thing, VALUE before, VALUE after) { - const VALUE obj = thing; - const VALUE *elems; - long len; - bool have_array = RB_TYPE_P(thing, T_ARRAY); - /* Compare vm_insnhelper.c:vm_expandarray. We do things a little differently - * because we don't use the Ruby stack as scratch space and we're making an - * array nominally big enough to contain all the elements we need for a - * destructuring assignment. vm_expandarray is potentially called multiple - * times in such situations. - */ - if (!have_array && NIL_P(thing = rb_check_array_type(thing))) { - thing = obj; - elems = &thing; - len = 1; - } else { - elems = RARRAY_CONST_PTR_TRANSIENT(thing); - len = RARRAY_LEN(thing); - } - - long needed = FIX2LONG(before) + FIX2LONG(after); - long missing = needed - len; - if (missing <= 0) { - return have_array ? thing : rb_ary_new4(len, elems); - } - - RB_GC_GUARD(thing); - - VALUE arr = rb_ary_new4(len, elems); - for (long i = 0; i < missing; i++) { - rb_ary_push(arr, RUBY_Qnil); - } - return arr; -} - -// **** -// **** Symbol Intrinsics. See CallCMethod in SymbolIntrinsics.cc -// **** - -VALUE sorbet_enumerator_size_func_array_length(VALUE array, VALUE args, VALUE eobj) { - return RARRAY_LEN(array); -} - -extern VALUE rb_obj_as_string_result(VALUE, VALUE); - -extern VALUE rb_str_concat_literals(int, const VALUE *const restrict); - -VALUE sorbet_stringInterpolate(VALUE recv, ID fun, int argc, VALUE *argv, BlockFFIType blk, VALUE closure) { - for (int i = 0; i < argc; ++i) { - if (!RB_TYPE_P(argv[i], T_STRING)) { - VALUE str = rb_funcall(argv[i], idTo_s, 0); - argv[i] = rb_obj_as_string_result(str, argv[i]); - } - } - - return rb_str_concat_literals(argc, argv); -} - -// **** -// **** Exceptions -// **** - -// This is a function that can be used in place of any exception function, and does nothing except for return nil. -VALUE sorbet_blockReturnUndef(VALUE **pc, VALUE closure, rb_control_frame_t *cfp) { - return RUBY_Qundef; -} - -extern VALUE sorbet_getConstant(const char *path, long pathLen); - -#define MOD_CONST_GET(path) sorbet_getConstant(path, sizeof(path) - 1) - -static VALUE sigs_for_methods() { - static VALUE sigs; - if (UNLIKELY(sigs == 0)) { - sigs = rb_hash_new(); - rb_gc_register_address(&sigs); - } - return sigs; -} - -static VALUE sigs_for_self_methods() { - static VALUE sigs; - if (UNLIKELY(sigs == 0)) { - sigs = rb_hash_new(); - rb_gc_register_address(&sigs); - } - return sigs; -} - -// In Ruby terms, this is: -// sig{params(isSelf: T::Boolean, method: Symbol, self: Module, arg: T.nilable(Symbol), block: T.untyped).void} -void sorbet_vm_register_sig(VALUE isSelf, VALUE method, VALUE self, VALUE arg, rb_block_call_func_t block) { - VALUE methods = MOD_CONST_GET("T::Private::Methods"); - VALUE args[] = {self, arg}; - VALUE built_sig = rb_block_call(methods, rb_intern("_declare_sig"), 2, args, block, Qnil); - - // Store the sig someplace where we can get to it later. This is complicated, - // because we can have cases like: - // - // sig {...} - // def some_method(...) - // # sometime later - // sig {...} - // def some_method(...) - // - // and Sorbet will accept this (so long as the multiple definitions type-check). - // - // Nested method definitions are also problematic: Sorbet models the *sigs* - // of all such definitions as occuring at the top-level, even if those signatures - // are not applied until execution would reach the definition of the method, viz. - // - // sig {...} # sig 1 - // def some_method(...) - // sig {...} # sig 2 - // def some_internal_method(...) # internal - // # sometime later - // sig {...} # sig 3 - // def some_internal_method(...) # external - // - // is internally modeled as: - // - // Sorbet::Private::Static.sig {...} # sig 1 - // Sorbet::Private::Static.sig {...} # sig 2 - // Sorbet::Private::Static.sig {...} # sig 3 - // # ... - // def some_method(...) - // Sorbet::Private::Static.keep_def(:some_internal_method) - // end - // - // Sorbet::Private::Static.keep_def(:some_internal_method) - // - // In such a situation, we should actually apply sig 3 to the external definition - // and only use sig 2 for the internal definition, but it seems hard to come up - // with data structures that would reflect that situation. - // - // There are disabled testcases for situations like this, and multiple definitions - // generally, which the Sorbet compiler does not gracefully handle at the moment. - // - // For now, we're just going to say that for every (module, method_name) - // combination, there always exists a unique signature that applies to the single - // method defined as method_name. This is obviously not correct, given the above, - // but is good enough for our purposes. - VALUE sig_table = RTEST(isSelf) ? sigs_for_self_methods() : sigs_for_methods(); - VALUE mod_entry = rb_hash_lookup2(sig_table, self, Qundef); - if (mod_entry == Qundef) { - mod_entry = rb_hash_new(); - rb_hash_aset(sig_table, self, mod_entry); - } - - rb_hash_aset(mod_entry, method, built_sig); -} - -struct method_block_params { - VALUE klass; - const char *name; - // name as an ID. - ID id; - rb_sorbet_func_t methodPtr; - // rb_sorbet_param_t - void *paramp; - rb_iseq_t *iseq; - bool isSelf; -}; - -static VALUE define_method_block(RB_BLOCK_CALL_FUNC_ARGLIST(first_arg, data)) { - struct method_block_params *params = (struct method_block_params *)data; - if (params->isSelf) { - rb_define_singleton_sorbet_method(params->klass, params->name, params->methodPtr, params->paramp, params->iseq); - } else { - rb_add_method_sorbet(params->klass, params->id, params->methodPtr, params->paramp, METHOD_VISI_PUBLIC, - params->iseq); - } - return Qnil; -} - -void sorbet_vm_define_method(VALUE klass, const char *name, rb_sorbet_func_t methodPtr, void *paramp, rb_iseq_t *iseq, - bool isSelf) { - VALUE sig_table = isSelf ? sigs_for_self_methods() : sigs_for_methods(); - VALUE mod_entry = rb_hash_lookup2(sig_table, klass, Qnil); - VALUE built_sig = Qnil; - ID id = rb_intern(name); - if (mod_entry != Qnil) { - built_sig = rb_hash_delete(mod_entry, ID2SYM(id)); - } - - struct method_block_params params; - params.klass = klass; - params.name = name; - params.id = id; - params.methodPtr = methodPtr; - params.paramp = paramp; - params.iseq = iseq; - params.isSelf = isSelf; - - VALUE methods = MOD_CONST_GET("T::Private::Methods"); - VALUE args[] = {klass, built_sig}; - rb_block_call(methods, rb_intern("_with_declared_signature"), 2, args, define_method_block, (VALUE)¶ms); -} - -static VALUE sorbet_getTPropsDecorator() { - static const char decorator[] = "T::Props::Decorator"; - return sorbet_getConstant(decorator, sizeof(decorator)); -} - -/* See the patched proc.c */ -extern VALUE sorbet_vm_method_owner(VALUE obj); - -void sorbet_vm_define_prop_getter(VALUE klass, const char *name, rb_sorbet_func_t methodPtr, void *paramp, - rb_iseq_t *iseq) { - /* See T::Props::Decorator#define_getter_and_setter. */ - ID prop_get = rb_intern("prop_get"); - VALUE decorator = rb_funcall(klass, rb_intern("decorator"), 0); - VALUE prop_get_method = rb_obj_method(decorator, rb_id2sym(prop_get)); - VALUE method_owner = sorbet_vm_method_owner(prop_get_method); - /* The code that the compiler generated was fully general, accessing instance variables - * and going through any available prop_get_logic method. In the case where prop_get - * is known to be defined from T::Props::Decorator, we can use the attr_reader fastpath, - * which is significantly faster. - */ - if (method_owner == sorbet_getTPropsDecorator()) { - ID method_name = rb_intern(name); - ID attriv = rb_intern_str(rb_sprintf("@%" PRIsVALUE, rb_id2str(method_name))); - rb_add_method(klass, method_name, VM_METHOD_TYPE_IVAR, (void *)attriv, METHOD_VISI_PUBLIC); - return; - } - - const bool isSelf = false; - sorbet_vm_define_method(klass, name, methodPtr, paramp, iseq, isSelf); -} - -// The layout of these structs is known to the compiler. -struct IDDescriptor { - unsigned int offset; - unsigned int length; -}; - -struct RubyStringDescriptor { - unsigned int offset; - unsigned int length; -}; - -void sorbet_vm_intern_ids(ID *idTable, struct IDDescriptor *idDescriptors, size_t numIDs, const char *stringTable) { - for (size_t i = 0; i < numIDs; ++i) { - const struct IDDescriptor *desc = &idDescriptors[i]; - idTable[i] = rb_intern2(&stringTable[desc->offset], desc->length); - } -} - -extern VALUE sorbet_vm_fstring_new(const char *ptr, long len); -extern void rb_gc_register_address(VALUE *addr); - -void sorbet_vm_init_string_table(VALUE *rubyStringTable, struct RubyStringDescriptor *descriptors, - size_t numRubyStrings, const char *stringTable) { - for (size_t i = 0; i < numRubyStrings; ++i) { - const struct RubyStringDescriptor *desc = &descriptors[i]; - rubyStringTable[i] = sorbet_vm_fstring_new(&stringTable[desc->offset], desc->length); - // TODO(froydnj) This is going to allocate a separate linked list node for - // every literal string in the program, which seems suboptimal. We could - // investigate a internal-only ruby object that knew how to "mark" itself - // and marked every address in here during a GC? Or add a special - // rb_gc_register_address_range function? - rb_gc_register_address(&rubyStringTable[i]); - } -} diff --git a/compiler/IREmitter/RubyStackArgs.h b/compiler/IREmitter/RubyStackArgs.h deleted file mode 100644 index 389ae77303..0000000000 --- a/compiler/IREmitter/RubyStackArgs.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef SORBET_COMPILER_RUBYSTACKARGS_H -#define SORBET_COMPILER_RUBYSTACKARGS_H - -#include "compiler/Core/ForwardDeclarations.h" -#include "compiler/IREmitter/CallCacheFlags.h" - -#include -#include - -namespace sorbet::compiler { -class CompilerState; - -struct RubyStackArgs { - RubyStackArgs(std::vector stack, std::vector keywords, CallCacheFlags flags) - : stack{std::move(stack)}, keywords{std::move(keywords)}, flags(flags) {} - - std::vector stack; - std::vector keywords; - CallCacheFlags flags; -}; - -} // namespace sorbet::compiler - -#endif diff --git a/compiler/IREmitter/SymbolBasedIntrinsicMethod.h b/compiler/IREmitter/SymbolBasedIntrinsicMethod.h deleted file mode 100644 index 9e1bfd982a..0000000000 --- a/compiler/IREmitter/SymbolBasedIntrinsicMethod.h +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef SORBET_COMPILER_LLVMIREMITTER_SYMINTRINSICS_H -#define SORBET_COMPILER_LLVMIREMITTER_SYMINTRINSICS_H - -#include "IREmitterHelpers.h" -#include -#include - -namespace sorbet::compiler { - -struct IREmitterContext; - -class SymbolBasedIntrinsicMethod { -public: - const Intrinsics::HandleBlock blockHandled; - virtual llvm::Value *makeCall(MethodCallContext &mcctx) const = 0; - virtual InlinedVector applicableClasses(const core::GlobalState &gs) const = 0; - virtual InlinedVector applicableMethods(const core::GlobalState &gs) const = 0; - - // Before emitting a call to a symbol-based intrinsic, the compiled code will emit a runtime - // type test to ensure that the receiver actually has the expected type at runtime (i.e., the - // type in the CFG could be wrong). - // - // Sometimes we need to explicitly opt out of that type test, because it doesn't make sense, and - // we completely trust the type of the receiver (e.g., because it's controlled exclusively by - // Sorbet, not the user). - // - // When using this, you must be very careful to handle cases where the runtime type information - // doesn't match up with the static type information, or ensure that it is impossible for that - // information to mismatch. This most commonly means falling back to dispatch the method via the - // Ruby VM. - virtual llvm::Value *receiverFastPathTest(MethodCallContext &mcctx, core::ClassOrModuleRef potentialClass) const; - - // The above is a runtime test, since it returns an llvm::Value (which might be `true`). - // This method is a compile test for instances where we statically know the runtime - // test would succeed. This might be done for intrinsics which we know don't have - // a slow path through the VM (e.g. calls on Sorbet::Private::Static) or where having - // a slow path through the VM would hinder optimizations like having an assumption - // about the return type of the method. - // - // You should not typically be returning true from this. - virtual bool skipFastPathTest(MethodCallContext &mcctx, core::ClassOrModuleRef potentialClass) const; - - // Returns whether we should be calling Payload::afterIntrinsic to handle any - // post-intrinsic processing (e.g. checking whether VM-level interrupts have arrived). - // The default is to call. - // - // You should not typically be returning false from this. - virtual bool needsAfterIntrinsicProcessing() const; - - SymbolBasedIntrinsicMethod(Intrinsics::HandleBlock blockHandled) : blockHandled(blockHandled){}; - virtual ~SymbolBasedIntrinsicMethod() = default; - static std::vector &definedIntrinsics(const core::GlobalState &gs); - - virtual void sanityCheck(const core::GlobalState &gs) const; -}; -}; // namespace sorbet::compiler -#endif diff --git a/compiler/IREmitter/SymbolBasedIntrinsics.cc b/compiler/IREmitter/SymbolBasedIntrinsics.cc deleted file mode 100644 index fdc99f7af6..0000000000 --- a/compiler/IREmitter/SymbolBasedIntrinsics.cc +++ /dev/null @@ -1,1076 +0,0 @@ -// These violate our poisons so have to happen first -#include "llvm/IR/Attributes.h" -#include "llvm/IR/DerivedTypes.h" // FunctionType, StructType -#include "llvm/IR/IRBuilder.h" -#include "llvm/IR/Verifier.h" - -#include "absl/base/casts.h" -#include "ast/Helpers.h" -#include "ast/ast.h" -#include "cfg/CFG.h" -#include "common/FileOps.h" -#include "common/sort/sort.h" -#include "compiler/Core/CompilerState.h" -#include "compiler/Core/FailCompilation.h" -#include "compiler/Errors/Errors.h" -#include "compiler/IREmitter/IREmitter.h" -#include "compiler/IREmitter/IREmitterContext.h" -#include "compiler/IREmitter/IREmitterHelpers.h" -#include "compiler/IREmitter/MethodCallContext.h" -#include "compiler/IREmitter/Payload.h" -#include "compiler/IREmitter/SymbolBasedIntrinsicMethod.h" -#include -#include - -using namespace std; -namespace sorbet::compiler { -namespace { -core::ClassOrModuleRef typeToSym(const core::GlobalState &gs, core::TypePtr typ) { - core::ClassOrModuleRef sym; - if (core::isa_type(typ)) { - sym = core::cast_type_nonnull(typ).symbol; - } else if (auto appliedType = core::cast_type(typ)) { - sym = appliedType->klass; - } else { - ENFORCE(false); - } - sym = IREmitterHelpers::fixupOwningSymbol(gs, sym).asClassOrModuleRef(); - return sym; -} - -class CMethod final { -public: - string cMethod; - core::ClassOrModuleRef resultType; - - CMethod(string cMethod, core::ClassOrModuleRef resultType = core::Symbols::noClassOrModule()) - : cMethod{cMethod}, resultType{resultType} {} - - llvm::Function *getFunction(CompilerState &cs) const { - return cs.module->getFunction(cMethod); - } - - void assertResultType(CompilerState &cs, llvm::IRBuilderBase &build, llvm::Value *res) const { - if (resultType.exists()) { - Payload::assumeType(cs, build, res, resultType); - } - } - - void sanityCheck(const core::GlobalState &gs, core::MethodRef primaryMethod) const { - if (resultType.exists()) { - auto intrinsicResultType = resultType.data(gs)->externalType(); - - // We can only reasonably add type assertions for methods that have signatures - ENFORCE(primaryMethod.data(gs)->hasSig()); - - // test all overloads to see if we can find a sig that produces this type - if (core::Types::isSubType(gs, intrinsicResultType, primaryMethod.data(gs)->resultType)) { - return; - } - - int i = 0; - auto methodName = primaryMethod.data(gs)->name; - auto current = primaryMethod; - while (current.data(gs)->flags.isOverloaded) { - i++; - auto overloadName = gs.lookupNameUnique(core::UniqueNameKind::Overload, methodName, i); - auto overload = primaryMethod.data(gs)->owner.data(gs)->findMethod(gs, overloadName); - ENFORCE(overload.exists()); - if (core::Types::isSubType(gs, intrinsicResultType, overload.data(gs)->resultType)) { - return; - } - - current = overload; - } - - ENFORCE(false, "The method `{}` (or an overload) does not return `{}`", primaryMethod.show(gs), - intrinsicResultType.show(gs)); - } - } -}; - -class CallCMethod : public SymbolBasedIntrinsicMethod { -protected: - core::ClassOrModuleRef rubyClass; - string_view rubyMethod; - CMethod cMethod; - optional cMethodWithBlock; - vector expectedRubyCFuncs; - -private: - // Generate a one-off function that looks like the following: - // - // > define VALUE @(VALUE %env) { - // > VALUE %res = call @sorbet_inlineIntrinsicEnv_apply(%env, @cMethod, %blkArg) - // > ret VALUE %res - // > } - llvm::Function *generateForwarder(MethodCallContext &mcctx) const { - auto &cs = mcctx.cs; - auto &builder = mcctx.builder; - - // function signature - auto linkage = llvm::Function::InternalLinkage; - llvm::Twine name{"forward_" + llvm::Twine{cMethodWithBlock->cMethod}}; - auto *fn = llvm::Function::Create(cs.getInlineForwarderType(), linkage, name, cs.module); - auto *env = fn->arg_begin(); - - // function body - auto *entry = llvm::BasicBlock::Create(cs, "entry", fn); - auto ip = builder.saveIP(); - builder.SetInsertPoint(entry); - auto *cfunc = cMethodWithBlock->getFunction(cs); - auto *blk = mcctx.blkAsFunction(); - ENFORCE(blk != nullptr); - ENFORCE(mcctx.blk.has_value()); - int blockRubyBlockId = *mcctx.blk; - const auto &argsFlags = mcctx.irctx.blockLinks[blockRubyBlockId]->argFlags; - int maxPositionalArgs = 0; - for (auto &flag : argsFlags) { - if (flag.isKeyword) { - continue; - } - if (flag.isRepeated) { - continue; - } - if (flag.isDefault) { - maxPositionalArgs += 1; - continue; - } - if (flag.isBlock) { - continue; - } - maxPositionalArgs += 1; - } - auto *result = - builder.CreateCall(cs.module->getFunction("sorbet_inlineIntrinsicEnv_apply"), - {env, cfunc, blk, IREmitterHelpers::buildS4(cs, maxPositionalArgs)}, "result"); - builder.CreateRet(result); - builder.restoreIP(ip); - - return fn; - } - -public: - CallCMethod(core::ClassOrModuleRef rubyClass, string_view rubyMethod, CMethod cMethod, - optional cMethodWithBlock = nullopt, vector expectedRubyCFuncs = {}) - : SymbolBasedIntrinsicMethod(cMethodWithBlock.has_value() ? Intrinsics::HandleBlock::Handled - : Intrinsics::HandleBlock::Unhandled), - rubyClass(rubyClass), rubyMethod(rubyMethod), cMethod(cMethod), cMethodWithBlock(cMethodWithBlock), - expectedRubyCFuncs(expectedRubyCFuncs){}; - - virtual llvm::Value *makeCall(MethodCallContext &mcctx) const override { - auto &cs = mcctx.cs; - auto &builder = mcctx.builder; - auto *send = mcctx.send; - - auto *recv = mcctx.varGetRecv(); - auto *id = Payload::idIntern(cs, builder, send->fun.shortName(cs)); - auto *offset = Payload::buildLocalsOffset(cs); - - // kwsplat is used by the vm only, and we don't use the vm's api for calling an intrinsic directly. - auto args = IREmitterHelpers::fillSendArgArray(mcctx); - - llvm::Value *res{nullptr}; - if (auto *blk = mcctx.blkAsFunction()) { - if (!cMethodWithBlock.has_value()) { - core::Loc loc{mcctx.cs.file, send->argLocs.back()}; - failCompilation(cs, loc, "Unable to handle a block with this intrinsic"); - } - auto *forwarder = generateForwarder(mcctx); - - auto blkId = mcctx.blk.value(); - - // NOTE: The ruby stack doesn't need to be managed here because the known c intrinsics don't expect to be - // called by the vm. - bool usesBreak = mcctx.irctx.blockUsesBreak[blkId]; - auto *blkIfunc = Payload::getOrBuildBlockIfunc(cs, builder, mcctx.irctx, blkId); - if (usesBreak) { - res = builder.CreateCall(cs.module->getFunction("sorbet_callIntrinsicInlineBlock"), - {forwarder, recv, id, args.argc, args.argv, blkIfunc, offset}, - "rawSendResultWithBlock"); - } else { - // Since the block doesn't use break we can make two optimizations: - // - // 1. Use the version of sorbet_callIntrinsicInlineBlock that doesn't use rb_iterate and will inline - // better - // 2. Emit a type assertion on the result of the function, as we know that there won't be non-local - // control flow based on the use of `break` that could change the type of the returned value - res = builder.CreateCall(cs.module->getFunction("sorbet_callIntrinsicInlineBlock_noBreak"), - {forwarder, recv, id, args.argc, args.argv, blkIfunc, offset}, - "rawSendResultWithBlock"); - cMethodWithBlock->assertResultType(cs, builder, res); - } - } else { - auto *blkPtr = llvm::ConstantPointerNull::get(cs.getRubyBlockFFIType()->getPointerTo()); - res = builder.CreateCall(cMethod.getFunction(cs), {recv, id, args.argc, args.argv, blkPtr, offset}, - "rawSendResult"); - cMethod.assertResultType(cs, builder, res); - } - - return res; - }; - - virtual InlinedVector applicableClasses(const core::GlobalState &gs) const override { - return {rubyClass}; - }; - virtual InlinedVector applicableMethods(const core::GlobalState &gs) const override { - return {gs.lookupNameUTF8(rubyMethod)}; - }; - virtual llvm::Value *receiverFastPathTest(MethodCallContext &mcctx, - core::ClassOrModuleRef potentialClass) const override { - if (!this->expectedRubyCFuncs.empty()) { - return IREmitterHelpers::receiverFastPathTestWithCache(mcctx, this->expectedRubyCFuncs, string(rubyMethod)); - } else { - return SymbolBasedIntrinsicMethod::receiverFastPathTest(mcctx, potentialClass); - } - } - - virtual void sanityCheck(const core::GlobalState &gs) const override { - CallCMethod::sanityCheckInternal(gs, this->rubyClass, *this); - } - -protected: - static void sanityCheckInternal(const core::GlobalState &gs, core::ClassOrModuleRef klass, - const CallCMethod &call) { - auto methodName = gs.lookupNameUTF8(call.rubyMethod); - ENFORCE(methodName.exists()); - - auto methodSym = klass.data(gs)->findMemberTransitive(gs, methodName); - ENFORCE(methodSym.exists()); - - auto primaryMethod = methodSym.asMethodRef(); - ENFORCE(primaryMethod.exists()); - - call.cMethod.sanityCheck(gs, primaryMethod); - if (call.cMethodWithBlock.has_value()) { - call.cMethodWithBlock->sanityCheck(gs, primaryMethod); - } - - // Determine if the primary, or any overload, accepts a block argument. - int i = 0; - bool acceptsBlock = false; - auto current = primaryMethod; - while (current.data(gs)->flags.isOverloaded) { - const auto &args = current.data(gs)->arguments; - if (!args.empty() && !args.back().isSyntheticBlockArgument()) { - acceptsBlock = true; - break; - } - - i++; - auto overloadName = gs.lookupNameUnique(core::UniqueNameKind::Overload, methodName, i); - auto overloadSym = primaryMethod.data(gs)->owner.data(gs)->findMember(gs, overloadName); - ENFORCE(overloadSym.exists()); - - current = overloadSym.asMethodRef(); - ENFORCE(current.exists()); - } - - ENFORCE(!acceptsBlock || call.cMethodWithBlock.has_value(), - "the intrinsic for `{}` needs to have a block variant added", primaryMethod.show(gs)); - } -}; - -void emitParamInitialization(CompilerState &cs, llvm::IRBuilderBase &builder, const IREmitterContext &irctx, - core::MethodRef funcSym, int rubyRegionId, llvm::Value *param) { - // Following the comment in vm_core.h: - // https://github.com/ruby/ruby/blob/344a824ef9d4b6152703d02d7ffa042abd4252c1/vm_core.h#L321-L342 - // Comment reproduced here to make things somewhat easier to follow. - - /* - * parameter information - * - * def m(a1, a2, ..., aM, # mandatory - * b1=(...), b2=(...), ..., bN=(...), # optional - * *c, # rest - * d1, d2, ..., dO, # post - * e1:(...), e2:(...), ..., eK:(...), # keyword - * **f, # keyword_rest - * &g) # block - * => - * - * lead_num = M - * opt_num = N - * rest_start = M+N - * post_start = M+N+(*1) - * post_num = O - * keyword_num = K - * block_start = M+N+(*1)+O+K - * keyword_bits = M+N+(*1)+O+K+(&1) - * size = M+N+O+(*1)+K+(&1)+(**1) // parameter size. - */ - - int leadNum = 0; // # of required arguments (M) - int optNum = 0; // # of optional arguments (N) - int restStart = 0; // M + N - int postStart = 0; // M + N + 1 - int postNum = 0; // # of required arguments after rest (O) - int kwNum = 0; // # of keyword argments (K) - int blockStart = 0; // M + N + 1 + O + K - int reqKwNum = 0; // # of required keyword arguments - bool hasRest = false; - bool hasPost = false; - bool hasKw = false; - bool hasKwRest = false; - bool hasBlock = false; - - InlinedVector nonKeywordArgInfo; - InlinedVector keywordArgInfo; - - int i = -1; - for (auto &argInfo : funcSym.data(cs)->arguments) { - ++i; - auto &flags = argInfo.flags; - if (flags.isBlock) { - if (argInfo.loc.exists()) { - hasBlock = true; - blockStart = nonKeywordArgInfo.size(); - nonKeywordArgInfo.emplace_back(&argInfo); - } - } else if (flags.isKeyword) { - if (flags.isRepeated) { - hasKwRest = true; - } else { - hasKw = true; - kwNum++; - - if (!flags.isDefault) { - reqKwNum++; - } - } - keywordArgInfo.emplace_back(&argInfo); - } else if (flags.isRepeated) { - hasRest = true; - restStart = i; - nonKeywordArgInfo.emplace_back(&argInfo); - } else if (flags.isDefault) { - optNum++; - nonKeywordArgInfo.emplace_back(&argInfo); - } else { - if (hasRest) { - // This is the first post-rest required argument we have seen. - if (postNum == 0) { - hasPost = true; - postStart = i; - } - postNum++; - } else { - leadNum++; - } - nonKeywordArgInfo.emplace_back(&argInfo); - } - } - - // Construct all the necessary LLVM values to make the call. We name them - // according to what arguments they correspond to on the C side. - - // Flags structure. - auto *has_lead = builder.getInt1(leadNum != 0); - auto *has_opt = builder.getInt1(optNum != 0); - auto *has_rest = builder.getInt1(hasRest); - auto *has_post = builder.getInt1(hasPost); - auto *has_kw = builder.getInt1(hasKw); - auto *has_kwrest = builder.getInt1(hasKwRest); - auto *has_block = builder.getInt1(hasBlock); - // TODO: Sorbet doesn't supply enough information to be able to track this correctly: - // it is not !(has_kw || has_kwrest) but whether **nil was supplied in the arglist - auto *accepts_no_kwarg = builder.getInt1(false); - - // Fields according to the diagram above. - auto *lead_num = IREmitterHelpers::buildS4(cs, leadNum); - auto *opt_num = IREmitterHelpers::buildS4(cs, optNum); - auto *rest_start = IREmitterHelpers::buildS4(cs, restStart); - auto *post_start = IREmitterHelpers::buildS4(cs, postStart); - auto *post_num = IREmitterHelpers::buildS4(cs, postNum); - auto *block_start = IREmitterHelpers::buildS4(cs, blockStart); - auto *size = IREmitterHelpers::buildU4(cs, leadNum + optNum + hasRest + postNum + hasBlock + kwNum + hasKwRest); - - builder.CreateCall(cs.getFunction("sorbet_setParamInfo"), - {param, has_lead, has_opt, has_rest, has_post, has_kw, has_kwrest, has_block, accepts_no_kwarg, - lead_num, opt_num, rest_start, post_start, post_num, block_start, size}); - - if (!nonKeywordArgInfo.empty()) { - // Create a table for all the positional argument IDs. - auto *table = builder.CreateAlloca(llvm::Type::getInt64Ty(cs), - IREmitterHelpers::buildS4(cs, nonKeywordArgInfo.size()), "positional_table"); - - int i = -1; - for (auto info : nonKeywordArgInfo) { - ++i; - auto *id = Payload::idIntern(cs, builder, info->argumentName(cs)); - builder.CreateStore(id, builder.CreateConstGEP1_32(table, i)); - } - - auto *tableSize = IREmitterHelpers::buildS4(cs, nonKeywordArgInfo.size()); - builder.CreateCall(cs.getFunction("sorbet_setupParamPositional"), {param, tableSize, table}); - } - - if (hasKw || hasKwRest) { - ENFORCE(kwNum > 0 || hasKwRest); - ENFORCE(reqKwNum <= kwNum); - ENFORCE(keywordArgInfo.size() == (kwNum + hasKwRest)); - - // Create a table for all the keyword argument IDs. - auto *table = builder.CreateAlloca(llvm::Type::getInt64Ty(cs), - IREmitterHelpers::buildS4(cs, keywordArgInfo.size()), "keyword_table"); - - int i = -1; - for (auto info : keywordArgInfo) { - ++i; - auto *id = Payload::idIntern(cs, builder, info->argumentName(cs)); - builder.CreateStore(id, builder.CreateConstGEP1_32(table, i)); - } - - auto *kw_num = IREmitterHelpers::buildS4(cs, kwNum); - auto *required_num = IREmitterHelpers::buildS4(cs, reqKwNum); - auto *tableSize = IREmitterHelpers::buildS4(cs, keywordArgInfo.size()); - - builder.CreateCall(cs.getFunction("sorbet_setupParamKeywords"), - {param, kw_num, required_num, tableSize, table}); - } -} - -// TODO(froydnj): we need to do something like this for blocks as well. -llvm::Value *buildParamInfo(CompilerState &cs, llvm::IRBuilderBase &builder, const IREmitterContext &irctx, - core::MethodRef funcSym, int rubyRegionId) { - auto *paramInfo = builder.CreateCall(cs.getFunction("sorbet_allocateParamInfo"), {}, "parameterInfo"); - - emitParamInitialization(cs, builder, irctx, funcSym, rubyRegionId, paramInfo); - - return paramInfo; -} - -class DefineMethodIntrinsic : public SymbolBasedIntrinsicMethod { -public: - DefineMethodIntrinsic() : SymbolBasedIntrinsicMethod(Intrinsics::HandleBlock::Unhandled){}; - virtual llvm::Value *makeCall(MethodCallContext &mcctx) const override { - auto &cs = mcctx.cs; - auto &builder = mcctx.builder; - auto *send = mcctx.send; - - bool isSelf = send->fun == core::Names::keepSelfDef(); - - ENFORCE(send->args.size() == 3, "Invariant established by rewriter/Flatten.cc"); - - // First arg: define method on what - auto ownerSym = typeToSym(cs, send->args[0].type); - llvm::Value *klass; - // If we're defining the method on `T.class_of(T.class_of(X))`, we need to - // programatically access the class, rather than letting getRubyConstant do - // that work for us. - if (ownerSym.data(cs)->isSingletonClass(cs)) { - auto attachedClass = ownerSym.data(cs)->attachedClass(cs); - ENFORCE(attachedClass.exists()); - if (attachedClass.data(cs)->isSingletonClass(cs)) { - klass = Payload::getRubyConstant(cs, attachedClass, builder); - klass = builder.CreateCall(cs.getFunction("sorbet_singleton_class"), {klass}, "singletonClass"); - } else { - klass = Payload::getRubyConstant(cs, ownerSym, builder); - } - } else { - klass = Payload::getRubyConstant(cs, ownerSym, builder); - } - - // Second arg: name of method to define - auto litName = core::cast_type_nonnull(send->args[1].type); - ENFORCE(litName.literalKind == core::NamedLiteralType::LiteralTypeKind::Symbol); - auto funcNameRef = litName.asName(); - auto name = Payload::toCString(cs, funcNameRef.show(cs), builder); - - // Third arg: method kind (normal, attr_reader, or genericPropGetter) - auto litMethodKind = core::cast_type_nonnull(send->args[2].type); - ENFORCE(litMethodKind.literalKind == core::NamedLiteralType::LiteralTypeKind::Symbol); - auto methodKind = litMethodKind.asName(); - - auto lookupSym = isSelf ? ownerSym : ownerSym.data(cs)->attachedClass(cs); - if (ownerSym == core::Symbols::Object() && !isSelf) { - // TODO Figure out if this speicial case is right - lookupSym = core::Symbols::Object(); - } - auto funcSym = lookupSym.data(cs)->findMethod(cs, funcNameRef); - ENFORCE(funcSym.exists()); - - // We are going to rely on compiled final methods having their return values checked. - const bool needsTypechecking = funcSym.data(cs)->flags.isFinal; - - if (methodKind == core::Names::attrReader() && !needsTypechecking) { - const char *payloadFuncName = isSelf ? "sorbet_defineIvarMethodSingleton" : "sorbet_defineIvarMethod"; - auto payloadFunc = cs.getFunction(payloadFuncName); - - builder.CreateCall(payloadFunc, {klass, name}); - } else { - const bool isPropGetter = methodKind == core::Names::genericPropGetter(); - - ENFORCE(methodKind == core::Names::normal() || isPropGetter || - (methodKind == core::Names::attrReader() && needsTypechecking), - "Unknown method kind: {}", methodKind.show(cs)); - - if (isPropGetter) { - ENFORCE(!isSelf); - } - auto funcHandle = IREmitterHelpers::getOrCreateFunction(cs, funcSym); - auto *stackFrameVar = Payload::rubyStackFrameVar(cs, builder, mcctx.irctx, funcSym); - auto *stackFrame = builder.CreateLoad(stackFrameVar, "stackFrame"); - - // If a prop getter doesn't necessarily need to be typechecked, then we can - // decide at runtime whether to use the fully-general compiled version of - // the getter or whether to use a fast attr_reader-based version. - const char *payloadFuncName = isSelf ? "sorbet_defineMethodSingleton" - : (isPropGetter && !needsTypechecking) ? "sorbet_definePropGetter" - : "sorbet_defineMethod"; - auto rubyFunc = cs.getFunction(payloadFuncName); - auto *paramInfo = buildParamInfo(cs, builder, mcctx.irctx, funcSym, mcctx.rubyRegionId); - builder.CreateCall(rubyFunc, {klass, name, funcHandle, paramInfo, stackFrame}); - - builder.CreateCall(IREmitterHelpers::getInitFunction(cs, funcSym), {}); - } - - // Return the symbol of the method name even if we don't emit a definition. This will be a problem if there are - // meta-progrmaming methods applied to an abstract method definition, see - // https://github.com/stripe/sorbet_llvm/issues/115 for more information. - return Payload::varGet(cs, send->args[1].variable, builder, mcctx.irctx, mcctx.rubyRegionId); - } - - virtual bool skipFastPathTest(MethodCallContext &mcctx, core::ClassOrModuleRef potentialClass) const override { - return true; - } - virtual bool needsAfterIntrinsicProcessing() const override { - return false; - } - virtual InlinedVector applicableClasses(const core::GlobalState &gs) const override { - return {core::Symbols::Sorbet_Private_Static().data(gs)->lookupSingletonClass(gs)}; - }; - virtual InlinedVector applicableMethods(const core::GlobalState &gs) const override { - return {core::Names::keepDef(), core::Names::keepSelfDef()}; - } -} DefineMethodIntrinsic; - -class SorbetPrivateStaticResolvedSigIntrinsic : public SymbolBasedIntrinsicMethod { -public: - SorbetPrivateStaticResolvedSigIntrinsic() : SymbolBasedIntrinsicMethod(Intrinsics::HandleBlock::Handled){}; - virtual llvm::Value *makeCall(MethodCallContext &mcctx) const override { - auto &cs = mcctx.cs; - auto &irctx = mcctx.irctx; - auto &builder = mcctx.builder; - auto *send = mcctx.send; - auto rubyRegionId = mcctx.rubyRegionId; - // args[0] = originalRecv - // args[1] = arg to sig, if present - // args[2] = isSelf - // args[3] = methodName - // args[4..] = originalArgs, if present - // - // Do nothing for non-self calls (e.g. T::Sig::WithoutRuntime.sig) - if (send->args[0].variable != cfg::LocalRef::selfVariable()) { - return Payload::rubyNil(mcctx.cs, builder); - } - - ENFORCE(mcctx.blkAsFunction() != nullptr); - llvm::Value *originalRecv = Payload::varGet(cs, send->args[0].variable, builder, irctx, rubyRegionId); - llvm::Value *sigArg; - cfg::VariableUseSite *remainingArgs; - if (send->args.size() > 3) { - sigArg = Payload::varGet(cs, send->args[1].variable, builder, irctx, rubyRegionId); - remainingArgs = &send->args[2]; - } else { - sigArg = Payload::rubyNil(cs, builder); - remainingArgs = &send->args[1]; - } - llvm::Value *isSelf = Payload::varGet(cs, remainingArgs[0].variable, builder, irctx, rubyRegionId); - llvm::Value *methodName = Payload::varGet(cs, remainingArgs[1].variable, builder, irctx, rubyRegionId); - builder.CreateCall(cs.getFunction("sorbet_vm_register_sig"), - {isSelf, methodName, originalRecv, sigArg, mcctx.blkAsFunction()}); - return Payload::rubyNil(mcctx.cs, builder); - } - - virtual bool skipFastPathTest(MethodCallContext &mcctx, core::ClassOrModuleRef potentialClass) const override { - return true; - } - virtual bool needsAfterIntrinsicProcessing() const override { - return false; - } - virtual InlinedVector applicableClasses(const core::GlobalState &gs) const override { - return {core::Symbols::Sorbet_Private_Static_ResolvedSig().data(gs)->lookupSingletonClass(gs)}; - }; - virtual InlinedVector applicableMethods(const core::GlobalState &gs) const override { - return {core::Names::sig()}; - } -} SorbetPrivateStaticResolvedSigIntrinsic; - -class SorbetPrivateStaticSigIntrinsic : public SymbolBasedIntrinsicMethod { -public: - SorbetPrivateStaticSigIntrinsic() : SymbolBasedIntrinsicMethod(Intrinsics::HandleBlock::Handled){}; - virtual llvm::Value *makeCall(MethodCallContext &mcctx) const override { - auto &builder = mcctx.builder; - return Payload::rubyNil(mcctx.cs, builder); - } - - virtual bool skipFastPathTest(MethodCallContext &mcctx, core::ClassOrModuleRef potentialClass) const override { - return true; - } - virtual bool needsAfterIntrinsicProcessing() const override { - return false; - } - virtual InlinedVector applicableClasses(const core::GlobalState &gs) const override { - return {core::Symbols::Sorbet_Private_Static().data(gs)->lookupSingletonClass(gs)}; - }; - virtual InlinedVector applicableMethods(const core::GlobalState &gs) const override { - return {core::Names::sig()}; - } -} SorbetPrivateStaticSigIntrinsic; - -/* Reuse logic from typeTest to speedup SomeClass === someVal */ -class Module_tripleEq : public SymbolBasedIntrinsicMethod { -public: - Module_tripleEq() : SymbolBasedIntrinsicMethod(Intrinsics::HandleBlock::Unhandled) {} - virtual llvm::Value *makeCall(MethodCallContext &mcctx) const override { - auto &cs = mcctx.cs; - auto *send = mcctx.send; - auto representedClass = core::Types::getRepresentedClass(cs, send->recv.type); - if (!representedClass.exists()) { - return IREmitterHelpers::emitMethodCallViaRubyVM(mcctx); - } - auto recvType = representedClass.data(cs)->externalType(); - auto &arg0 = send->args[0]; - - auto &builder = mcctx.builder; - - auto recvValue = mcctx.varGetRecv(); - auto representedClassValue = Payload::getRubyConstant(cs, representedClass, builder); - auto classEq = builder.CreateICmpEQ(recvValue, representedClassValue, "Module_tripleEq_shortCircuit"); - - auto fastStart = llvm::BasicBlock::Create(cs, "Module_tripleEq_fast", builder.GetInsertBlock()->getParent()); - auto slowStart = llvm::BasicBlock::Create(cs, "Module_tripleEq_slow", builder.GetInsertBlock()->getParent()); - auto cont = llvm::BasicBlock::Create(cs, "Module_tripleEq_cont", builder.GetInsertBlock()->getParent()); - - auto expected = Payload::setExpectedBool(cs, builder, classEq, true); - builder.CreateCondBr(expected, fastStart, slowStart); - - builder.SetInsertPoint(fastStart); - auto arg0Value = Payload::varGet(cs, arg0.variable, builder, mcctx.irctx, mcctx.rubyRegionId); - auto typeTest = Payload::typeTest(cs, builder, arg0Value, recvType); - auto fastPath = Payload::boolToRuby(cs, builder, typeTest); - auto fastEnd = builder.GetInsertBlock(); - builder.CreateBr(cont); - - builder.SetInsertPoint(slowStart); - auto slowPath = IREmitterHelpers::emitMethodCallViaRubyVM(mcctx); - auto slowEnd = builder.GetInsertBlock(); - builder.CreateBr(cont); - - builder.SetInsertPoint(cont); - auto incomingEdges = 2; - auto phi = builder.CreatePHI(builder.getInt64Ty(), incomingEdges, "Module_tripleEq_result"); - phi->addIncoming(fastPath, fastEnd); - phi->addIncoming(slowPath, slowEnd); - - return phi; - }; - - virtual InlinedVector applicableClasses(const core::GlobalState &gs) const override { - return {core::Symbols::Module()}; - }; - virtual InlinedVector applicableMethods(const core::GlobalState &gs) const override { - return {core::Names::tripleEq()}; - }; -} Module_tripleEq; - -class Regexp_new : public SymbolBasedIntrinsicMethod { -public: - Regexp_new() : SymbolBasedIntrinsicMethod(Intrinsics::HandleBlock::Unhandled) {} - virtual llvm::Value *makeCall(MethodCallContext &mcctx) const override { - auto &cs = mcctx.cs; - auto *send = mcctx.send; - if (send->args.size() < 1 || send->args.size() > 2) { - // todo: make this work with options. - return IREmitterHelpers::emitMethodCallViaRubyVM(mcctx); - } - auto options = 0; - if (send->args.size() == 2) { - auto &arg1 = send->args[1]; - if (!core::isa_type(arg1.type)) { - return IREmitterHelpers::emitMethodCallViaRubyVM(mcctx); - } - const auto &literalOptions = core::cast_type_nonnull(arg1.type); - options = literalOptions.value; - } - - auto &arg0 = send->args[0]; - if (!core::isa_type(arg0.type)) { - return IREmitterHelpers::emitMethodCallViaRubyVM(mcctx); - } - - auto literal = core::cast_type_nonnull(arg0.type); - if (literal.literalKind != core::NamedLiteralType::LiteralTypeKind::String) { - return IREmitterHelpers::emitMethodCallViaRubyVM(mcctx); - } - auto &builder = mcctx.builder; - auto str = literal.asName().shortName(cs); - return Payload::cPtrToRubyRegexp(cs, builder, str, options); - }; - - virtual InlinedVector applicableClasses(const core::GlobalState &gs) const override { - return {core::Symbols::Regexp().data(gs)->lookupSingletonClass(gs)}; - }; - virtual InlinedVector applicableMethods(const core::GlobalState &gs) const override { - return {core::Names::new_()}; - }; -} Regexp_new; - -class TEnum_new : public SymbolBasedIntrinsicMethod { -public: - TEnum_new() : SymbolBasedIntrinsicMethod(Intrinsics::HandleBlock::Unhandled) {} - virtual llvm::Value *makeCall(MethodCallContext &mcctx) const override { - auto &cs = mcctx.cs; - auto *send = mcctx.send; - // Instead of `MyEnum::X$1.new(...)`, we want to do `.new(...)` to get back to what - // would have happened at runtime. This is effecctively "undo-ing" the earlier DSL pass. - auto appliedType = core::cast_type(send->recv.type); - if (appliedType == nullptr) { - return IREmitterHelpers::emitMethodCallViaRubyVM(mcctx); - } - - auto attachedClass = appliedType->klass.data(cs)->attachedClass(cs); - ENFORCE(attachedClass.exists()); - if (!attachedClass.data(cs)->name.isTEnumName(cs)) { - return IREmitterHelpers::emitMethodCallViaRubyVM(mcctx); - } - - auto *blockHandler = Payload::vmBlockHandlerNone(cs, mcctx.builder); - - auto recv = cfg::LocalRef::selfVariable(); - auto [stack, keywords, flags] = IREmitterHelpers::buildSendArgs(mcctx, recv, 0); - auto &irctx = mcctx.irctx; - auto &builder = mcctx.builder; - auto rubyRegionId = mcctx.rubyRegionId; - auto *cfp = Payload::getCFPForBlock(cs, builder, irctx, rubyRegionId); - Payload::pushRubyStackVector(cs, builder, cfp, Payload::varGet(cs, recv, builder, irctx, rubyRegionId), stack); - auto *cache = IREmitterHelpers::makeInlineCache(cs, builder, "new", flags, stack.size(), keywords); - return Payload::callFuncWithCache(cs, builder, cache, blockHandler); - }; - - virtual InlinedVector applicableClasses(const core::GlobalState &gs) const override { - return {core::Symbols::T_Enum().data(gs)->lookupSingletonClass(gs)}; - }; - virtual InlinedVector applicableMethods(const core::GlobalState &gs) const override { - return {core::Names::new_()}; - }; - virtual llvm::Value *receiverFastPathTest(MethodCallContext &mcctx, core::ClassOrModuleRef c) const override { - return mcctx.builder.getInt1(true); - }; -} TEnum_new; - -class TEnum_abstract : public SymbolBasedIntrinsicMethod { -public: - TEnum_abstract() : SymbolBasedIntrinsicMethod(Intrinsics::HandleBlock::Unhandled) {} - virtual llvm::Value *makeCall(MethodCallContext &mcctx) const override { - auto &builder = mcctx.builder; - return Payload::rubyNil(mcctx.cs, builder); - }; - - virtual InlinedVector applicableClasses(const core::GlobalState &gs) const override { - return {core::Symbols::T_Enum().data(gs)->lookupSingletonClass(gs)}; - }; - virtual InlinedVector applicableMethods(const core::GlobalState &gs) const override { - return {core::Names::declareAbstract()}; - }; -} TEnum_abstract; - -class TPrivateCompiler_runningCompiled_p : public SymbolBasedIntrinsicMethod { -public: - TPrivateCompiler_runningCompiled_p() : SymbolBasedIntrinsicMethod(Intrinsics::HandleBlock::Unhandled) {} - virtual llvm::Value *makeCall(MethodCallContext &mcctx) const override { - auto &builder = mcctx.builder; - return Payload::rubyTrue(mcctx.cs, builder); - }; - - virtual InlinedVector applicableClasses(const core::GlobalState &gs) const override { - return {core::Symbols::T_Private_CompilerSingleton()}; - }; - virtual InlinedVector applicableMethods(const core::GlobalState &gs) const override { - return {core::Names::runningCompiled_p()}; - }; -} TPrivateCompiler_runningCompiled_p; - -class TPrivateCompiler_compilerVersion : public SymbolBasedIntrinsicMethod { -public: - TPrivateCompiler_compilerVersion() : SymbolBasedIntrinsicMethod(Intrinsics::HandleBlock::Unhandled) {} - virtual llvm::Value *makeCall(MethodCallContext &mcctx) const override { - auto &builder = mcctx.builder; - auto frozen = true; - return Payload::cPtrToRubyString(mcctx.cs, builder, sorbet_full_version_string, frozen); - }; - - virtual InlinedVector applicableClasses(const core::GlobalState &gs) const override { - return {core::Symbols::T_Private_CompilerSingleton()}; - }; - virtual InlinedVector applicableMethods(const core::GlobalState &gs) const override { - return {core::Names::compilerVersion()}; - }; -} TPrivateCompiler_compilerVersion; - -class Thread_squareBrackets : public CallCMethod { -public: - // This sorbet_Thread_square_br is a slower version that will do arity checking and convert the - // argument to a symbol. If our `makeCall` fast path doesn't apply, we'll fall back to calling - // the slow version (via super class `makeCall`). - Thread_squareBrackets() : CallCMethod(core::Symbols::Thread(), "[]"sv, CMethod{"sorbet_Thread_square_br"}) {} - - virtual llvm::Value *makeCall(MethodCallContext &mcctx) const override { - auto &cs = mcctx.cs; - auto &builder = mcctx.builder; - auto &irctx = mcctx.irctx; - auto *send = mcctx.send; - - if (send->args.size() != 1 || send->numPosArgs != 1) { - return CallCMethod::makeCall(mcctx); - } - - auto &arg = send->args[0]; - auto symit = irctx.symbols.find(arg.variable); - if (symit == irctx.symbols.end()) { - return CallCMethod::makeCall(mcctx); - } - - auto *recv = mcctx.varGetRecv(); - auto *id = Payload::idIntern(cs, builder, symit->second); - return builder.CreateCall(cs.getFunction("sorbet_Thread_square_br_symarg"), {recv, id}); - } -} Thread_squareBrackets; - -class Thread_squareBracketsEq : public CallCMethod { -public: - // This sorbet_Thread_square_br_eq is a slower version that will do arity checking and convert the - // argument to a symbol. If our `makeCall` fast path doesn't apply, we'll fall back to calling - // the slow version (via super class). - Thread_squareBracketsEq() : CallCMethod(core::Symbols::Thread(), "[]="sv, CMethod{"sorbet_Thread_square_br_eq"}) {} - - virtual llvm::Value *makeCall(MethodCallContext &mcctx) const override { - auto &cs = mcctx.cs; - auto &builder = mcctx.builder; - auto &irctx = mcctx.irctx; - auto *send = mcctx.send; - - if (send->args.size() != 2 || send->numPosArgs != 2) { - return CallCMethod::makeCall(mcctx); - } - - auto &arg = send->args[0]; - auto symit = irctx.symbols.find(arg.variable); - if (symit == irctx.symbols.end()) { - return CallCMethod::makeCall(mcctx); - } - - auto *recv = mcctx.varGetRecv(); - auto *id = Payload::idIntern(cs, builder, symit->second); - return builder.CreateCall( - cs.getFunction("sorbet_Thread_square_br_eq_symarg"), - {recv, id, Payload::varGet(cs, send->args[1].variable, builder, irctx, mcctx.rubyRegionId)}); - } -} Thread_squareBracketsEq; - -class CallCMethodSingleton : public CallCMethod { -public: - CallCMethodSingleton(core::ClassOrModuleRef rubyClass, string_view rubyMethod, CMethod cMethod) - : CallCMethod(rubyClass, rubyMethod, cMethod){}; - - CallCMethodSingleton(core::ClassOrModuleRef rubyClass, string_view rubyMethod, CMethod cMethod, - string cMethodWithBlock) - : CallCMethod(rubyClass, rubyMethod, cMethod, cMethodWithBlock){}; - - // It is safe to skip the test if the receiver is a constant of the given class. - virtual bool skipFastPathTest(MethodCallContext &mcctx, core::ClassOrModuleRef potentialClass) const override { - auto &cs = mcctx.cs; - auto &irctx = mcctx.irctx; - auto &recv = mcctx.send->recv; - - return IREmitterHelpers::isAliasToSingleton(cs, irctx, recv.variable, potentialClass); - } - virtual InlinedVector applicableClasses(const core::GlobalState &gs) const override { - return {rubyClass.data(gs)->lookupSingletonClass(gs)}; - }; - - virtual void sanityCheck(const core::GlobalState &gs) const override { - auto singletonClass = this->rubyClass.data(gs)->lookupSingletonClass(gs); - CallCMethodSingleton::sanityCheckInternal(gs, singletonClass, *this); - } -}; - -static const vector knownCMethodsInstance{ - {core::Symbols::Array(), "[]", CMethod{"sorbet_rb_array_square_br"}}, - {core::Symbols::Array(), "[]=", CMethod{"sorbet_rb_array_square_br_eq"}}, - {core::Symbols::Array(), "empty?", CMethod{"sorbet_rb_array_empty"}}, - {core::Symbols::Array(), "each", CMethod{"sorbet_rb_array_each", core::Symbols::Enumerator()}, - CMethod{"sorbet_rb_array_each_withBlock", core::Symbols::Array()}}, - {core::Symbols::Array(), "each_with_object", - CMethod{"sorbet_rb_array_each_with_object", core::Symbols::Enumerator()}, - CMethod{"sorbet_rb_array_each_with_object_withBlock"}}, - {core::Symbols::Array(), "select", CMethod{"sorbet_rb_array_select", core::Symbols::Enumerator()}, - CMethod{"sorbet_rb_array_select_withBlock", core::Symbols::Array()}}, - // filter is an alias for select, so we call the same intrinsic - {core::Symbols::Array(), "filter", CMethod{"sorbet_rb_array_select", core::Symbols::Enumerator()}, - CMethod{"sorbet_rb_array_select_withBlock", core::Symbols::Array()}}, - {core::Symbols::Array(), "reject", CMethod{"sorbet_rb_array_reject", core::Symbols::Enumerator()}, - CMethod{"sorbet_rb_array_reject_withBlock", core::Symbols::Array()}}, - {core::Symbols::Array(), "reject!", CMethod{"sorbet_rb_array_reject_bang", core::Symbols::Enumerator()}, - CMethod{"sorbet_rb_array_reject_bang_withBlock", core::Symbols::Array()}}, - {core::Symbols::Array(), "find", CMethod{"sorbet_rb_array_find"}, CMethod{"sorbet_rb_array_find_withBlock"}}, - {core::Symbols::Array(), "collect", CMethod{"sorbet_rb_array_collect", core::Symbols::Enumerator()}, - CMethod{"sorbet_rb_array_collect_withBlock", core::Symbols::Array()}}, - // Ruby implements map and collect with the same function (named with "collect" in its name). - // We do the same for consistency. - {core::Symbols::Array(), "map", CMethod{"sorbet_rb_array_collect", core::Symbols::Enumerator()}, - CMethod{"sorbet_rb_array_collect_withBlock", core::Symbols::Array()}}, - {core::Symbols::Array(), "collect!", CMethod{"sorbet_rb_array_collect_bang", core::Symbols::Enumerator()}, - CMethod{"sorbet_rb_array_collect_bang_withBlock", core::Symbols::Array()}}, - {core::Symbols::Array(), "map!", CMethod{"sorbet_rb_array_collect_bang", core::Symbols::Enumerator()}, - CMethod{"sorbet_rb_array_collect_bang_withBlock", core::Symbols::Array()}}, - {core::Symbols::Array(), "any?", CMethod{"sorbet_rb_array_any"}, CMethod{"sorbet_rb_array_any_withBlock"}}, - {core::Symbols::Array(), "all?", CMethod{"sorbet_rb_array_all"}, CMethod{"sorbet_rb_array_all_withBlock"}}, - {core::Symbols::Array(), "compact", CMethod{"sorbet_rb_array_compact", core::Symbols::Array()}}, - {core::Symbols::Array(), "compact!", CMethod{"sorbet_rb_array_compact_bang"}}, - {core::Symbols::Array(), "to_ary", CMethod{"sorbet_returnRecv", core::Symbols::Array()}}, - {core::Symbols::Array(), "to_h", CMethod{"sorbet_rb_array_to_h", core::Symbols::Hash()}}, - {core::Symbols::Array(), "size", CMethod{"sorbet_rb_array_len", core::Symbols::Integer()}}, - {core::Symbols::Array(), "length", CMethod{"sorbet_rb_array_len", core::Symbols::Integer()}}, - {core::Symbols::Array(), - "to_a", - CMethod{"sorbet_int_ary_to_a", core::Symbols::Array()}, - std::nullopt, - {KnownFunction::cached("sorbet_rb_ary_to_a_func")}}, - {core::Symbols::Array(), "uniq", CMethod("sorbet_rb_array_uniq", core::Symbols::Array()), - CMethod("sorbet_rb_array_uniq_withBlock", core::Symbols::Array())}, - {core::Symbols::Hash(), "[]", CMethod{"sorbet_rb_hash_square_br"}}, - {core::Symbols::Hash(), "[]=", CMethod{"sorbet_rb_hash_square_br_eq"}}, - {core::Symbols::Hash(), "each_pair", CMethod{"sorbet_rb_hash_each_pair", core::Symbols::Enumerator()}, - CMethod{"sorbet_rb_hash_each_pair_withBlock", core::Symbols::Hash()}}, - {core::Symbols::Hash(), "each", CMethod{"sorbet_rb_hash_each_pair", core::Symbols::Enumerator()}, - CMethod{"sorbet_rb_hash_each_pair_withBlock", core::Symbols::Hash()}}, - {core::Symbols::Hash(), "each_with_object", CMethod{"sorbet_rb_hash_each_with_object", core::Symbols::Enumerator()}, - CMethod{"sorbet_rb_hash_each_with_object_withBlock"}}, - {core::Symbols::Hash(), "any?", CMethod{"sorbet_rb_hash_any"}, CMethod{"sorbet_rb_hash_any_withBlock"}}, - {core::Symbols::Hash(), - "keys", - CMethod{"sorbet_rb_hash_keys", core::Symbols::Array()}, - nullopt, - {KnownFunction("rb_hash_keys")}}, - {core::Symbols::Hash(), - "values", - CMethod{"sorbet_rb_hash_values", core::Symbols::Array()}, - nullopt, - {KnownFunction("rb_hash_values")}}, - {core::Symbols::Hash(), - "to_h", - CMethod{"sorbet_int_hash_to_h", core::Symbols::Hash()}, - CMethod{"sorbet_int_hash_to_h_withBlock", core::Symbols::Hash()}, - {KnownFunction::cached("sorbet_rb_hash_to_h_func")}}, - {core::Symbols::Hash(), "to_hash", CMethod{"sorbet_returnRecv", core::Symbols::Hash()}}, - {core::Symbols::Hash(), "fetch", CMethod{"sorbet_rb_hash_fetch_m"}, CMethod{"sorbet_rb_hash_fetch_m_withBlock"}}, - {core::Symbols::Hash(), "merge", CMethod{"sorbet_rb_hash_merge", core::Symbols::Hash()}, - CMethod{"sorbet_rb_hash_merge_withBlock", core::Symbols::Hash()}}, - {core::Symbols::Hash(), "merge!", CMethod{"sorbet_rb_hash_update", core::Symbols::Hash()}, - CMethod{"sorbet_rb_hash_update_withBlock", core::Symbols::Hash()}}, - {core::Symbols::Hash(), "update", CMethod{"sorbet_rb_hash_update", core::Symbols::Hash()}, - CMethod{"sorbet_rb_hash_update_withBlock", core::Symbols::Hash()}}, - {core::Symbols::Hash(), "select", CMethod{"sorbet_rb_hash_select", core::Symbols::Enumerator()}, - CMethod{"sorbet_rb_hash_select_withBlock", core::Symbols::Hash()}}, - {core::Symbols::Hash(), "delete", CMethod{"sorbet_rb_hash_delete_m"}, CMethod{"sorbet_rb_hash_delete_m_withBlock"}}, - {core::Symbols::Hash(), "empty?", CMethod{"sorbet_rb_hash_empty_p"}}, - {core::Symbols::Hash(), "transform_values", CMethod{"sorbet_rb_hash_transform_values", core::Symbols::Hash()}, - CMethod{"sorbet_rb_hash_transform_values_withBlock", core::Symbols::Hash()}}, - {core::Symbols::TrueClass(), "|", CMethod{"sorbet_int_bool_true"}}, - {core::Symbols::FalseClass(), "|", CMethod{"sorbet_int_bool_and"}}, - {core::Symbols::TrueClass(), "&", CMethod{"sorbet_int_bool_and"}}, - {core::Symbols::FalseClass(), "&", CMethod{"sorbet_int_bool_false"}}, - {core::Symbols::TrueClass(), "^", CMethod{"sorbet_int_bool_nand"}}, - {core::Symbols::FalseClass(), "^", CMethod{"sorbet_int_bool_and"}}, - {core::Symbols::Integer(), "+", CMethod{"sorbet_rb_int_plus"}}, - {core::Symbols::Integer(), "-", CMethod{"sorbet_rb_int_minus"}}, - {core::Symbols::Integer(), "*", CMethod{"sorbet_rb_int_mul"}}, - {core::Symbols::Integer(), "/", CMethod{"sorbet_rb_int_div"}}, - {core::Symbols::Integer(), ">", CMethod{"sorbet_rb_int_gt"}}, - {core::Symbols::Integer(), "<", CMethod{"sorbet_rb_int_lt"}}, - {core::Symbols::Integer(), ">=", CMethod{"sorbet_rb_int_ge"}}, - {core::Symbols::Integer(), "<=", CMethod{"sorbet_rb_int_le"}}, - {core::Symbols::Integer(), "to_i", CMethod{"sorbet_returnRecv", core::Symbols::Integer()}}, - {core::Symbols::Integer(), "to_int", CMethod{"sorbet_returnRecv", core::Symbols::Integer()}}, - {core::Symbols::Integer(), "to_s", CMethod{"sorbet_rb_int_to_s", core::Symbols::String()}}, - {core::Symbols::Integer(), "==", CMethod{"sorbet_rb_int_equal"}}, - {core::Symbols::Integer(), "!=", CMethod{"sorbet_rb_int_neq"}}, - {core::Symbols::Integer(), "times", CMethod{"sorbet_rb_int_dotimes", core::Symbols::Enumerator()}, - CMethod{"sorbet_rb_int_dotimes_withBlock", core::Symbols::Integer()}}, - {core::Symbols::String(), "+@", CMethod{"sorbet_int_str_uplus", core::Symbols::String()}}, - {core::Symbols::String(), - "to_s", - CMethod{"sorbet_int_str_to_s", core::Symbols::String()}, - std::nullopt, - {KnownFunction::cached("sorbet_rb_str_to_s_func")}}, - {core::Symbols::Symbol(), "==", CMethod{"sorbet_rb_sym_equal"}}, - {core::Symbols::Symbol(), "===", CMethod{"sorbet_rb_sym_equal"}}, - {core::Symbols::Symbol(), "to_sym", CMethod{"sorbet_returnRecv"}}, -#include "WrappedIntrinsics.h" -}; - -static const vector knownCMethodsSingleton{ - {core::Symbols::T(), "unsafe", CMethod{"sorbet_T_unsafe"}}, - {core::Symbols::T(), "must", CMethod{"sorbet_T_must"}}, - {core::Symbols::Thread(), "current", CMethod{"sorbet_Thread_current", core::Symbols::Thread()}}, - {core::Symbols::Thread(), "main", CMethod{"sorbet_Thread_main", core::Symbols::Thread()}}, -}; - -vector getKnownCMethodPtrs(const core::GlobalState &gs) { - vector res{ - &DefineMethodIntrinsic, - &SorbetPrivateStaticResolvedSigIntrinsic, - &SorbetPrivateStaticSigIntrinsic, - &Module_tripleEq, - &Regexp_new, - &TEnum_new, - &TEnum_abstract, - &TPrivateCompiler_runningCompiled_p, - &TPrivateCompiler_compilerVersion, - &Thread_squareBrackets, - &Thread_squareBracketsEq, - }; - for (auto &method : knownCMethodsInstance) { - if (debug_mode) { - method.sanityCheck(gs); - } - res.emplace_back(&method); - } - for (auto &method : knownCMethodsSingleton) { - if (debug_mode) { - method.sanityCheck(gs); - } - res.emplace_back(&method); - } - return res; -} - -// stuff -}; // namespace - -llvm::Value *SymbolBasedIntrinsicMethod::receiverFastPathTest(MethodCallContext &mcctx, - core::ClassOrModuleRef potentialClass) const { - auto *recv = mcctx.varGetRecv(); - return Payload::typeTest(mcctx.cs, mcctx.builder, recv, potentialClass); -} - -bool SymbolBasedIntrinsicMethod::skipFastPathTest(MethodCallContext &mcctx, - core::ClassOrModuleRef potentialClass) const { - return false; -} - -bool SymbolBasedIntrinsicMethod::needsAfterIntrinsicProcessing() const { - return true; -} - -void SymbolBasedIntrinsicMethod::sanityCheck(const core::GlobalState &gs) const {} - -vector &SymbolBasedIntrinsicMethod::definedIntrinsics(const core::GlobalState &gs) { - static vector ret = getKnownCMethodPtrs(gs); - - return ret; -} - -}; // namespace sorbet::compiler diff --git a/compiler/IREmitter/WrappedIntrinsics.h b/compiler/IREmitter/WrappedIntrinsics.h deleted file mode 100644 index 81ab717603..0000000000 --- a/compiler/IREmitter/WrappedIntrinsics.h +++ /dev/null @@ -1,93 +0,0 @@ -// This file is autogenerated. Do not edit it by hand. Regenerate it with: -// cd compiler/IREmitter/Intrinsics && make - -// clang-format off - {core::Symbols::Array(), "+", CMethod{"sorbet_int_rb_ary_plus"}}, - {core::Symbols::Array(), "-", CMethod{"sorbet_int_rb_ary_diff"}}, - {core::Symbols::Array(), "<<", CMethod{"sorbet_int_rb_ary_push"}}, - {core::Symbols::Array(), "<=>", CMethod{"sorbet_int_rb_ary_cmp"}}, - {core::Symbols::Array(), "[]", CMethod{"sorbet_int_rb_ary_aref"}}, - {core::Symbols::Array(), "slice", CMethod{"sorbet_int_rb_ary_aref"}}, - {core::Symbols::Array(), "assoc", CMethod{"sorbet_int_rb_ary_assoc"}}, - {core::Symbols::Array(), "at", CMethod{"sorbet_int_rb_ary_at"}}, - {core::Symbols::Array(), "clear", CMethod{"sorbet_int_rb_ary_clear"}}, - {core::Symbols::Array(), "concat", CMethod{"sorbet_int_rb_ary_concat_multi"}}, - {core::Symbols::Array(), "delete", CMethod{"sorbet_int_rb_ary_delete"}}, - {core::Symbols::Array(), "first", CMethod{"sorbet_int_rb_ary_first"}}, - {core::Symbols::Array(), "flatten", CMethod{"sorbet_int_rb_ary_flatten"}}, - {core::Symbols::Array(), "include?", CMethod{"sorbet_int_rb_ary_includes"}}, - {core::Symbols::Array(), "initialize_copy", CMethod{"sorbet_int_rb_ary_replace"}}, - {core::Symbols::Array(), "replace", CMethod{"sorbet_int_rb_ary_replace"}}, - {core::Symbols::Array(), "join", CMethod{"sorbet_int_rb_ary_join_m"}}, - {core::Symbols::Array(), "last", CMethod{"sorbet_int_rb_ary_last"}}, - {core::Symbols::Array(), "push", CMethod{"sorbet_int_rb_ary_push_m"}}, - {core::Symbols::Array(), "rassoc", CMethod{"sorbet_int_rb_ary_rassoc"}}, - {core::Symbols::Array(), "sort", CMethod{"sorbet_int_rb_ary_sort"}}, - {core::Symbols::Array(), "sort!", CMethod{"sorbet_int_rb_ary_sort_bang"}}, - {core::Symbols::Float(), "*", CMethod{"sorbet_int_rb_float_mul"}}, - {core::Symbols::Float(), "**", CMethod{"sorbet_int_rb_float_pow"}}, - {core::Symbols::Float(), "+", CMethod{"sorbet_int_rb_float_plus"}}, - {core::Symbols::Float(), "-@", CMethod{"sorbet_int_rb_float_uminus"}}, - {core::Symbols::Float(), "<", CMethod{"sorbet_int_flo_lt"}}, - {core::Symbols::Float(), "<=", CMethod{"sorbet_int_flo_le"}}, - {core::Symbols::Float(), ">", CMethod{"sorbet_int_rb_float_gt"}}, - {core::Symbols::Float(), ">=", CMethod{"sorbet_int_flo_ge"}}, - {core::Symbols::Float(), "abs", CMethod{"sorbet_int_rb_float_abs"}}, - {core::Symbols::Float(), "magnitude", CMethod{"sorbet_int_rb_float_abs"}}, - {core::Symbols::Float(), "finite?", CMethod{"sorbet_int_rb_flo_is_finite_p"}}, - {core::Symbols::Float(), "infinite?", CMethod{"sorbet_int_rb_flo_is_infinite_p"}}, - {core::Symbols::Hash(), "dig", CMethod{"sorbet_int_rb_hash_dig"}}, - {core::Symbols::Hash(), "include?", CMethod{"sorbet_int_rb_hash_has_key"}}, - {core::Symbols::Hash(), "member?", CMethod{"sorbet_int_rb_hash_has_key"}}, - {core::Symbols::Hash(), "has_key?", CMethod{"sorbet_int_rb_hash_has_key"}}, - {core::Symbols::Hash(), "key?", CMethod{"sorbet_int_rb_hash_has_key"}}, - {core::Symbols::Integer(), "%", CMethod{"sorbet_int_rb_int_modulo"}}, - {core::Symbols::Integer(), "modulo", CMethod{"sorbet_int_rb_int_modulo"}}, - {core::Symbols::Integer(), "&", CMethod{"sorbet_int_rb_int_and"}}, - {core::Symbols::Integer(), "*", CMethod{"sorbet_int_rb_int_mul"}}, - {core::Symbols::Integer(), "**", CMethod{"sorbet_int_rb_int_pow"}}, - {core::Symbols::Integer(), "+", CMethod{"sorbet_int_rb_int_plus"}}, - {core::Symbols::Integer(), "-", CMethod{"sorbet_int_rb_int_minus"}}, - {core::Symbols::Integer(), "-@", CMethod{"sorbet_int_rb_int_uminus"}}, - {core::Symbols::Integer(), "/", CMethod{"sorbet_int_rb_int_div"}}, - {core::Symbols::Integer(), "<<", CMethod{"sorbet_int_rb_int_lshift"}}, - {core::Symbols::Integer(), "<=>", CMethod{"sorbet_int_rb_int_cmp"}}, - {core::Symbols::Integer(), "===", CMethod{"sorbet_int_rb_int_equal"}}, - {core::Symbols::Integer(), "==", CMethod{"sorbet_int_rb_int_equal"}}, - {core::Symbols::Integer(), ">", CMethod{"sorbet_int_rb_int_gt"}}, - {core::Symbols::Integer(), ">=", CMethod{"sorbet_int_rb_int_ge"}}, - {core::Symbols::Integer(), "abs", CMethod{"sorbet_int_rb_int_abs"}}, - {core::Symbols::Integer(), "magnitude", CMethod{"sorbet_int_rb_int_abs"}}, - {core::Symbols::Integer(), "div", CMethod{"sorbet_int_rb_int_idiv"}}, - {core::Symbols::Integer(), "divmod", CMethod{"sorbet_int_rb_int_divmod"}}, - {core::Symbols::Integer(), "fdiv", CMethod{"sorbet_int_rb_int_fdiv"}}, - {core::Symbols::Integer(), "gcd", CMethod{"sorbet_int_rb_gcd"}}, - {core::Symbols::Integer(), "gcdlcm", CMethod{"sorbet_int_rb_gcdlcm"}}, - {core::Symbols::Integer(), "lcm", CMethod{"sorbet_int_rb_lcm"}}, - {core::Symbols::Integer(), "odd?", CMethod{"sorbet_int_rb_int_odd_p"}}, - {core::Symbols::Integer(), "pow", CMethod{"sorbet_int_rb_int_powm"}}, - {core::Symbols::Integer(), "to_f", CMethod{"sorbet_int_int_to_f"}}, - {core::Symbols::Regexp(), "encoding", CMethod{"sorbet_int_rb_obj_encoding"}}, - {core::Symbols::String(), "encoding", CMethod{"sorbet_int_rb_obj_encoding"}}, - {core::Symbols::String(), "*", CMethod{"sorbet_int_rb_str_times"}}, - {core::Symbols::String(), "+", CMethod{"sorbet_int_rb_str_plus"}}, - {core::Symbols::String(), "<<", CMethod{"sorbet_int_rb_str_concat"}}, - {core::Symbols::String(), "==", CMethod{"sorbet_int_rb_str_equal"}}, - {core::Symbols::String(), "===", CMethod{"sorbet_int_rb_str_equal"}}, - {core::Symbols::String(), "[]", CMethod{"sorbet_int_rb_str_aref_m"}}, - {core::Symbols::String(), "slice", CMethod{"sorbet_int_rb_str_aref_m"}}, - {core::Symbols::String(), "dump", CMethod{"sorbet_int_rb_str_dump"}}, - {core::Symbols::String(), "eql?", CMethod{"sorbet_int_rb_str_eql"}}, - {core::Symbols::String(), "freeze", CMethod{"sorbet_int_rb_str_freeze"}}, - {core::Symbols::String(), "initialize_copy", CMethod{"sorbet_int_rb_str_replace"}}, - {core::Symbols::String(), "replace", CMethod{"sorbet_int_rb_str_replace"}}, - {core::Symbols::String(), "inspect", CMethod{"sorbet_int_rb_str_inspect"}}, - {core::Symbols::String(), "intern", CMethod{"sorbet_int_rb_str_intern"}}, - {core::Symbols::String(), "to_sym", CMethod{"sorbet_int_rb_str_intern"}}, - {core::Symbols::String(), "length", CMethod{"sorbet_int_rb_str_length"}}, - {core::Symbols::String(), "size", CMethod{"sorbet_int_rb_str_length"}}, - {core::Symbols::String(), "ord", CMethod{"sorbet_int_rb_str_ord"}}, - {core::Symbols::String(), "start_with?", CMethod{"sorbet_int_rb_str_start_with"}}, - {core::Symbols::String(), "succ", CMethod{"sorbet_int_rb_str_succ"}}, - {core::Symbols::String(), "next", CMethod{"sorbet_int_rb_str_succ"}}, - // clang-format on diff --git a/compiler/IREmitter/sends.cc b/compiler/IREmitter/sends.cc deleted file mode 100644 index 6a270ae6e3..0000000000 --- a/compiler/IREmitter/sends.cc +++ /dev/null @@ -1,535 +0,0 @@ -// These violate our poisons so have to happen first -#include "llvm/IR/Attributes.h" -#include "llvm/IR/DerivedTypes.h" // FunctionType, StructType -#include "llvm/IR/IRBuilder.h" -#include "llvm/IR/Verifier.h" - -#include "absl/base/casts.h" -#include "ast/Helpers.h" -#include "ast/ast.h" -#include "cfg/CFG.h" -#include "common/FileOps.h" -#include "common/sort/sort.h" -#include "compiler/Core/CompilerState.h" -#include "compiler/Core/FailCompilation.h" -#include "compiler/Errors/Errors.h" -#include "compiler/IREmitter/IREmitter.h" -#include "compiler/IREmitter/IREmitterContext.h" -#include "compiler/IREmitter/IREmitterHelpers.h" -#include "compiler/IREmitter/MethodCallContext.h" -#include "compiler/IREmitter/NameBasedIntrinsics.h" -#include "compiler/IREmitter/Payload.h" -#include "compiler/IREmitter/SymbolBasedIntrinsicMethod.h" -#include - -using namespace std; -namespace sorbet::compiler { -namespace { - -llvm::Value *tryNameBasedIntrinsic(MethodCallContext &mcctx) { - for (auto nameBasedIntrinsic : NameBasedIntrinsicMethod::definedIntrinsics()) { - if (!absl::c_linear_search(nameBasedIntrinsic->applicableMethods(mcctx.cs), mcctx.send->fun)) { - continue; - } - - if (mcctx.blk.has_value() && nameBasedIntrinsic->blockHandled == Intrinsics::HandleBlock::Unhandled) { - continue; - } - - return nameBasedIntrinsic->makeCall(mcctx); - } - return IREmitterHelpers::emitMethodCallViaRubyVM(mcctx); -} - -llvm::Value *tryFinalMethodCall(MethodCallContext &mcctx) { - // TODO(trevor) we could probably handle methods wih block args as well, by passing the block handler through the - // current ruby execution context. - if (mcctx.blk.has_value()) { - return tryNameBasedIntrinsic(mcctx); - } - - // NOTE: if we don't see a final call to another compiled method, we skip this optimization as it would just degrade - // the performance of a normal send. - auto &cs = mcctx.cs; - auto recvType = mcctx.send->recv.type; - auto finalInfo = IREmitterHelpers::isFinalMethod(cs, recvType, mcctx.send->fun); - if (!finalInfo.has_value()) { - return tryNameBasedIntrinsic(mcctx); - } - - // If the wrapper is defined in another file, this will be resolved by the runtime linker. - auto *wrapper = IREmitterHelpers::getOrCreateDirectWrapper(mcctx.cs, finalInfo->method); - - auto &builder = mcctx.builder; - auto *send = mcctx.send; - auto *recv = mcctx.varGetRecv(); - - auto methodName = string(send->fun.shortName(cs)); - auto *fastFinalCall = - llvm::BasicBlock::Create(cs, llvm::Twine("fastFinalCall_") + methodName, builder.GetInsertBlock()->getParent()); - auto *slowFinalCall = - llvm::BasicBlock::Create(cs, llvm::Twine("slowFinalCall_") + methodName, builder.GetInsertBlock()->getParent()); - auto *afterFinalCall = llvm::BasicBlock::Create(cs, llvm::Twine("afterFinalCall_") + methodName, - builder.GetInsertBlock()->getParent()); - - llvm::Value *typeTest; - - // When the receiver is a singleton, we can skip the type test because it would end up looking like the following: - // - // > %klass = sorbet_i_getRubyConst(...) - // > sorbet_isa_class_of(%klass, %klass) - // - // which we know to be true. - if (IREmitterHelpers::isAliasToSingleton(cs, mcctx.irctx, send->recv.variable, finalInfo->recv)) { - typeTest = llvm::ConstantInt::getTrue(cs); - } else { - typeTest = Payload::typeTest(cs, builder, recv, finalInfo->recv); - } - - builder.CreateCondBr(Payload::setExpectedBool(cs, builder, typeTest, true), fastFinalCall, slowFinalCall); - - // fast path: emit a direct call - builder.SetInsertPoint(fastFinalCall); - - // we need a method entry to be able to perform a direct call, so we ensure that an empty inline cache is available, - // and populate it on the first call to the fast path - CallCacheFlags flags; - if (send->isPrivateOk) { - flags.fcall = true; - } else { - flags.args_simple = true; - } - auto *cache = IREmitterHelpers::makeInlineCache(cs, builder, methodName, flags, 0, {}); - - // this is unfortunate: fillSendArgsArray will allocate a hash when keyword arguments are present. - auto args = IREmitterHelpers::fillSendArgArray(mcctx); - auto *fastPathResult = builder.CreateCall(wrapper, {cache, args.argc, args.argv, recv}); - - Payload::assumeType(cs, builder, fastPathResult, finalInfo->method.data(cs)->resultType); - - auto *fastPathEnd = builder.GetInsertBlock(); - builder.CreateBr(afterFinalCall); - - // slow path: emit a call via the ruby vm - builder.SetInsertPoint(slowFinalCall); - auto *slowPathResult = IREmitterHelpers::emitMethodCallViaRubyVM(mcctx); - auto *slowPathEnd = builder.GetInsertBlock(); - builder.CreateBr(afterFinalCall); - - // merge the two paths - builder.SetInsertPoint(afterFinalCall); - auto *phi = builder.CreatePHI(builder.getInt64Ty(), 2, llvm::Twine("finalCallResult_") + methodName); - phi->addIncoming(fastPathResult, fastPathEnd); - phi->addIncoming(slowPathResult, slowPathEnd); - - return phi; -} - -// We want at least inline storage for one intrinsic so we don't allocate during -// the search process. Two intrinsics are reasonably common (e.g. .length) and -// three could come up quite a bit in numeric code. -const size_t NUM_INTRINSICS = 3; - -struct ApplicableIntrinsic { - ApplicableIntrinsic(const SymbolBasedIntrinsicMethod *method, core::ClassOrModuleRef klass) - : method(method), klass(klass) {} - const SymbolBasedIntrinsicMethod *method; - core::ClassOrModuleRef klass; -}; - -InlinedVector applicableIntrinsics(MethodCallContext &mcctx) { - InlinedVector intrinsics; - auto remainingType = mcctx.send->recv.type; - for (auto symbolBasedIntrinsic : SymbolBasedIntrinsicMethod::definedIntrinsics(mcctx.cs)) { - if (!absl::c_linear_search(symbolBasedIntrinsic->applicableMethods(mcctx.cs), mcctx.send->fun)) { - continue; - } - - if (mcctx.blk.has_value() && symbolBasedIntrinsic->blockHandled == Intrinsics::HandleBlock::Unhandled) { - continue; - } - - auto potentialClasses = symbolBasedIntrinsic->applicableClasses(mcctx.cs); - for (auto &c : potentialClasses) { - auto leftType = core::Types::dropSubtypesOf(mcctx.cs, remainingType, c); - - if (leftType == remainingType) { - continue; - } - - remainingType = leftType; - intrinsics.emplace_back(symbolBasedIntrinsic, c); - } - } - - return intrinsics; -} - -llvm::Value *trySymbolBasedIntrinsic(MethodCallContext &mcctx) { - auto &cs = mcctx.cs; - auto *send = mcctx.send; - auto &builder = mcctx.builder; - auto remainingType = mcctx.send->recv.type; - auto afterSend = llvm::BasicBlock::Create(cs, "afterSend", builder.GetInsertBlock()->getParent()); - auto rememberStart = builder.GetInsertBlock(); - builder.SetInsertPoint(afterSend); - auto methodName = send->fun.shortName(cs); - llvm::StringRef methodNameRef(methodName.data(), methodName.size()); - auto phi = builder.CreatePHI(builder.getInt64Ty(), 2, llvm::Twine("symIntrinsicRawPhi_") + methodNameRef); - builder.SetInsertPoint(rememberStart); - if (!remainingType.isUntyped()) { - auto intrinsics = applicableIntrinsics(mcctx); - - if (intrinsics.size() == 1 && intrinsics[0].method->skipFastPathTest(mcctx, intrinsics[0].klass)) { - auto fastPathRes = intrinsics[0].method->makeCall(mcctx); - if (intrinsics[0].method->needsAfterIntrinsicProcessing()) { - Payload::afterIntrinsic(cs, builder); - } - auto fastPathEnd = builder.GetInsertBlock(); - builder.CreateBr(afterSend); - builder.SetInsertPoint(afterSend); - phi->addIncoming(fastPathRes, fastPathEnd); - return phi; - } - - for (auto &intrinsic : intrinsics) { - auto clazName = intrinsic.klass.data(cs)->name.shortName(cs); - llvm::StringRef clazNameRef(clazName.data(), clazName.size()); - - auto alternative = llvm::BasicBlock::Create( - cs, llvm::Twine("alternativeCallIntrinsic_") + clazNameRef + "_" + methodNameRef, - builder.GetInsertBlock()->getParent()); - auto fastPath = - llvm::BasicBlock::Create(cs, llvm::Twine("fastSymCallIntrinsic_") + clazNameRef + "_" + methodNameRef, - builder.GetInsertBlock()->getParent()); - - auto *typeTest = intrinsic.method->receiverFastPathTest(mcctx, intrinsic.klass); - builder.CreateCondBr(Payload::setExpectedBool(cs, builder, typeTest, true), fastPath, alternative); - builder.SetInsertPoint(fastPath); - auto fastPathRes = intrinsic.method->makeCall(mcctx); - if (intrinsic.method->needsAfterIntrinsicProcessing()) { - Payload::afterIntrinsic(cs, builder); - } - auto fastPathEnd = builder.GetInsertBlock(); - builder.CreateBr(afterSend); - phi->addIncoming(fastPathRes, fastPathEnd); - builder.SetInsertPoint(alternative); - } - } - auto slowPathRes = tryFinalMethodCall(mcctx); - auto slowPathEnd = builder.GetInsertBlock(); - builder.CreateBr(afterSend); - builder.SetInsertPoint(afterSend); - phi->addIncoming(slowPathRes, slowPathEnd); - return phi; -} - -} // namespace -llvm::Value *IREmitterHelpers::emitMethodCall(MethodCallContext &mcctx) { - return trySymbolBasedIntrinsic(mcctx); -} - -std::size_t IREmitterHelpers::sendArgCount(cfg::Send *send) { - std::size_t numPosArgs = send->numPosArgs; - if (numPosArgs < send->args.size()) { - numPosArgs++; - } - return numPosArgs; -} - -namespace { - -void setSendArgsEntry(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *sendArgs, int index, - llvm::Value *val) { - // the sendArgArray is always a pointer to an array of VALUEs. That is, since the - // outermost type is a pointer, not an array, the first 0 in the instruction: - // getelementptr inbounds , * %s, i64 0, i64 - // just means to offset 0 from the pointer's contents. Then the second index is into the - // array (so for this type of pointer-to-array, the first index will always be 0, - // unless you're trying to do something more powerful with GEP, like compute sizeof) - builder.CreateStore(val, builder.CreateConstGEP2_64(sendArgs, 0, index, fmt::format("callArgs{}Addr", index))); -} - -llvm::Value *getSendArgsPointer(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *sendArgs) { - return builder.CreateConstGEP2_64(sendArgs, 0, 0); -} - -} // namespace - -IREmitterHelpers::SendArgInfo::SendArgInfo(llvm::Value *argc, llvm::Value *argv, llvm::Value *kw_splat, - vector argValues) - : argc{argc}, argv{argv}, kw_splat{kw_splat}, argValues(std::move(argValues)) {} - -IREmitterHelpers::SendArgInfo IREmitterHelpers::fillSendArgArray(MethodCallContext &mcctx, const std::size_t offset) { - auto &cs = mcctx.cs; - auto &builder = mcctx.builder; - auto &args = mcctx.send->args; - auto irctx = mcctx.irctx; - auto rubyRegionId = mcctx.rubyRegionId; - - auto posEnd = mcctx.send->numPosArgs; - auto kwEnd = mcctx.send->args.size(); - bool hasKwSplat = (kwEnd - posEnd) & 0x1; - if (hasKwSplat) { - kwEnd--; - } - - ENFORCE(offset <= posEnd, "Invalid positional offset given to fillSendArgArray"); - - // keep track of the intermediate argument values that we've seen - vector argValues; - - // compute the number of actual args that will be filled in - auto numPosArgs = posEnd - offset; - auto numKwArgs = kwEnd - posEnd; - auto length = numPosArgs; - bool hasKwArgs = numKwArgs > 0 || hasKwSplat; - llvm::Value *kw_splat; - if (hasKwArgs) { - length++; - kw_splat = llvm::ConstantInt::get(cs, llvm::APInt(32, 1, true)); - } else { - kw_splat = llvm::ConstantInt::get(cs, llvm::APInt(32, 0, true)); - } - - auto *argc = llvm::ConstantInt::get(cs, llvm::APInt(32, length, true)); - if (length == 0) { - return SendArgInfo{argc, llvm::Constant::getNullValue(llvm::Type::getInt64PtrTy(cs)), kw_splat, - std::move(argValues)}; - } - - auto *sendArgs = irctx.sendArgArrayByBlock[rubyRegionId]; - - // fill in keyword args first, so that we can re-use the args vector to build the initial hash - if (hasKwArgs) { - llvm::Value *kwHash = nullptr; - if (numKwArgs == 0) { - // no inlined keyword args, lookup the hash to be splatted - kwHash = Payload::varGet(cs, args.back().variable, builder, irctx, rubyRegionId); - } else { - // fill in inlined args (posEnd .. kwEnd) - auto it = args.begin() + posEnd; - for (auto argId = 0; argId < numKwArgs; ++argId, ++it) { - auto var = Payload::varGet(cs, it->variable, builder, irctx, rubyRegionId); - argValues.emplace_back(var); - setSendArgsEntry(cs, builder, sendArgs, argId, var); - } - - kwHash = builder.CreateCall(cs.getFunction("sorbet_hashBuild"), - {llvm::ConstantInt::get(cs, llvm::APInt(32, numKwArgs, true)), - getSendArgsPointer(cs, builder, sendArgs)}, - "kwargsHash"); - - // merge in the splat if it's present (mcctx.send->args.back()) - if (hasKwSplat) { - auto *splat = Payload::varGet(cs, args.back().variable, builder, irctx, rubyRegionId); - argValues.emplace_back(splat); - builder.CreateCall(cs.getFunction("sorbet_hashUpdate"), {kwHash, splat}); - } - } - - setSendArgsEntry(cs, builder, sendArgs, numPosArgs, kwHash); - } - - // fill in positional args - { - int argId = 0; - auto it = args.begin() + offset; - for (; argId < numPosArgs; argId += 1, ++it) { - auto var = Payload::varGet(cs, it->variable, builder, irctx, rubyRegionId); - argValues.emplace_back(var); - setSendArgsEntry(cs, builder, sendArgs, argId, var); - } - } - - return SendArgInfo{argc, getSendArgsPointer(cs, builder, sendArgs), kw_splat, std::move(argValues)}; -} - -IREmitterHelpers::SendArgInfo IREmitterHelpers::fillSendArgArray(MethodCallContext &mcctx) { - return fillSendArgArray(mcctx, 0); -} - -RubyStackArgs IREmitterHelpers::buildSendArgs(MethodCallContext &mcctx, cfg::LocalRef recv, const std::size_t offset) { - auto &cs = mcctx.cs; - auto &irctx = mcctx.irctx; - auto &builder = mcctx.builder; - auto &send = mcctx.send; - auto rubyRegionId = mcctx.rubyRegionId; - - CallCacheFlags flags; - - auto name = string(send->fun.shortName(cs)); - vector keywords{}; - vector stack{}; - - // push positional args onto the ruby stack - auto posEnd = send->numPosArgs; - auto argIdx = offset; - for (; argIdx < posEnd; ++argIdx) { - stack.emplace_back(Payload::varGet(cs, send->args[argIdx].variable, builder, irctx, rubyRegionId)); - } - - // push keyword argument values, and populate the keywords vector - auto kwEnd = send->args.size(); - if (posEnd < kwEnd) { - // if there is a keyword splat, merge everything into a hash that's used for the last argument, otherwise - // keywords go in the inline cache, and values go on the stack. - int numKwArgs = kwEnd - posEnd; - bool hasKwSplat = numKwArgs & 0x1; - if (hasKwSplat) { - // The current desugaring behavior of keyword splats is that it will merge all keyword arguments into the - // splat, even inlined ones. As a result, we'll either see keyword args inlined, or one hash argument that - // we'll pass in. This enforce is here so that we remember to update the behavior of this function when we - // eventually fix that desugaring. - ENFORCE(numKwArgs == 1); - flags.kw_splat = true; - - auto var = Payload::varGet(cs, send->args[argIdx].variable, builder, irctx, rubyRegionId); - stack.emplace_back(var); - } else { - flags.kwarg = true; - - while (argIdx < kwEnd) { - auto kwArg = send->args[argIdx++].variable; - auto it = irctx.symbols.find(kwArg); - ENFORCE(it != irctx.symbols.end(), "Keyword arg present with non-symbol keyword"); - keywords.emplace_back(it->second); - - stack.emplace_back(Payload::varGet(cs, send->args[argIdx++].variable, builder, irctx, rubyRegionId)); - } - } - } else { - flags.args_simple = true; - } - - if (send->isPrivateOk) { - flags.fcall = true; - } - - return RubyStackArgs(std::move(stack), std::move(keywords), flags); -} - -namespace { - -llvm::Value *callViaRubyVMSimple(MethodCallContext &mcctx) { - auto &cs = mcctx.cs; - auto &builder = mcctx.builder; - auto &irctx = mcctx.irctx; - auto rubyRegionId = mcctx.rubyRegionId; - auto *cfp = Payload::getCFPForBlock(cs, builder, irctx, rubyRegionId); - - auto &stack = mcctx.getStackArgs().stack; - auto *cache = mcctx.getInlineCache(); - auto *recv = mcctx.varGetRecv(); - - vector args; - args.emplace_back(cache); - - if (auto *blk = mcctx.blkAsFunction()) { - auto blkId = mcctx.blk.value(); - args.emplace_back(llvm::ConstantInt::get(cs, llvm::APInt(1, static_cast(irctx.blockUsesBreak[blkId])))); - auto *blkIfunc = Payload::getOrBuildBlockIfunc(cs, builder, irctx, blkId); - args.emplace_back(blkIfunc); - } else { - args.emplace_back(llvm::ConstantInt::get(cs, llvm::APInt(1, static_cast(false)))); - auto *vmIfuncType = llvm::StructType::getTypeByName(cs, "struct.vm_ifunc"); - args.emplace_back(llvm::ConstantPointerNull::get(llvm::PointerType::getUnqual(vmIfuncType))); - } - auto *searchSuper = - llvm::ConstantInt::get(cs, llvm::APInt(1, static_cast(mcctx.send->fun == core::Names::super()))); - args.emplace_back(searchSuper); - args.emplace_back(cfp); - args.emplace_back(recv); - for (auto *arg : stack) { - args.emplace_back(arg); - } - - // TODO(neil), RUBYLANG-338: add methodName as a phantom arg to sorbet_i_send - return builder.CreateCall(cs.getFunction("sorbet_i_send"), llvm::ArrayRef(args)); -} - -} // namespace - -llvm::Value *IREmitterHelpers::emitMethodCallViaRubyVM(MethodCallContext &mcctx) { - auto &cs = mcctx.cs; - auto &builder = mcctx.builder; - auto &irctx = mcctx.irctx; - auto *send = mcctx.send; - - // If we get here with or Sorbet::Private::Static, then something has - // gone wrong. - if (auto *at = core::cast_type(send->recv.type)) { - if (at->klass == core::Symbols::MagicSingleton()) { - failCompilation(cs, core::Loc(cs.file, send->receiverLoc), - "No runtime implemention for method exists"); - } - if (at->klass == core::Symbols::Sorbet_Private_StaticSingleton()) { - failCompilation(cs, core::Loc(cs.file, send->receiverLoc), - "No runtime implementation for Sorbet::Private::Static method exists"); - } - } - - // Try to call blocks directly without reifying the block into a proc. - if (send->fun == core::Names::call() && - IREmitterHelpers::canPassThroughBlockViaRubyVM(mcctx, send->recv.variable)) { - // If our send has a block, we should have turned it into a call to call-with-block - // or similar. - ENFORCE(!mcctx.blk.has_value()); - - // fill in args - auto args = IREmitterHelpers::fillSendArgArray(mcctx); - auto *cfp = Payload::getCFPForBlock(cs, builder, irctx, mcctx.rubyRegionId); - - return builder.CreateCall(cs.getFunction("sorbet_vm_callBlock"), {cfp, args.argc, args.argv, args.kw_splat}, - "rawBlockSendResult"); - } - - return callViaRubyVMSimple(mcctx); -} - -// Create a global to hold the FunctionInlineCache value, and setup its initialization in the `Init_` function. -llvm::Value *IREmitterHelpers::makeInlineCache(CompilerState &cs, llvm::IRBuilderBase &builder, string methodName, - CallCacheFlags flags, int argc, const vector &keywords) { - auto *setupFn = cs.getFunction("sorbet_setupFunctionInlineCache"); - auto *cacheTy = static_cast(setupFn->arg_begin()->getType())->getElementType(); - auto *zero = llvm::ConstantAggregateZero::get(cacheTy); - - auto *cache = new llvm::GlobalVariable(*cs.module, cacheTy, false, llvm::GlobalVariable::InternalLinkage, zero, - llvm::Twine("ic_") + methodName); - - // initialize the global during GlobalConstructorsInit - { - auto restore = builder.saveIP(); - - builder.SetInsertPoint(cs.globalConstructorsEntry); - - auto *midVal = Payload::idIntern(cs, builder, methodName); - - auto *argcVal = llvm::ConstantInt::get(cs, llvm::APInt(32, argc, true)); - - auto *keywordsLenVal = llvm::ConstantInt::get(cs, llvm::APInt(32, keywords.size(), true)); - - auto *flagVal = flags.build(cs, builder); - - vector args; - args.push_back(cache); - args.push_back(midVal); - args.push_back(flagVal); - args.push_back(argcVal); - args.push_back(keywordsLenVal); - if (!keywords.empty()) { - for (auto kw : keywords) { - auto *kwVal = Payload::idIntern(cs, builder, kw); - args.push_back(kwVal); - } - } - - builder.CreateCall(setupFn, args); - - builder.restoreIP(restore); - } - - return cache; -} - -} // namespace sorbet::compiler diff --git a/compiler/Linker/BUILD b/compiler/Linker/BUILD deleted file mode 100644 index a49a42d417..0000000000 --- a/compiler/Linker/BUILD +++ /dev/null @@ -1,18 +0,0 @@ -cc_library( - name = "Linker", - srcs = [ - "Linker.cc", - "Linker.h", - ], - linkstatic = select({ - "@com_stripe_ruby_typer//tools/config:linkshared": 0, - "//conditions:default": 1, - }), - visibility = ["//visibility:public"], - deps = [ - "@com_stripe_ruby_typer//common", - "@com_stripe_ruby_typer//core", - "@llvm//:Core", - "@spdlog", - ], -) diff --git a/compiler/Linker/Linker.cc b/compiler/Linker/Linker.cc deleted file mode 100644 index 32067245c0..0000000000 --- a/compiler/Linker/Linker.cc +++ /dev/null @@ -1,41 +0,0 @@ -#include "compiler/Linker/Linker.h" -#include "common/Subprocess.h" -#include "common/timers/Timer.h" - -using namespace std; -namespace sorbet::compiler { -enum class OS { Lin, Darwin }; -bool Linker::run(spdlog::logger &log, vector objectFiles, string outputFile) { - Timer timer(log, "linking"); - OS os; -#if defined(__linux__) - os = OS::Lin; -#elif defined(__APPLE__) - os = OS::Darwin; -#else - Exception::notImplemented(); -#endif - - // this should probably be changed to direct dependency on lld source and fork+call to main of it. - // https://github.com/llvm-mirror/lld/blob/64b024a57c56c3528d6be3d14be5e3da42614a6f/tools/lld/lld.cpp#L147 is what - // to cargocult - string executable = "ld"; - vector commandLineArgs; - switch (os) { - case OS::Darwin: - commandLineArgs = { - "-bundle", "-o", outputFile + ".bundle", "-undefined", "dynamic_lookup", "-macosx_version_min", - "10.14"}; - break; - case OS::Lin: - commandLineArgs = {"-shared", "-o", outputFile + ".so", "--allow-shlib-undefined"}; - break; - } - for (auto &of : objectFiles) { - commandLineArgs.emplace_back(move(of)); - } - auto ldOutput = Subprocess::spawn(executable, commandLineArgs, nullopt); - return ldOutput != nullopt; -}; - -} // namespace sorbet::compiler diff --git a/compiler/Linker/Linker.h b/compiler/Linker/Linker.h deleted file mode 100644 index eb8176bfdf..0000000000 --- a/compiler/Linker/Linker.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef SORBET_COMPILER_LINKER_H -#define SORBET_COMPILER_LINKER_H - -#include -#include -#include -#include - -namespace spdlog { -class logger; -} -namespace sorbet::compiler { -class Linker { -public: - static bool run(spdlog::logger &log, std::vector objectFiles, std::string outputFile); -}; -} // namespace sorbet::compiler -#endif diff --git a/compiler/ObjectFileEmitter/BUILD b/compiler/ObjectFileEmitter/BUILD deleted file mode 100644 index e134d49bd6..0000000000 --- a/compiler/ObjectFileEmitter/BUILD +++ /dev/null @@ -1,20 +0,0 @@ -cc_library( - name = "ObjectFileEmitter", - srcs = [ - "ObjectFileEmitter.cc", - "ObjectFileEmitter.h", - ], - hdrs = ["ObjectFileEmitter.h"], - linkstatic = select({ - "//tools/config:linkshared": 0, - "//conditions:default": 1, - }), - visibility = ["//visibility:public"], - deps = [ - "//compiler/IREmitter/Payload", - "//compiler/Linker", - "//compiler/Passes", - "//core", - "@spdlog", - ], -) diff --git a/compiler/ObjectFileEmitter/ObjectFileEmitter.cc b/compiler/ObjectFileEmitter/ObjectFileEmitter.cc deleted file mode 100644 index dd05ad9f8e..0000000000 --- a/compiler/ObjectFileEmitter/ObjectFileEmitter.cc +++ /dev/null @@ -1,460 +0,0 @@ -// These violate our poisons so have to happen first -#include "llvm/Analysis/GlobalsModRef.h" -#include "llvm/Analysis/ScopedNoAliasAA.h" -#include "llvm/Analysis/TargetLibraryInfo.h" -#include "llvm/Analysis/TargetTransformInfo.h" -#include "llvm/Analysis/TypeBasedAliasAnalysis.h" -#include "llvm/CodeGen/CommandFlags.h" -#include "llvm/IR/BasicBlock.h" -#include "llvm/IR/IRBuilder.h" -#include "llvm/IR/IRPrintingPasses.h" -#include "llvm/IR/LLVMContext.h" -#include "llvm/IR/LegacyPassManager.h" -#include "llvm/IR/Module.h" -#include "llvm/IR/Verifier.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/Host.h" -#include "llvm/Support/TargetRegistry.h" -#include "llvm/Support/TargetSelect.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/Target/TargetMachine.h" -#include "llvm/Target/TargetOptions.h" -#include "llvm/Transforms/AggressiveInstCombine/AggressiveInstCombine.h" -#include "llvm/Transforms/IPO.h" -#include "llvm/Transforms/IPO/AlwaysInliner.h" -#include "llvm/Transforms/IPO/Attributor.h" -#include "llvm/Transforms/IPO/ForceFunctionAttrs.h" -#include "llvm/Transforms/IPO/FunctionAttrs.h" -#include "llvm/Transforms/IPO/InferFunctionAttrs.h" -#include "llvm/Transforms/IPO/PassManagerBuilder.h" -#include "llvm/Transforms/InstCombine/InstCombine.h" -#include "llvm/Transforms/Instrumentation.h" -#include "llvm/Transforms/Scalar.h" -#include "llvm/Transforms/Scalar/GVN.h" -#include "llvm/Transforms/Scalar/InstSimplifyPass.h" -#include "llvm/Transforms/Utils.h" -#include "llvm/Transforms/Utils/Mem2Reg.h" -#include "llvm/Transforms/Utils/PromoteMemToReg.h" -#include "llvm/Transforms/Vectorize.h" - -#include "common/FileOps.h" -#include "common/timers/Timer.h" -#include "compiler/Linker/Linker.h" -#include "compiler/ObjectFileEmitter/ObjectFileEmitter.h" -#include "compiler/Passes/Passes.h" -#include - -using namespace std; -namespace sorbet::compiler { -namespace { - -constexpr int sizeLevel = 0; -constexpr int optLevel = 2; - -bool outputObjectFile(spdlog::logger &logger, llvm::legacy::PassManager &pm, const string &rubyFileName, - const string &fileName, unique_ptr module, llvm::TargetMachine *targetMachine) { - Timer timer(logger, "objectFileEmission", {{"file", rubyFileName}}); - std::error_code ec; - llvm::raw_fd_ostream dest(fileName, ec, llvm::sys::fs::OF_None); - - if (ec) { - llvm::errs() << "Could not open file: " << ec.message(); - return false; - } - - auto fileType = llvm::CGFT_ObjectFile; - - if (targetMachine->addPassesToEmitFile(pm, dest, nullptr, fileType, !debug_mode)) { - llvm::errs() << "TheTargetMachine can't emit a file of this type"; - return false; - } - - pm.run(*module); - dest.flush(); - return true; -} - -constexpr bool runExpensiveInstructionConbining = false; // true in O3 -constexpr bool unnecessaryForUs = false; // phases that don't do anything due to us not being a c++ compiler - -void addModulePasses(llvm::legacy::PassManager &pm) { - // this is intended to mimic llvm::PassManagerBuilder::populateModulePassManager - // while disabling optimizations that don't help us much to speedup and adding ones that do. Please explicitly - // leave comments with added/removed passes - // pmbuilder.populateModulePassManager(pm); - - pm.add(llvm::createSROAPass()); // this is super useful for us so we want to run it early - if (unnecessaryForUs) { - pm.add(llvm::createForceFunctionAttrsLegacyPass()); - } - pm.add(llvm::createTypeBasedAAWrapperPass()); - pm.add(llvm::createScopedNoAliasAAWrapperPass()); - pm.add(llvm::createInferFunctionAttrsLegacyPass()); - // pm.add(llvm::createCallSiteSplittingPass()); // this is only enabled under O3 for C++ - pm.add(llvm::createIPSCCPPass()); - pm.add(llvm::createCalledValuePropagationPass()); - pm.add(llvm::createAttributorLegacyPass()); - pm.add(llvm::createGlobalOptimizerPass()); - pm.add(llvm::createPromoteMemoryToRegisterPass()); - pm.add(llvm::createDeadArgEliminationPass()); - pm.add(llvm::createInstructionCombiningPass(runExpensiveInstructionConbining)); - pm.add(llvm::createCFGSimplificationPass()); - // We add a module alias analysis pass here. In part due to bugs in the - // analysis infrastructure this "works" in that the analysis stays alive - // for the entire SCC pass run below. - pm.add(llvm::createGlobalsAAWrapperPass()); - pm.add(llvm::createPruneEHPass()); - - pm.add(llvm::createFunctionInliningPass(optLevel, sizeLevel, false)); - pm.add(llvm::createPostOrderFunctionAttrsLegacyPass()); - // pm.add(llvm::createArgumentPromotionPass()); // O3 - pm.add(llvm::createEarlyCSEPass(true /* Enable mem-ssa. */)); - if (unnecessaryForUs) { - pm.add(llvm::createSpeculativeExecutionIfHasBranchDivergencePass()); - } - pm.add(llvm::createJumpThreadingPass()); - pm.add(llvm::createCorrelatedValuePropagationPass()); - pm.add(llvm::createCFGSimplificationPass()); - // pm.add(llvm::createAggressiveInstCombinerPass()); // O3 - pm.add(llvm::createInstructionCombiningPass(runExpensiveInstructionConbining)); - if (unnecessaryForUs) { - pm.add(llvm::createLibCallsShrinkWrapPass()); - } - pm.add(llvm::createPGOMemOPSizeOptLegacyPass()); - pm.add(llvm::createTailCallEliminationPass()); - pm.add(llvm::createCFGSimplificationPass()); - pm.add(llvm::createReassociatePass()); - pm.add(llvm::createLoopRotatePass(-1)); - pm.add(llvm::createLICMPass(100, 250)); - pm.add(llvm::createLoopUnswitchPass(false, false)); // first bool is true for O2 and lower - pm.add(llvm::createCFGSimplificationPass()); - pm.add(llvm::createInstructionCombiningPass(runExpensiveInstructionConbining)); - pm.add(llvm::createIndVarSimplifyPass()); - pm.add(llvm::createLoopIdiomPass()); - pm.add(llvm::createLoopDeletionPass()); - pm.add(llvm::createSimpleLoopUnrollPass(2, false, false)); - pm.add(llvm::createMergedLoadStoreMotionPass()); - // pm.add(llvm::createGVNPass(false)); - pm.add(llvm::createNewGVNPass()); // by default clang uses _old_ GVN - pm.add(llvm::createMemCpyOptPass()); - pm.add(llvm::createSCCPPass()); - pm.add(llvm::createBitTrackingDCEPass()); - pm.add(llvm::createInstructionCombiningPass(runExpensiveInstructionConbining)); - pm.add(llvm::createJumpThreadingPass()); // Thread jumps - pm.add(llvm::createCorrelatedValuePropagationPass()); - pm.add(llvm::createDeadStoreEliminationPass()); // Delete dead stores - pm.add(llvm::createLICMPass(100, 250)); - pm.add(llvm::createAggressiveDCEPass()); // Delete dead instructions - pm.add(llvm::createCFGSimplificationPass()); // Merge & remove BBs - pm.add(llvm::createInstructionCombiningPass(runExpensiveInstructionConbining)); - pm.add(llvm::createBarrierNoopPass()); - // pm.add(llvm::createPartialInliningPass()); - pm.add(llvm::createEliminateAvailableExternallyPass()); - pm.add(llvm::createReversePostOrderFunctionAttrsPass()); - pm.add(llvm::createGlobalOptimizerPass()); - pm.add(llvm::createGlobalDCEPass()); - pm.add(llvm::createGlobalsAAWrapperPass()); - pm.add(llvm::createFloat2IntPass()); - // pm.add(llvm::createLowerConstantIntrinsicsPass()); // llvm 10-prerelease - pm.add(llvm::createLoopRotatePass(-1)); - pm.add(llvm::createLoopDistributePass()); - - pm.add(llvm::createLoopVectorizePass(false, false)); - pm.add(llvm::createLoopLoadEliminationPass()); - pm.add(llvm::createInstructionCombiningPass(runExpensiveInstructionConbining)); - // pm.add(llvm::createCFGSimplificationPass(1, true, true, false, true)); - pm.add(llvm::createCFGSimplificationPass(llvm::SimplifyCFGOptions() - .forwardSwitchCondToPhi(true) - .convertSwitchToLookupTable(true) - .needCanonicalLoops(false) - .sinkCommonInsts(true))); - pm.add(llvm::createSLPVectorizerPass()); - pm.add(llvm::createInstructionCombiningPass(runExpensiveInstructionConbining)); - pm.add(llvm::createLoopUnrollPass(2, false, false)); - pm.add(llvm::createInstructionCombiningPass(runExpensiveInstructionConbining)); - if (unnecessaryForUs) { - pm.add(llvm::createLICMPass(100, 250)); - pm.add(llvm::createAlignmentFromAssumptionsPass()); - pm.add(llvm::createStripDeadPrototypesPass()); - } - pm.add(llvm::createGlobalDCEPass()); // Remove dead fns and globals. - pm.add(llvm::createConstantMergePass()); // Merge dup global constants - - pm.add(llvm::createHotColdSplittingPass()); - pm.add(llvm::createMergeFunctionsPass()); - // START SORBET ADDITION - // after splitting hot/cold paths, inline hot paths before GVN - pm.add(llvm::createFunctionInliningPass(optLevel, sizeLevel, false)); - // END SORBET ADDITION - if (unnecessaryForUs) { - pm.add(llvm::createLoopSinkPass()); - pm.add(llvm::createInstSimplifyLegacyPass()); - pm.add(llvm::createDivRemPairsPass()); - pm.add(llvm::createCFGSimplificationPass()); - } -} -void addLTOPasses(llvm::legacy::PassManager &pm, llvm::ModulePass *printLowered) { - // this is intended to mimic llvm::PassManagerBuilder::populateLTOPassManager - // while disabling optimizations that don't help us much to speedup and adding ones that do. Please explicitly - // leave comments with added/removed passes - // pmbuilder.populateLTOPassManager(pm); - - // these are optimizations - if (unnecessaryForUs) { - pm.add(llvm::createGlobalDCEPass()); - } - pm.add(llvm::createTypeBasedAAWrapperPass()); - pm.add(llvm::createScopedNoAliasAAWrapperPass()); - if (unnecessaryForUs) { - pm.add(llvm::createForceFunctionAttrsLegacyPass()); - pm.add(llvm::createInferFunctionAttrsLegacyPass()); - pm.add(llvm::createCallSiteSplittingPass()); - pm.add(llvm::createPGOIndirectCallPromotionLegacyPass(true, false)); - } - pm.add(llvm::createIPSCCPPass()); - pm.add(llvm::createCalledValuePropagationPass()); // should follow IPSCCP - if (unnecessaryForUs) { - pm.add(llvm::createAttributorLegacyPass()); - pm.add(llvm::createPostOrderFunctionAttrsLegacyPass()); - pm.add(llvm::createReversePostOrderFunctionAttrsPass()); - pm.add(llvm::createGlobalSplitPass()); - } - - if (unnecessaryForUs) { - pm.add(llvm::createWholeProgramDevirtPass(nullptr, nullptr)); - } - pm.add(llvm::createGlobalOptimizerPass()); - pm.add(llvm::createPromoteMemoryToRegisterPass()); - if (unnecessaryForUs) { - pm.add(llvm::createConstantMergePass()); - } - pm.add(llvm::createDeadArgEliminationPass()); - if (unnecessaryForUs) { - pm.add(llvm::createAggressiveInstCombinerPass()); // O3 - pm.add(llvm::createInstructionCombiningPass(runExpensiveInstructionConbining)); - - pm.add(llvm::createFunctionInliningPass(optLevel, sizeLevel, false)); - pm.add(llvm::createPruneEHPass()); - pm.add(llvm::createGlobalOptimizerPass()); - pm.add(llvm::createGlobalDCEPass()); - } - pm.add(llvm::createArgumentPromotionPass()); - if (unnecessaryForUs) { - pm.add(llvm::createInstructionCombiningPass(runExpensiveInstructionConbining)); - pm.add(llvm::createJumpThreadingPass()); - pm.add(llvm::createSROAPass()); - pm.add(llvm::createTailCallEliminationPass()); - pm.add(llvm::createPostOrderFunctionAttrsLegacyPass()); - pm.add(llvm::createGlobalsAAWrapperPass()); - } - pm.add(llvm::createLICMPass(100, 250)); - if (unnecessaryForUs) { - pm.add(llvm::createMergedLoadStoreMotionPass()); - pm.add(llvm::createGVNPass(false)); - } - pm.add(llvm::createNewGVNPass()); // by default clang uses _old_ GVN - if (unnecessaryForUs) { - pm.add(llvm::createMemCpyOptPass()); - } - pm.add(llvm::createDeadStoreEliminationPass()); - pm.add(llvm::createIndVarSimplifyPass()); - if (unnecessaryForUs) { - pm.add(llvm::createLoopDeletionPass()); - pm.add(llvm::createSimpleLoopUnrollPass(2, false, false)); - pm.add(llvm::createLoopVectorizePass(false, false)); - } - pm.add(llvm::createLoopUnrollPass(2, false, false)); - pm.add(llvm::createInstructionCombiningPass(runExpensiveInstructionConbining)); - { - // print out the IR right before the sorbet lowerings - if (printLowered != nullptr) { - pm.add(printLowered); - } - - // Sorbet modifications, run our lowering super late in pipeline - // this allows other optimizations to move intrinsics around as black boxes and keep optimizing them under - // assumptions(that are marked with function attributes) - for (auto pass : Passes::standardLowerings()) { - pm.add(pass); - } - - { - pm.add(llvm::createFunctionInliningPass(optLevel, sizeLevel, false)); - pm.add(llvm::createPruneEHPass()); - pm.add(llvm::createGlobalOptimizerPass()); - pm.add(llvm::createGlobalDCEPass()); - pm.add(llvm::createInstructionCombiningPass(runExpensiveInstructionConbining)); - pm.add(llvm::createTailCallEliminationPass()); - // pm.add(llvm::createNewGVNPass()); // a single benchmark(fib) benefits from this. Wonder if pay-server - // does? - } - } - pm.add(llvm::createCFGSimplificationPass()); - pm.add(llvm::createSCCPPass()); - pm.add(llvm::createInstructionCombiningPass(runExpensiveInstructionConbining)); - if (unnecessaryForUs) { - pm.add(llvm::createBitTrackingDCEPass()); - } - pm.add(llvm::createSLPVectorizerPass()); - if (unnecessaryForUs) { - pm.add(llvm::createAlignmentFromAssumptionsPass()); - } - pm.add(llvm::createInstructionCombiningPass(runExpensiveInstructionConbining)); - pm.add(llvm::createJumpThreadingPass()); - - if (unnecessaryForUs) { - // non-optimization passes - pm.add(llvm::createCrossDSOCFIPass()); - } - if (unnecessaryForUs) { - pm.add(llvm::createLowerTypeTestsPass(nullptr, nullptr)); - } - - if (unnecessaryForUs) { - // late optimization passes - pm.add(llvm::createHotColdSplittingPass()); - pm.add(llvm::createCFGSimplificationPass()); - pm.add(llvm::createEliminateAvailableExternallyPass()); - } - pm.add(llvm::createGlobalDCEPass()); - // pm.add(llvm::createMergeFunctionsPass()); -} -}; // namespace - -void ObjectFileEmitter::init() { - // Initialize the target registry etc. - llvm::InitializeNativeTarget(); - llvm::InitializeNativeTargetAsmPrinter(); - llvm::InitializeNativeTargetAsmParser(); -} - -[[nodiscard]] bool ObjectFileEmitter::run(spdlog::logger &logger, llvm::LLVMContext &lctx, - unique_ptr module, string_view soDir, - optional llvmIrDir, string_view objectName) { - // We need to ensure that the codegen flags have been initialized, so that InitTargetOptionsFromCodeGenFlags has - // sane defaults to use. - static llvm::codegen::RegisterCodeGenFlags codeGenFlags; - - /* setup target */ - std::string error; - auto targetTriple = llvm::sys::getDefaultTargetTriple(); - auto target = llvm::TargetRegistry::lookupTarget(targetTriple, error); - - // Print an error and exit if we couldn't find the requested target. - // This generally occurs if we've forgotten to initialise the - // TargetRegistry or we have a bogus target triple. - if (!target) { - llvm::errs() << error; - return false; - } - - auto cpu = "skylake"; // this should probably not be hardcoded in future, but for now, this is what llc uses on - // mac and thus brings us closer to their assembly - auto features = ""; - - auto opt = llvm::codegen::InitTargetOptionsFromCodeGenFlags(llvm::Triple(targetTriple)); - auto relocationModel = llvm::Optional(llvm::Reloc::PIC_); - auto targetMachine = target->createTargetMachine(targetTriple, cpu, features, opt, relocationModel); - ENFORCE(targetMachine); - - // We encode this value in our `.exp` files right now so we have to hard - // code it to something that doesn't chance accross sytems. - // TODO stop putting it in our .exp files and then unhardcode this - // auto targetTriple = llvm::sys::getDefaultTargetTriple(); - - module->setTargetTriple(targetMachine->getTargetTriple().str()); - module->setDataLayout(targetMachine->createDataLayout()); - - /* run optimizations */ - - llvm::legacy::PassManager pm; - // print unoptimized IR - if (llvmIrDir.has_value()) { - llvm::legacy::PassManager ppm; - std::error_code ec; - auto name = fmt::format("{}/{}.ll", llvmIrDir.value(), objectName); - llvm::raw_fd_ostream llFile(name, ec, llvm::sys::fs::F_Text); - ppm.add(llvm::createPrintModulePass(llFile, "")); - ppm.run(*module); - } - - // add platform specific information - pm.add(new llvm::TargetLibraryInfoWrapperPass(targetMachine->getTargetTriple())); - pm.add(llvm::createTargetTransformInfoWrapperPass(targetMachine->getTargetIRAnalysis())); - - llvm::legacy::FunctionPassManager fnPasses(module.get()); - fnPasses.add(llvm::createTargetTransformInfoWrapperPass(targetMachine->getTargetIRAnalysis())); - - // enable optimizations - llvm::PassManagerBuilder pmbuilder; - pmbuilder.OptLevel = optLevel; - pmbuilder.SizeLevel = sizeLevel; - pmbuilder.Inliner = llvm::createFunctionInliningPass(optLevel, sizeLevel, false); - pmbuilder.DisableUnrollLoops = false; - pmbuilder.LoopVectorize = true; - pmbuilder.SLPVectorize = true; - pmbuilder.VerifyInput = debug_mode; - targetMachine->adjustPassManager(pmbuilder); - pmbuilder.populateFunctionPassManager(fnPasses); - std::error_code ec1; - // We need to run this early, prior to inlining, so the intrinsics to remove - // still exist in some fashion. - pm.add(Passes::createDeleteUnusedSorbetIntrinsticsPass()); - pm.add(Passes::createDeleteUnusedInlineCachesPass()); - pm.add(llvm::createAlwaysInlinerLegacyPass(false)); // Force inline functions early - pm.add(llvm::createGlobalDCEPass()); // Remove dead fns and globals. We benefit from this a lot - // Module passes - addModulePasses(pm); - pm.add(Passes::createRemoveUnnecessaryHashDupsPass()); - // print lowered IR - llvm::ModulePass *printLowered = nullptr; - // We put the ostream in an std::optional declared outside the "if", because unfortunately `createPrintModulePass` - // doesn't take ownership of it. - std::optional loweredllFile; - if (llvmIrDir.has_value()) { - auto nameOptl = fmt::format("{}/{}.lowered.ll", llvmIrDir.value(), objectName); - loweredllFile.emplace(nameOptl, ec1, llvm::sys::fs::F_Text); - printLowered = llvm::createPrintModulePass(loweredllFile.value(), ""); - } - // LTO passes - addLTOPasses(pm, printLowered); - // print optimized IR - // We put the ostream in an std::optional declared outside the "if", because unfortunately `createPrintModulePass` - // doesn't take ownership of it. - std::optional optllFile; - if (llvmIrDir.has_value()) { - auto nameOpt = fmt::format("{}/{}.opt.ll", llvmIrDir.value(), objectName); - optllFile.emplace(nameOpt, ec1, llvm::sys::fs::F_Text); - pm.add(llvm::createPrintModulePass(optllFile.value(), "")); - } - { - Timer timer(logger, "functionPasses"); - - fnPasses.doInitialization(); - for (llvm::Function &func : *module) { - if (debug_mode && llvm::verifyFunction(func, &llvm::errs())) { - fmt::print("failed to verify:\n"); - func.dump(); - ENFORCE(false); - } - - fnPasses.run(func); - } - fnPasses.doFinalization(); - } - if (debug_mode) { - pm.add(llvm::createVerifierPass()); - } - auto objectFileName = fmt::format("{}/{}.o", soDir, objectName); - auto soNamePrefix = fmt::format("{}/{}", soDir, objectName); - if (!outputObjectFile(logger, pm, string(objectName), objectFileName, move(module), targetMachine)) { - return false; - } - if (!Linker::run(logger, {objectFileName}, soNamePrefix)) { - return false; - } - FileOps::removeFile(objectFileName); - return true; -} - -} // namespace sorbet::compiler diff --git a/compiler/ObjectFileEmitter/ObjectFileEmitter.h b/compiler/ObjectFileEmitter/ObjectFileEmitter.h deleted file mode 100644 index bdc3a76608..0000000000 --- a/compiler/ObjectFileEmitter/ObjectFileEmitter.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef SORBET_COMPILER_OBJECT_FILE_EMITTER_H -#define SORBET_COMPILER_OBJECT_FILE_EMITTER_H - -#include -#include -#include -namespace spdlog { -class logger; -} -namespace llvm { -class LLVMContext; -class Module; -class BasicBlock; -} // namespace llvm - -namespace sorbet::compiler { -class ObjectFileEmitter { -public: - static void init(); - [[nodiscard]] static bool run(spdlog::logger &logger, llvm::LLVMContext &lctx, std::unique_ptr module, - std::string_view soDir, std::optional llvmIrDir, - std::string_view fileName); -}; -} // namespace sorbet::compiler -#endif diff --git a/compiler/Passes/BUILD b/compiler/Passes/BUILD deleted file mode 100644 index b3f37b323a..0000000000 --- a/compiler/Passes/BUILD +++ /dev/null @@ -1,18 +0,0 @@ -cc_library( - name = "Passes", - srcs = glob([ - "*.cc", - "*.h", - ]), - hdrs = ["Passes.h"], - linkstatic = select({ - "@com_stripe_ruby_typer//tools/config:linkshared": 0, - "//conditions:default": 1, - }), - visibility = ["//visibility:public"], - deps = [ - "//compiler/Core", - "//compiler/IREmitter/Payload", - "@spdlog", - ], -) diff --git a/compiler/Passes/Lowerings.cc b/compiler/Passes/Lowerings.cc deleted file mode 100644 index eb26f29ffb..0000000000 --- a/compiler/Passes/Lowerings.cc +++ /dev/null @@ -1,788 +0,0 @@ -// These violate our poisons so have to happen first -#include "llvm/IR/DerivedTypes.h" // FunctionType -#include "llvm/IR/IRBuilder.h" -#include "llvm/IR/InstVisitor.h" -#include "llvm/IR/MDBuilder.h" -#include "llvm/Pass.h" -#include "llvm/Transforms/Utils/BasicBlockUtils.h" - -#include "Passes.h" -#include "compiler/Core/OptimizerException.h" -#include "core/core.h" -#include - -using namespace std; -namespace sorbet::compiler { -namespace { - -class IRIntrinsic { -public: - virtual vector implementedFunctionCall() const = 0; - - // The contract you're expected to implement here is basically: - // - // - You're given an llvm::CallInst (which is a subclass of llvm::Value) that corresponds to the - // `call` instruction matching one of the names in implementedFunctionCall() - // - You can look at that instruction and all the rest of the `module` to do literally anything - // LLVM allows you to do. - // - You return a new `llvm::Value *` (like a cfg::Binding in Sorbet--contains the instruction and - // the variable to write that instruction into) - // - The framework will then update all reads from the variable of the `instr` assigns to to - // instead read from the variable of the `llvm::Value *` you just returned. - // - The frameowrk will delete `instr` from the module. This might unlock further optimization - // opportunities in later phases. - // - // You cannot (currently at least): - // - // - return `nullptr` - // - return `instr` unchanged - // - // We might change this (I don't know if this decision was intentional or accidental). - virtual llvm::Value *replaceCall(llvm::LLVMContext &lctx, llvm::Module &module, llvm::CallInst *instr) const = 0; - - virtual ~IRIntrinsic() = default; -}; - -// TODO: add more from https://git.corp.stripe.com/stripe-internal/ruby/blob/48bf9833/include/ruby/ruby.h#L1962. Will -// need to modify core sorbet for it. -const vector> knownSymbolMapping = { - {"Array", "rb_cArray"}, - {"BasicObject", "rb_cBasicObject"}, - {"Class", "rb_cClass"}, - {"Comparable", "rb_mComparable"}, - {"Enumerable", "rb_mEnumerable"}, - {"FalseClass", "rb_cFalseClass"}, - {"Float", "rb_cFloat"}, - {"Hash", "rb_cHash"}, - {"Integer", "rb_cInteger"}, - {"Kernel", "rb_mKernel"}, - {"Method", "rb_cMethod"}, - {"Module", "rb_cModule"}, - {"NameError", "rb_eNameError"}, - {"NilClass", "rb_cNilClass"}, - {"NoMethodError", "rb_eNoMethodError"}, - {"Numeric", "rb_cNumeric"}, - {"Object", "rb_cObject"}, - {"Proc", "rb_cProc"}, - {"Range", "rb_cRange"}, - {"Rational", "rb_cRational"}, - {"Regexp", "rb_cRegexp"}, - {"StandardError", "rb_eStandardError"}, - {"String", "rb_cString"}, - {"Struct", "rb_cStruct"}, - {"Symbol", "rb_cSymbol"}, - {"Thread", "rb_cThread"}, - {"TrueClass", "rb_cTrueClass"}, -}; - -class ClassAndModuleLoading : public IRIntrinsic { -public: - virtual vector implementedFunctionCall() const override { - return {"sorbet_i_getRubyClass", "sorbet_i_getRubyConstant"}; - } - - virtual llvm::Value *replaceCall(llvm::LLVMContext &lctx, llvm::Module &module, - llvm::CallInst *instr) const override { - auto elemPtr = llvm::dyn_cast(instr->getArgOperand(0)); - if (elemPtr == nullptr) { - throw OptimizerException("Unexpected argument to intrinsic"); - } - auto stringLength = llvm::dyn_cast(instr->getArgOperand(1)); - if (stringLength == nullptr) { - throw OptimizerException("Unexpected argument to intrinsic"); - } - - llvm::APInt apOffset{64, 0}; - bool success = elemPtr->accumulateConstantOffset(module.getDataLayout(), apOffset); - if (!success) { - throw OptimizerException("Cannot determine constant offset for intrinsic"); - } - uint64_t offset = apOffset.getLimitedValue(); - if (offset == UINT64_MAX) { - throw OptimizerException("Too big of an offset for intrinsic"); - } - - uint64_t length = stringLength->getZExtValue(); - - auto global = llvm::dyn_cast(elemPtr->getOperand(0)); - - if (global == nullptr) { - throw OptimizerException("Unexpected argument to intrinsic"); - } - auto initializer = llvm::dyn_cast(global->getInitializer()); - if (initializer == nullptr) { - throw OptimizerException("Unexpected argument to intrinsic"); - } - - llvm::IRBuilder<> builder(instr); - - auto initializerString = initializer->getAsString(); - auto symName = initializerString.substr(offset, length); - auto tp = llvm::Type::getInt64Ty(lctx); - - for (const auto &[rubySourceName, rubyCApiName] : knownSymbolMapping) { - if (symName == rubySourceName) { - auto &nm = rubyCApiName; // C++ bindings don't play well with captures - auto globalDeclaration = module.getOrInsertGlobal(rubyCApiName, tp, [&] { - auto isConstant = true; - llvm::Constant *initializer = nullptr; - auto ret = new llvm::GlobalVariable(module, tp, isConstant, llvm::GlobalVariable::ExternalLinkage, - initializer, nm); - return ret; - }); - return builder.CreateLoad(globalDeclaration); - } - } - string str(symName); - ENFORCE(str.length() < 2 || (str[0] != ':'), "implementation assumes that strings dont start with ::"); - auto loaderName = "const_load" + str; - auto guardEpochName = "guard_epoch_" + str; - auto guardedConstName = "guarded_const_" + str; - - auto guardEpochDeclaration = module.getOrInsertGlobal(guardEpochName, tp, [&] { - auto isConstant = false; - auto ret = new llvm::GlobalVariable(module, tp, isConstant, llvm::GlobalVariable::LinkOnceAnyLinkage, - llvm::ConstantInt::get(tp, 0), guardEpochName); - return ret; - }); - - auto guardedConstDeclaration = module.getOrInsertGlobal(guardedConstName, tp, [&] { - auto isConstant = false; - auto ret = new llvm::GlobalVariable(module, tp, isConstant, llvm::GlobalVariable::LinkOnceAnyLinkage, - llvm::ConstantInt::get(tp, 0), guardedConstName); - return ret; - }); - - auto needTakeSlowPath = - builder.CreateICmpNE(builder.CreateLoad(guardEpochDeclaration), - builder.CreateCall(module.getFunction("sorbet_getConstantEpoch")), "needTakeSlowPath"); - - auto splitPoint = builder.CreateLoad(guardedConstDeclaration); - builder.CreateIntrinsic( - llvm::Intrinsic::IndependentIntrinsics::assume, {}, - {builder.CreateICmpEQ(builder.CreateLoad(guardEpochDeclaration), - builder.CreateCall(module.getFunction("sorbet_getConstantEpoch")), "guardUpdated")} - - ); - - auto slowPathTerm = llvm::SplitBlockAndInsertIfThen(needTakeSlowPath, splitPoint, false, - llvm::MDBuilder(lctx).createBranchWeights(1, 10000)); - builder.SetInsertPoint(slowPathTerm); - auto recomputeFunName = "const_recompute_" + str; - auto recomputeFun = module.getFunction(recomputeFunName); - if (!recomputeFun) { - // generate something logically similar to - // VALUE const_load_FOO() { - // if (const_guard == constant_epoch()) { - // return guarded_const; - // } else { - // guardedConst = sorbet_getConstant("FOO"); - // const_guard = constant_epoch(); - // return guarded_const; - // } - // - // It's not exactly this as I'm doing some tunnings, more specifically, the "else" branch will be marked - // cold and shared across all modules - - auto recomputeFunT = llvm::FunctionType::get(llvm::Type::getVoidTy(lctx), {}, false /*not varargs*/); - recomputeFun = llvm::Function::Create(recomputeFunT, llvm::Function::LinkOnceAnyLinkage, - llvm::Twine("const_recompute_") + str, module); - llvm::IRBuilder<> functionBuilder(lctx); - - functionBuilder.SetInsertPoint(llvm::BasicBlock::Create(lctx, "", recomputeFun)); - - auto origIndices = elemPtr->idx_begin(); - llvm::Constant *indicesString[] = {llvm::dyn_cast(&**origIndices), - llvm::dyn_cast(&**(origIndices + 1))}; - functionBuilder.CreateStore( - functionBuilder.CreateCall( - module.getFunction("sorbet_getConstant"), - {llvm::ConstantExpr::getInBoundsGetElementPtr(global->getValueType(), global, indicesString), - llvm::ConstantInt::get(lctx, llvm::APInt(64, str.length()))}), - guardedConstDeclaration); - functionBuilder.CreateStore(functionBuilder.CreateCall(module.getFunction("sorbet_getConstantEpoch")), - guardEpochDeclaration); - functionBuilder.CreateRetVoid(); - } - builder.CreateCall(recomputeFun); - return splitPoint; - } -} ClassAndModuleLoading; - -class ObjIsKindOf : public IRIntrinsic { - llvm::Value *fallbackToVM(llvm::Module &module, llvm::CallInst *instr) const { - llvm::IRBuilder<> builder(instr); - - auto *res = builder.CreateCall(module.getFunction("rb_obj_is_kind_of"), - {instr->getArgOperand(0), instr->getArgOperand(1)}); - - auto *isTrueClass = module.getFunction("sorbet_isa_TrueClass"); - ENFORCE(isTrueClass != nullptr); - return builder.CreateCall(isTrueClass, {res}); - } - -public: - virtual vector implementedFunctionCall() const override { - return {"sorbet_i_objIsKindOf"}; - } - - // Detects code that looks like this: - // - // %44 = load i64, i64* @rb_cNilClass, align 8, !dbg !14 - // %45 = load i64, i64* @rb_cModule, align 8, !dbg !14 - // %46 = call i64 @sorbet_i_objIsKindOf(i64 %44, i64 %45) #1, !dbg !14 - // - // and returns an instruction that looks like this: - // - // %47 = call i64 @sorbet_rubyTrue() - // - // Then the LowerIntrinsicsPass harness below will update all reads from %46 to read from %47 - // instead and then delete the write to %46 entirely (which might unlock other optimizations). - virtual llvm::Value *replaceCall(llvm::LLVMContext &lctx, llvm::Module &module, - llvm::CallInst *instr) const override { - auto valueLoad = llvm::dyn_cast(instr->getArgOperand(0)); - auto kindLoad = llvm::dyn_cast(instr->getArgOperand(1)); - - if (valueLoad == nullptr || kindLoad == nullptr) { - return this->fallbackToVM(module, instr); - } - - auto value = llvm::dyn_cast(valueLoad->getPointerOperand()); - auto kind = llvm::dyn_cast(kindLoad->getPointerOperand()); - - if (value == nullptr || kind == nullptr) { - return this->fallbackToVM(module, instr); - } - - llvm::IRBuilder<> builder(instr); - - auto kindName = kind->getName(); - if (kindName != "rb_cModule") { - return this->fallbackToVM(module, instr); - } - - auto valueName = value->getName(); - for (const auto &[_rubySourceName, rubyCApiName] : knownSymbolMapping) { - if (valueName == rubyCApiName) { - return llvm::ConstantInt::getTrue(lctx); - } - } - - return this->fallbackToVM(module, instr); - } -} ObjIsKindOf; - -class TypeTest : public IRIntrinsic { - static const vector> intrinsicMap; - -public: - virtual vector implementedFunctionCall() const override { - vector methods; - for (auto &[intrinsic, realMethod] : intrinsicMap) { - methods.emplace_back(intrinsic); - } - return methods; - } - - virtual llvm::Value *replaceCall(llvm::LLVMContext &lctx, llvm::Module &module, - llvm::CallInst *instr) const override { - llvm::IRBuilder<> builder(instr); - auto *arg = instr->getArgOperand(0); - auto name = instr->getCalledFunction()->getName(); - - auto realMethod = absl::c_find_if(intrinsicMap, [&name](const auto &pair) { return pair.first == name; }); - ENFORCE(realMethod != intrinsicMap.end()); - - return builder.CreateCall(module.getFunction(realMethod->second), {arg}); - } - -} TypeTest; - -const vector> TypeTest::intrinsicMap{ - {"sorbet_i_isa_Array", "sorbet_isa_Array"}, {"sorbet_i_isa_Integer", "sorbet_isa_Integer"}, - {"sorbet_i_isa_TrueClass", "sorbet_isa_TrueClass"}, {"sorbet_i_isa_FalseClass", "sorbet_isa_FalseClass"}, - {"sorbet_i_isa_NilClass", "sorbet_isa_NilClass"}, {"sorbet_i_isa_Symbol", "sorbet_isa_Symbol"}, - {"sorbet_i_isa_Float", "sorbet_isa_Float"}, {"sorbet_i_isa_Untyped", "sorbet_isa_Untyped"}, - {"sorbet_i_isa_Hash", "sorbet_isa_Hash"}, {"sorbet_i_isa_Array", "sorbet_isa_Array"}, - {"sorbet_i_isa_Regexp", "sorbet_isa_Regexp"}, {"sorbet_i_isa_Rational", "sorbet_isa_Rational"}, - {"sorbet_i_isa_String", "sorbet_isa_String"}, {"sorbet_i_isa_Proc", "sorbet_isa_Proc"}, - {"sorbet_i_isa_Thread", "sorbet_isa_Thread"}, {"sorbet_i_isa_RootSingleton", "sorbet_isa_RootSingleton"}, -}; - -class SorbetSend : public IRIntrinsic { -public: - virtual vector implementedFunctionCall() const override { - return {"sorbet_i_send"}; - } - - // Original ruby code: - // puts "a" - // - // Detects code that looks like this: - // - // %3 = call ... @sorbet_i_send(%struct.FunctionInlineCache* @ic_puts, - // i1 false, - // %struct.vm_ifunc* null, - // %struct.rb_control_frame_struct* %cfp, - // i64 %selfRaw, - // i64 %rubyStr_a - // ), !dbg !121 - // - // and replaces it with: - // - // (disabling clang-format here because it will otherwise remove all the newlines) - // clang-format off - // (1) %18 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %11, i64 0, i32 1, !dbg !8 - // (2) %19 = load i64*, i64** %18, align 8, !dbg !8 - // (3) store i64 %8, i64* %19, align 8, !dbg !8, !tbaa !4 - // (4) %20 = getelementptr inbounds i64, i64* %19, i64 1, !dbg !8 - // (5) store i64 %rubyStr_a.i, i64* %20, align 8, !dbg !8, !tbaa !4 - // (6) %21 = getelementptr inbounds i64, i64* %20, i64 1, !dbg !8 - // (7) store i64* %21, i64** %18, align 8, !dbg !8 - // (8) %send = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts, i64 0), !dbg !8 - // clang-format on - // - // Lines 1 & 2 correspond to the sorbet_get_sp call, and the sp load from spPtr - // Lines 3 & 4 correspond to the sorbet_pushValueStack call for self, while 5 & 6 correspond to the - // sorbet_pushValueStack call for "a". - // Line 7 corresponds to the store to spPtr. - // Line 8 corresponds to the sorbet_callFuncWithCache call. - virtual llvm::Value *replaceCall(llvm::LLVMContext &lctx, llvm::Module &module, - llvm::CallInst *instr) const override { - // Make sure cache, blkUsesBreak, blk, searchSuper, cfp and self are passed in. - const int recvArgOffset = 5; - ENFORCE(instr->arg_size() >= (recvArgOffset + 1)); - - llvm::IRBuilder<> builder(instr); - auto *cache = instr->getArgOperand(0); - auto *blkIfunc = instr->getArgOperand(2); - auto *cfp = instr->getArgOperand(4); - - auto *spPtr = builder.CreateCall(module.getFunction("sorbet_get_sp"), {cfp}); - auto spPtrType = llvm::dyn_cast(spPtr->getType()); - llvm::Value *sp = builder.CreateLoad(spPtrType->getElementType(), spPtr); - for (auto iter = std::next(instr->arg_begin(), recvArgOffset); iter < instr->arg_end(); ++iter) { - sp = builder.CreateCall(module.getFunction("sorbet_pushValueStack"), {sp, iter->get()}); - } - builder.CreateStore(sp, spPtr); - - auto *searchSuper = llvm::dyn_cast(instr->getArgOperand(3)); - ENFORCE(searchSuper); - bool usesSuper = searchSuper->equalsInt(1); - - if (llvm::isa(blkIfunc)) { - llvm::StringRef func = usesSuper ? llvm::StringRef("sorbet_callSuperFuncWithCache") - : llvm::StringRef("sorbet_callFuncWithCache"); - auto *blockHandler = - builder.CreateCall(module.getFunction("sorbet_vmBlockHandlerNone"), {}, "VM_BLOCK_HANDLER_NONE"); - return builder.CreateCall(module.getFunction(func), {cache, blockHandler}, "send"); - } else { - auto *blkUsesBreak = llvm::dyn_cast(instr->getArgOperand(1)); - ENFORCE(blkUsesBreak); - bool usesBreak = blkUsesBreak->equalsInt(1); - - llvm::StringRef func; - if (usesSuper) { - func = usesBreak ? llvm::StringRef("sorbet_callSuperFuncBlockWithCache") - : llvm::StringRef("sorbet_callSuperFuncBlockWithCache_noBreak"); - } else { - func = usesBreak ? llvm::StringRef("sorbet_callFuncBlockWithCache") - : llvm::StringRef("sorbet_callFuncBlockWithCache_noBreak"); - } - - auto *callImpl = module.getFunction(func); - return builder.CreateCall(callImpl, {cache, blkIfunc}, "sendWithBlock"); - } - } -} SorbetSend; - -vector getIRIntrinsics() { - vector irIntrinsics{ - &ClassAndModuleLoading, - &ObjIsKindOf, - &TypeTest, - &SorbetSend, - }; - - return irIntrinsics; -} -static const vector irIntrinsics = getIRIntrinsics(); - -class LowerIntrinsicsPass : public llvm::ModulePass { -public: - // The contents of this variable don't matter; LLVM just uses the pointer address of it as an ID. - static char ID; - LowerIntrinsicsPass() : llvm::ModulePass(ID){}; - - struct CallInstVisitor : public llvm::InstVisitor { - vector result; - UnorderedSet lookups; - - void visitCallInst(llvm::CallInst &ci) { - auto maybeFunc = ci.getCalledFunction(); - if (maybeFunc == nullptr) { - return; - } - - auto fnd = lookups.find(maybeFunc); - if (fnd == lookups.end()) { - return; - } - - // We're taking the address of a llvm::CallInst & here to avoid having to deal with a - // vector of references. - // - // This lives at least long enough (owned by the llvm::Module) to let the visitor finish - // and also run the replaceCall callback. - // - // An alternative here would be to run the replaceCall callback and do the replacements - // right here, but it's probably not great to have a visitor that simultaneously mutates - // the structure it's visiting, which is why we accumulate a to-do list of results. - result.emplace_back(&ci); - } - }; - - virtual bool runOnModule(llvm::Module &mod) override { - vector functionsToRemove; - - for (auto &fn : mod.functions()) { - if (fn.getName().startswith("sorbet_exists_to_keep_alive_")) { - functionsToRemove.emplace_back(&fn); - } - } - - for (auto *fn : functionsToRemove) { - fn->eraseFromParent(); - } - - // We run each lowering pass in sequence. Each does a full visit on the module. - // We don't have that many lowering passes right now, so this cost is small. - // - // When we get around to optimizing compile-time performance in the future, this might need to change. - // At the very least, it should be easy enough to schedule `ClassAndModuleLoading` and `ObjIsKindOf` - // to run in sequence **at each call site**, but still in one `llvm::ModulePass`. - for (const auto *intrinsic : irIntrinsics) { - CallInstVisitor visitor; - - for (const auto &name : intrinsic->implementedFunctionCall()) { - if (auto fun = mod.getFunction(name)) { - visitor.lookups.insert(fun); - } - } - - visitor.visit(mod); - - for (const auto &callInst : visitor.result) { - auto newRes = intrinsic->replaceCall(mod.getContext(), mod, callInst); - callInst->replaceAllUsesWith(newRes); - callInst->eraseFromParent(); - } - } - - return true; - }; - - virtual ~LowerIntrinsicsPass() = default; -} LowerInrinsicsPass; -char LowerIntrinsicsPass::ID = 0; - -static llvm::RegisterPass X("lowerSorbetIntrinsics", "Lower Sorbet Intrinsics", - false, // Only looks at CFG - false // Analysis Pass -); - -// This pass is kind of a hack. There are certain calls that we eagerly generate -// because it makes code generation simpler. But after code generation, those -// calls may not actually be used and we know -- but LLVM doesn't -- that the -// calls are safe to delete. There's not an LLVM attribute to describe this sort -// of behavior, so we have to write our own pass. -class DeleteUnusedSorbetIntrinsicsPass : public llvm::ModulePass { -public: - static char ID; - - DeleteUnusedSorbetIntrinsicsPass() : llvm::ModulePass(ID) {} - - struct CallInstVisitor : public llvm::InstVisitor { - vector callsToDelete; - UnorderedSet functionsToDelete; - - CallInstVisitor(llvm::Module &m) { - auto f = m.getFunction("sorbet_getMethodBlockAsProc"); - ENFORCE(f); - functionsToDelete.insert(f); - } - - void visitCallInst(llvm::CallInst &ci) { - auto maybeFunc = ci.getCalledFunction(); - if (!maybeFunc) { - return; - } - - if (!functionsToDelete.contains(maybeFunc)) { - return; - } - - if (!ci.use_empty()) { - return; - } - - callsToDelete.emplace_back(&ci); - } - }; - - virtual bool runOnModule(llvm::Module &mod) override { - CallInstVisitor visitor(mod); - ENFORCE(!visitor.functionsToDelete.empty()); - - visitor.visit(mod); - - if (visitor.callsToDelete.empty()) { - return false; - } - - for (auto inst : visitor.callsToDelete) { - inst->eraseFromParent(); - } - - return true; - } -}; -char DeleteUnusedSorbetIntrinsicsPass::ID = 0; - -static llvm::RegisterPass Y("deleteUnusuedSorbetIntrinsics", - "Delete Unused Sorbet Intrinsics", - false, // Only looks at CFG - false // Analysis Pass -); - -// When through optimization llvm is able to prune out sends that go through the VM, the inline cache that they -// allocated will still be initialized during the module's Init function, and the global will persist. This pass detects -// this case by finding globals whose type is `struct FunctionInlineCache`, and that only have a single user which is a -// call to the `sorbet_setupFunctionInlineCache` function. -class DeleteUnusedInlineCachesPass : public llvm::ModulePass { -public: - static char ID; - - DeleteUnusedInlineCachesPass() : llvm::ModulePass(ID) {} - - virtual bool runOnModule(llvm::Module &mod) override { - auto *setupInlineCacheFun = mod.getFunction("sorbet_setupFunctionInlineCache"); - if (setupInlineCacheFun == nullptr) { - // Unlikely, but this would mean that there were no uses of the function, and that it was removed. If this - // function is missing, there would be no inline caches allocated in this module. - return false; - } - - auto *inlineCacheType = llvm::StructType::getTypeByName(mod.getContext(), "struct.FunctionInlineCache"); - ENFORCE(inlineCacheType != nullptr); - - std::vector toRemove; - for (auto &global : mod.globals()) { - if (global.getValueType() != inlineCacheType) { - continue; - } - - int numUses = std::distance(global.user_begin(), global.user_end()); - if (numUses != 1) { - continue; - } - - auto *inst = llvm::dyn_cast(global.user_back()); - if (inst == nullptr || inst->getCalledFunction() != setupInlineCacheFun) { - continue; - } - - toRemove.emplace_back(inst); - } - - if (toRemove.empty()) { - return false; - } - - for (auto *inst : toRemove) { - inst->eraseFromParent(); - } - - return true; - } -}; - -char DeleteUnusedInlineCachesPass::ID = 0; - -static llvm::RegisterPass Z("deleteUnusuedInlineCaches", "Delete Unused Inline Caches", - false, // Only looks at CFG - false // Analysis Pass -); - -class RemoveUnnecessaryHashDupsPass : public llvm::ModulePass { -public: - static char ID; - - RemoveUnnecessaryHashDupsPass() : llvm::ModulePass(ID) {} - - bool detectLiteralHash(llvm::Module &mod, llvm::Function *sorbetGlobalConstDupHashFn, llvm::CallInst *toHashCall) { - // Checks that the argument to rb_to_hash_type is the result of a sorbet_globalConstDupHash, - // and that it only has one user. - // This is the case for code written as follows: - // args = { a: 1 } - // foo(**args) - ENFORCE(toHashCall->getNumArgOperands() == 1); - auto *constDupCall = llvm::dyn_cast(toHashCall->getOperand(0)); - if (constDupCall == nullptr || constDupCall->getCalledFunction() != sorbetGlobalConstDupHashFn || - constDupCall->getParent() != toHashCall->getParent() || !constDupCall->hasOneUser()) { - return false; - } - - return true; - } - - bool detectPassThroughCase(llvm::Module &mod, llvm::Function *rbHashDupFn, llvm::Function *rbHashNewFn, - llvm::CallInst *toHashCall) { - // Checks that the argument to rb_to_hash_type is the result of a phi, - // where one branch is a call to rb_hash_new, and the other is a call to - // rb_hash_dup on the result of a load from the arg array. - // This is the case for code written as follows: - // def foo(**args) - // bar(**args) - // end - - auto *phiNode = llvm::dyn_cast(toHashCall->getOperand(0)); - if (phiNode == nullptr || phiNode->getParent() != toHashCall->getParent() || !phiNode->hasOneUser()) { - return false; - } - for (auto &u : phiNode->incoming_values()) { - auto *phiArg = llvm::dyn_cast(u); - if (phiArg == nullptr) { - return false; - } - // If we see a Hash.new, we can skip processing this branch of the phi, since there's nothing else to follow - // backwards. - if (phiArg->getCalledFunction() == rbHashNewFn) { - continue; - } - // If we see a function other than Hash.new or rb_hash_dup, we should abort, and not do anything, - // because the expected phi node only has calls to those 2. - if (phiArg->getCalledFunction() != rbHashDupFn) { - return false; - } - - ENFORCE(phiArg->getNumArgOperands() == 1); - auto *loadInst = llvm::dyn_cast(phiArg->getOperand(0)); - if (loadInst == nullptr) { - return false; - } - auto *gepInst = llvm::dyn_cast(loadInst->getOperand(0)); - if (gepInst == nullptr) { - return false; - } - // Check that the operand to the GEP is the 2nd arg to the function. - // gepInst->getParent() returns the basic block the GEP is part of, - // and ->getParent() of that returns the function. - if (gepInst->getOperand(0) != gepInst->getParent()->getParent()->getArg(1)) { - return false; - } - } - return true; - } - - bool runOnModule(llvm::Module &mod) override { - bool modifiedCode = false; - - auto *rbHashDupFn = mod.getFunction("rb_hash_dup"); - auto *rbHashNewFn = mod.getFunction("rb_hash_new"); - auto *rbToHashTypeFn = mod.getFunction("rb_to_hash_type"); - auto *sorbetGlobalConstDupHashFn = mod.getFunction("sorbet_globalConstDupHash"); - auto *sorbetSendFn = mod.getFunction("sorbet_i_send"); - - if (rbHashDupFn == nullptr || rbToHashTypeFn == nullptr || sorbetSendFn == nullptr) { - return false; - } - - for (auto &function : mod) { - for (auto &block : function) { - for (auto insn_iter = block.begin(); insn_iter != block.end();) { - llvm::Instruction *insn = &*insn_iter; - // This increment needs to happen outside of the loop header, because we are - // potentially deleting this instruction in this loop body, so incrementing at - // the end of the loop body could fail. - insn_iter++; - - // Look for the following instructions: - // %2 = call i64 @rb_to_hash_type(i64 %1) - // %3 = call i64 @rb_hash_dup(i64 %2) - // And ensure that both %2 and %3 have exactly one user, and are in the same basic block. - auto *hashDupCall = llvm::dyn_cast(insn); - if (hashDupCall == nullptr || hashDupCall->getCalledFunction() != rbHashDupFn || - !hashDupCall->hasOneUser()) { - continue; - } - ENFORCE(hashDupCall->getNumArgOperands() == 1); - auto *toHashCall = llvm::dyn_cast(hashDupCall->getOperand(0)); - if (toHashCall == nullptr || toHashCall->getCalledFunction() != rbToHashTypeFn || - toHashCall->getParent() != hashDupCall->getParent() || !toHashCall->hasOneUser()) { - continue; - } - - // Get the use for the rb_hash_dup value, and ensure that it is a send, in the same basic block. - auto use = hashDupCall->use_begin(); - llvm::User *user = use->getUser(); - auto *sendCall = llvm::dyn_cast(user); - if (sendCall == nullptr || sendCall->getCalledFunction() != sorbetSendFn || - sendCall->getParent() != hashDupCall->getParent()) { - continue; - } - - // Check that the rb_hash_dup is the last arg to send, - // The last operand is the definition, - // which means that getNumOperands - getOperandNo == 2 - if (user->getNumOperands() - use->getOperandNo() != 2) { - continue; - } - - bool shouldRemoveDup = false; - if (sorbetGlobalConstDupHashFn && detectLiteralHash(mod, sorbetGlobalConstDupHashFn, toHashCall)) { - shouldRemoveDup = true; - } - if (rbHashNewFn && detectPassThroughCase(mod, rbHashDupFn, rbHashNewFn, toHashCall)) { - shouldRemoveDup = true; - } - if (!shouldRemoveDup) { - continue; - } - - user->setOperand(use->getOperandNo(), toHashCall); - hashDupCall->eraseFromParent(); - modifiedCode = true; - } - } - } - return modifiedCode; - } -}; - -char RemoveUnnecessaryHashDupsPass::ID = 0; - -static llvm::RegisterPass AA("removeUnnecessaryHashDupsPass", - "Remove Unnecessary Hash Dups", - false, // Only looks at CFG - false // Analysis Pass -); - -} // namespace -const std::vector Passes::standardLowerings() { - // LLVM pass manager is going to destuct them, so we need to allocate them every time - return {new LowerIntrinsicsPass()}; -}; - -llvm::ModulePass *Passes::createDeleteUnusedSorbetIntrinsticsPass() { - return new DeleteUnusedSorbetIntrinsicsPass(); -} - -llvm::ModulePass *Passes::createDeleteUnusedInlineCachesPass() { - return new DeleteUnusedInlineCachesPass(); -} - -llvm::ModulePass *Passes::createRemoveUnnecessaryHashDupsPass() { - return new RemoveUnnecessaryHashDupsPass(); -} -} // namespace sorbet::compiler diff --git a/compiler/Passes/Passes.h b/compiler/Passes/Passes.h deleted file mode 100644 index 2c99a9da93..0000000000 --- a/compiler/Passes/Passes.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef SORBET_COMPILER_PASSES_H -#define SORBET_COMPILER_PASSES_H -#include -#include - -namespace llvm { -class ModulePass; -}; // namespace llvm -namespace sorbet::compiler { -class Passes { -public: - static llvm::ModulePass *createDeleteUnusedSorbetIntrinsticsPass(); - static llvm::ModulePass *createDeleteUnusedInlineCachesPass(); - static llvm::ModulePass *createRemoveUnnecessaryHashDupsPass(); - static const std::vector standardLowerings(); -}; -} // namespace sorbet::compiler -#endif diff --git a/compiler/README.md b/compiler/README.md deleted file mode 100644 index 94c838a564..0000000000 --- a/compiler/README.md +++ /dev/null @@ -1,369 +0,0 @@ -

- Sorbet Compiler logo -

- -# The Sorbet Compiler - -This repository contains the Sorbet compiler. It uses Sorbet and LLVM to emit -shared objects that conform to Ruby's C extension API given Ruby source files. - -It also contains Sorbet Ruby, a set of scripts that build Ruby in a specific way -so that it can run shared objects that the Sorbet Compiler emits. - -This README contains documentation specifically for contributing to the compiler -portion of Sorbet. - -If you are at Stripe, you might also want to see for -docs about Stripe-specific development workflows and historical Stripe context. - - - -## Table of Contents - -- [Quickstart](#quickstart) -- [Learning how the Sorbet compiler works](#learning-how-the-sorbet-compiler-works) -- [Running Sorbet Ruby and the Sorbet compiler](#running-sorbet-ruby-and-the-sorbet-compiler) -- [Running the tests](#running-the-tests) -- [Writing tests](#writing-tests) - - [Comparing LLVM IR](#comparing-llvm-ir) - - [Comparing stderr](#comparing-stderr) -- [Debugging](#debugging) - - [`.lldbinit`](#lldbinit) - - [Debugging the Ruby VM](#debugging-the-ruby-vm) -- [C++ in the compiler](#c-in-the-compiler) -- [Editor and environment](#editor-and-environment) - - - -## Quickstart - -For the best chance of success: - -- Run `sudo apt-get install libncurses-dev libssl-dev` - -Then run: - -```bash -./bazel test //test:compiler -``` - -This will take a long time, as it's downloading everything and building -everything (including LLVM) from scratch. - -> The Sorbet Compiler is tested on a very narrow set of machines and rarely -> built from scratch. It's likely that the first time you try to build it on a -> new machine you will encounter problems, and need to either install certain -> system-wide dependencies, or switch operating systems. - - -## Learning how the Sorbet Compiler works - -TODO(jez) compiler internals - - -## Running Sorbet Ruby and the Sorbet Compiler - -For a more complete walk through, see -[docs/running-compiled-code.md](/docs/running-compiled-code.md). - -There's a fair amount of set up before you can run Sorbet Ruby on a file or load -a compiled Ruby file: - -- Using `sig` and `T` depends on `sorbet-runtime` -- Compiling one or more Ruby files requires a folder for the LLVM IR and `*.so` - files. -- Running a compiled Ruby file requires passing that folder and also including - some Ruby monkeypatches to `require` find the artifacts. - -There are some short scripts that handle all this bookkeeping: - -```bash -# Interpret a file with Sorbet Ruby -test/run_ruby.sh [-d] test/testdata/compiler/hello.rb - -# Compile a file with the Sorbet compiler -test/run_sorbet.sh [-d] test/testdata/compiler/hello.rb - -# Compile and run a file -test/run_compiled.sh [-d] test/testdata/compiler/hello.rb -``` - -The optional `-d` flag to these scripts will launch the important command for -that script in `lldb` with all the right args, so you can set breakpoints before -running it. - - -## Running the tests - -To run all the tests: - -``` -bazel test //test:compiler //test/cli/compiler --config=dbg -``` - -By default, all test output goes into files. To also print it to the screen: - -``` -bazel test //test:compiler //test/cli/compiler --config=dbg --test_output=errors -``` - -If any test failed, you will see two pieces of information printed: - -``` -1. //test:test_testdata/compiler/hello FAILED in 0.1s -2. /home/jez/.cache/bazel/.../test/test_testdata/compiler/hellow/test.log -``` - -1. the test's target (in case you want to run just this test again with `bazel - test `) -2. a (runnable) file containing the test's output - -To see the failing output, either: - -- Re-run `bazel test` with the `--test_output=errors` flag -- Copy/paste the `*.log` file and run it (the output will open in `less`) - - -## Writing tests - -We write tests by adding files to subfolders of the `test/` directory. -Every compiler test works like this: - -- Write a Ruby file -- Interpret the Ruby file with Sorbet Ruby -- Compile and run the compiled output -- Compare the resulting exit codes, stdouts, and stderrs (they must match to - pass) - -These Ruby file tests live in `test/testdata/`. - -Thus, it's very important to **print things** in tests. If a test doesn't print -anything, then both the interpreted and compiled output will be empty and the -test will pass even if the compiler did something completely wrong. - -### Verifying LLVM IR - -We rely on LLVM's -[FileCheck](https://llvm.org/docs/CommandGuide/FileCheck.html) to verify -that the structure of the generated IR conforms to our expectations. If you -are writing a test where you would like to verify the structure of the -generated code, you need to add a `# run_filecheck: ` comment to the -file. This comment will typically be added in the top header block of comments. - -`` in the above comment can be one of three values: - -- `INITIAL`, for verifying the LLVM IR immediately after it is generated by the Sorbet compiler; -- `LOWERED`, for verifying the LLVM IR after the Sorbet compiler's custom lowerings have been run; -- `OPT`, for verifying the LLVM IR after LLVM's suite of optimizations has been run. - -`` corresponds to the value given to the `-check-prefix` option of FileCheck. - -Please note that FileCheck will verify that your checks occur in the same -order in the LLVM IR file as they are written in the test file. So, as an example, -if a file contains: - -``` -# OPT-LABEL: "func_Object#foo" -# OPT-NOT: call i64 @sorbet_getMethodBlockAsProc -# OPT: call i64 @rb_yield_values_kw -# OPT-NOT: call i64 @sorbet_getMethodBlockAsProc -# OPT{LITERAL}: } -``` - -The following checks will be run in order on the optimized LLVM IR: - -- `"func_Object#foo"` appears exactly once in the file; -- `call i64 @sorbet_getMethodBlockAsProc` does not appear between the above line and the next check; -- `call i64 @rb_yield_values_kw` appears; -- `call i64 @sorbet_getMethodBlockAsProc` does not appear between the previous line and the next check; -- a literal `}` appears on a line. - -The first check and the last check are intended to limit the scope of the -contained checks to a single function definition. The quotes in the first -check are intended to limit the check to the actual function definition and -avoid matching ancilliary data associated with the function. If we used -`INITIAL-LABEL:`, `INITIAL-NOT:`, `INITIAL:`, and so forth, the checks would -be run against the initial LLVM IR generated by the Sorbet compiler. - -It is generally not a good idea to run checks for only a single pass: -running checks against two passes enables you to check whether the initial -structure was what you expected (and make things robust against -e.g. function renames) and whether the transformation you expected to take -place actually did take place. - -### Comparing LLVM IR (deprecated) - -A test can also have a `*.llo.exp` file. If this file is present, the test -runner will use the `llvm-diff` command line tool to assert that the optimized -LLVM IR output for a given `*.rb` file matches what's in the `*.llo.exp` file. -For example: - -- [test/testdata/compiler/hello.rb](test/testdata/compiler/hello.rb) -- [test/testdata/compiler/hello.llo.exp](test/testdata/compiler/hello.llo.exp) - -The `llvm-diff` command is not a textual diff. It is slightly aware of -functionally equivalent files (e.g., names of variables are different but -they're initialized and used the same way means they match). - -The LLVM IR diff tests fail are **expected to fail** for most new changes. The -idea is that you should skim the diffs, make sure they look appropriate given -what was meant to change, and then record the new exp files with this command: - -```bash -# Can only be run on Linux. -tools/scripts/update_compiler_exp.sh -``` - -The expectations files are somewhat platform dependent, so they must be recorded -on a Linux machine. - -After the expectation files have been edited on disk, use - -``` -git diff --color-words -``` - -for a better git diff that ignores irrelevant parts that have changed. - -### Comparing stderr - -Frequently, a test fails because the output on stderr is different. For example, -interpreted vs compiled backtraces usually are slightly different. - -Rather than letting the test chunder an exception backtrace to stderr, consider -catching the expected exception and only printing the message. - -If the stderr difference is not due to exceptions (or the behavior of the test -changes when introducing exception handling), add this special comment to the -top of the test: - -``` -# skip_stderr_check -``` - -With this comment, only the exit code and stdout of a test must match. - -## Debugging - -It is hard to debug something inside the Bazel test sandbox directly. Instead, -use the `test/run_*.sh` scripts (discussed above). These scripts have a `-d` -option that will drop into `lldb` before running. - -### `.lldbinit` - -We have a custom `.lldbinit` file with some helper commands. -The new commands it makes available inside `lldb` are: - -- `rubysourcemaps` - - set up source maps Ruby VM source files (e.g., when looking at a `ruby` - C-level backtrace) -- `llvmsourcemaps` - - set up source maps for LLVM source files (e.g., when looking at exceptions - thrown when compiling a Ruby file) -- `rubystack` - - dump the current Ruby stack if stopped in a Ruby VM breakpoint - -If you see a message about local `.lldbinit` files when running `lldb`, you can -either: - -- ignore all local .lldbinit files: - - ``` - # ~/.lldbinit - settings set target.load-cwd-lldbinit false - ``` - -- run all local .lldbinit files: - - ``` - # ~/.lldbinit - settings set target.load-cwd-lldbinit true - ``` - -- make the choice run-by-run: - - ``` - lldb (--local-lldbinit|--no-lldbinit) - ``` - -### Debugging the Ruby VM - -Ruby code compiled by Sorbet is run by the Ruby VM. It helps to have an idea of -what functions are available in the Ruby VM to break on or call when stopped: - -Suggested breakpoints: - -- `rb_longjmp` - break on nearly any Ruby exception being raised - - There are a handful of cases where breaking on `rb_longjmp` won't stop when - a Ruby exception is raised, but I don't remember what they are. -- Find something in [insns.def](https://github.com/ruby/ruby/tree/master/insns.def) - - All Ruby VM bytecode instructions' implementations are in `insns.def` - - You can break on like `insns.def:762` - -Suggested functions to call while stopped: - -- `rb_backtrace()` - - Print a Ruby-level backtrace right now -- `rb_id2name` - - Convert an `ID` (Ruby interned string) to a C string -- `sorbet_dbg_p(...)` - - Print the given Ruby `VALUE` like you had called `p ...` in Ruby - -Other: - -- To debug Ruby running via Rbenv: - - ``` - lldb -- $(rbenv which ruby) [...] - ``` - -- You can use the [`stop_in_debugger`] gem to set a C-level debugger - breakpoint from Ruby source code. - -[`stop_in_debugger`]: https://github.com/jez/stop_in_debugger - - -## C++ in the compiler - - - -In Sorbet, any exception is a crash (Sorbet never raises exceptions for expected -behavior). - -But this is not true in the Sorbet compiler. Certain user-expected outcomes are -powered by exceptions (see `AbortCompilation`). This means that an exception -might be thrown to interrupt you during normal execution and be recovered from -higher up. For example: - -```cpp -unique_ptr foo(unique_ptr x) { - auto *y = x.release(); - codeThatRaisesAbortCompilation(); - auto z = unique_ptr(y); - return z; -} -``` - -In this case, ownership of `x` is released, but an exception is thrown before -ownership of `y` is transfered to `z`. This means that the memory associated -with `y` will never be freed, but the `AbortCompilation` will be caught and -execution will continue, and this memory will have leaked. - -It's best to just never use `unique_ptr::release` in the compiler. - - -## Editor and environment - -Nearly all of the [Editor and environment] tips from the main README apply here -too. - -[Editor and environment]: https://github.com/sorbet/sorbet#editor-and-environment - -Especially the `--disk_cache` suggestions. If you're working on a Stripe devbox, -you'll want to make the `.bazelrc.local` file on the devbox--it's gitignored by -default, so you'll want to make it directly on the devbox. - -Sorbet compiler-specific recommendations: - -- Get yourself a syntax highlighting plugin for LLVM IR. For example: - - diff --git a/compiler/main.cc b/compiler/main.cc deleted file mode 100644 index cf979a4507..0000000000 --- a/compiler/main.cc +++ /dev/null @@ -1,16 +0,0 @@ -#include "common/common.h" -#include "main/options/options.h" -#include "main/realmain.h" - -#include "compiler/ObjectFileEmitter/ObjectFileEmitter.h" - -int main(int argc, char *argv[]) { - try { - sorbet::compiler::ObjectFileEmitter::init(); - return sorbet::realmain::realmain(argc, argv); - } catch (sorbet::EarlyReturnWithCode &c) { - return c.returnCode; - } catch (sorbet::SorbetException &e) { - return 1; - } -}; diff --git a/compiler/ruby-static-exports/BUILD b/compiler/ruby-static-exports/BUILD deleted file mode 100644 index f7fb7c5d6e..0000000000 --- a/compiler/ruby-static-exports/BUILD +++ /dev/null @@ -1,5 +0,0 @@ -filegroup( - name = "vm_append_files", - srcs = glob(["*.c"]), - visibility = ["//visibility:public"], -) diff --git a/compiler/ruby-static-exports/array.c b/compiler/ruby-static-exports/array.c deleted file mode 100644 index 4ee05a2656..0000000000 --- a/compiler/ruby-static-exports/array.c +++ /dev/null @@ -1,23 +0,0 @@ -VALUE sorbet_rb_ary_push_m(int argc, const VALUE *args, VALUE obj) { - return rb_ary_push_m(argc, args, obj); -} - -VALUE sorbet_rb_ary_first(int argc, const VALUE *args, VALUE obj) { - return rb_ary_first(argc, args, obj); -} - -VALUE sorbet_rb_ary_join_m(int argc, const VALUE *args, VALUE obj) { - return rb_ary_join_m(argc, args, obj); -} - -VALUE sorbet_rb_ary_concat_multi(int argc, const VALUE *args, VALUE obj) { - return rb_ary_concat_multi(argc, args, obj); -} - -VALUE sorbet_rb_ary_diff(VALUE obj, VALUE arg_0) { - return rb_ary_diff(obj, arg_0); -} - -VALUE sorbet_rb_ary_flatten(int argc, const VALUE *args, VALUE obj) { - return rb_ary_flatten(argc, args, obj); -} diff --git a/compiler/ruby-static-exports/hash.c b/compiler/ruby-static-exports/hash.c deleted file mode 100644 index db63357b91..0000000000 --- a/compiler/ruby-static-exports/hash.c +++ /dev/null @@ -1,3 +0,0 @@ -VALUE sorbet_rb_hash_dig(int argc, const VALUE *args, VALUE obj) { - return rb_hash_dig(argc, args, obj); -} diff --git a/compiler/ruby-static-exports/numeric.c b/compiler/ruby-static-exports/numeric.c deleted file mode 100644 index 131729e328..0000000000 --- a/compiler/ruby-static-exports/numeric.c +++ /dev/null @@ -1,15 +0,0 @@ -VALUE sorbet_flo_ge(VALUE obj, VALUE arg_0) { - return flo_ge(obj, arg_0); -} - -VALUE sorbet_flo_lt(VALUE obj, VALUE arg_0) { - return flo_lt(obj, arg_0); -} - -VALUE sorbet_flo_le(VALUE obj, VALUE arg_0) { - return flo_le(obj, arg_0); -} - -VALUE sorbet_int_to_f(VALUE obj) { - return int_to_f(obj); -} diff --git a/compiler/ruby-static-exports/string.c b/compiler/ruby-static-exports/string.c deleted file mode 100644 index 260e846a7a..0000000000 --- a/compiler/ruby-static-exports/string.c +++ /dev/null @@ -1,7 +0,0 @@ -VALUE sorbet_rb_str_aref_m(int argc, const VALUE *args, VALUE obj) { - return rb_str_aref_m(argc, args, obj); -} - -VALUE sorbet_rb_str_start_with(int argc, const VALUE *args, VALUE obj) { - return rb_str_start_with(argc, args, obj); -} diff --git a/core/CompiledLevel.h b/core/CompiledLevel.h index 4f32756936..7e56af8453 100644 --- a/core/CompiledLevel.h +++ b/core/CompiledLevel.h @@ -1,6 +1,7 @@ #ifndef SORBET_CORE_COMPILED_LEVEL_H #define SORBET_CORE_COMPILED_LEVEL_H +#include "core/SigilTraits.h" #include namespace sorbet::core { @@ -16,6 +17,24 @@ enum class CompiledLevel : uint8_t { // This file should be compiled. True = 2, }; -} + +template <> class SigilTraits { +public: + static constexpr CompiledLevel NONE = CompiledLevel::None; + + static constexpr std::string_view SIGIL_PREFIX = "compiled:"; + + static CompiledLevel fromString(std::string_view s) { + if (s == "false") { + return CompiledLevel::False; + } else if (s == "true") { + return CompiledLevel::True; + } else { + return CompiledLevel::None; + } + } +}; + +} // namespace sorbet::core #endif diff --git a/core/Context.cc b/core/Context.cc index 8eabef315a..b1855f0963 100644 --- a/core/Context.cc +++ b/core/Context.cc @@ -1,5 +1,4 @@ #include "core/Context.h" -#include "common/FileOps.h" #include "core/GlobalState.h" #include "main/pipeline/semantic_extension/SemanticExtension.h" #include @@ -20,30 +19,6 @@ ClassOrModuleRef MutableContext::selfClass() { return this->owner.enclosingClass(this->state); } -bool Context::permitOverloadDefinitions(const core::GlobalState &gs, FileRef sigLoc, core::SymbolRef owner) { - if (!owner.exists()) { - return false; - } - for (auto loc : owner.locs(gs)) { - auto &file = loc.file().data(gs); - if ((file.isPayload() || file.isStdlib()) && owner != Symbols::root() && - (owner != Symbols::Object() || sigLoc.data(gs).isStdlib())) { - return true; - } - } - - constexpr string_view whitelistedTest = "overloads_test.rb"sv; - return FileOps::getFileName(sigLoc.data(gs).path()) == whitelistedTest; -} - -bool Context::permitOverloadDefinitions(FileRef sigLoc) const { - return Context::permitOverloadDefinitions(state, sigLoc, owner); -} - -bool MutableContext::permitOverloadDefinitions(FileRef sigLoc) const { - return Context::permitOverloadDefinitions(state, sigLoc, owner); -} - Context::Context(const MutableContext &other) noexcept : state(other.state), owner(other.owner), file(other.file) {} void Context::trace(string_view msg) const { diff --git a/core/Context.h b/core/Context.h index 6db52fc1f4..795f1be0bf 100644 --- a/core/Context.h +++ b/core/Context.h @@ -30,8 +30,6 @@ class Context { Context(const MutableContext &other) noexcept; ErrorBuilder beginError(LocOffsets loc, ErrorClass what) const; - bool permitOverloadDefinitions(FileRef sigLoc) const; - static bool permitOverloadDefinitions(const core::GlobalState &gs, FileRef sigLoc, core::SymbolRef owner); Context withOwner(SymbolRef sym) const; Context withFile(FileRef file) const; @@ -66,8 +64,6 @@ class MutableContext final { // their singleton classes for most purposes) ClassOrModuleRef selfClass(); - bool permitOverloadDefinitions(FileRef sigLoc) const; - MutableContext withOwner(SymbolRef sym) const; MutableContext withFile(FileRef file) const; ErrorBuilder beginError(LocOffsets loc, ErrorClass what) const; diff --git a/core/Error.cc b/core/Error.cc index cf6ea0a9f6..b39bbee572 100644 --- a/core/Error.cc +++ b/core/Error.cc @@ -106,7 +106,17 @@ string ErrorSection::toString(const GlobalState &gs) const { bool skipEOL = false; if (!this->header.empty()) { coloredLineHeaders = false; - buf << indent << DETAIL_COLOR << restoreColors(this->header, DETAIL_COLOR) << RESET_COLOR; + string formattedHeader; + if (this->isAutocorrectDescription) { + if (gs.autocorrect) { + formattedHeader = fmt::format("{} Done", this->header); + } else { + formattedHeader = ErrorColors::format("{} Use `{}` to autocorrect", this->header, "-a"); + } + } else { + formattedHeader = this->header; + } + buf << indent << DETAIL_COLOR << restoreColors(formattedHeader, DETAIL_COLOR) << RESET_COLOR; } else { skipEOL = true; } @@ -120,6 +130,30 @@ string ErrorSection::toString(const GlobalState &gs) const { return buf.str(); } +void ErrorSection::Collector::addErrorDetails(ErrorSection::Collector &&e) { + children.push_back(std::move(e)); +} + +void toErrorSectionHelper(const ErrorSection::Collector &e, vector &result, int indentLevel) { + ENFORCE(e.message.length() > 0); + std::string message = fmt::format("{}{}", string(indentLevel * 2, ' '), e.message); + result.push_back(ErrorLine(core::Loc::none(), move(message), ErrorLine::LocDisplay::Hidden)); + for (auto &c : e.children) { + toErrorSectionHelper(c, result, indentLevel + 1); + } +} + +std::optional ErrorSection::Collector::toErrorSection() const { + if (children.size() == 0) { + return nullopt; + } + vector lines; + for (auto &c : children) { + toErrorSectionHelper(c, lines, 0); + } + return ErrorSection("Detailed explanation:", move(lines)); +} + string Error::toString(const GlobalState &gs) const { stringstream buf; buf << RESET_STYLE << FILE_POS_STYLE << loc.filePosToString(gs) << RESET_STYLE << ": " << ERROR_COLOR @@ -164,16 +198,21 @@ void ErrorBuilder::addErrorSection(optional &§ion) { } } +void ErrorBuilder::addErrorSections(const ErrorSection::Collector &&errorDetailsCollector) { + if (auto errorSection = errorDetailsCollector.toErrorSection()) { + addErrorSection(move(errorSection.value())); + } +} + void ErrorBuilder::addAutocorrect(AutocorrectSuggestion &&autocorrect) { ENFORCE(state == State::WillBuild); string sectionTitle; if (gs.autocorrect) { - sectionTitle = "Autocorrect: Done"; + sectionTitle = "Autocorrect:"; } else if (autocorrect.isDidYouMean && autocorrect.edits.size() == 1) { - sectionTitle = - ErrorColors::format("Did you mean `{}`? Use `{}` to autocorrect", autocorrect.edits[0].replacement, "-a"); + sectionTitle = ErrorColors::format("Did you mean `{}`?", autocorrect.edits[0].replacement); } else { - sectionTitle = ErrorColors::format("Autocorrect: Use `{}` to autocorrect", "-a"); + sectionTitle = "Autocorrect:"; } std::vector messages; @@ -194,7 +233,10 @@ void ErrorBuilder::addAutocorrect(AutocorrectSuggestion &&autocorrect) { messages.emplace_back(std::move(line)); } } - addErrorSection(ErrorSection{sectionTitle, std::move(messages)}); + auto errorSection = ErrorSection{sectionTitle, std::move(messages)}; + errorSection.isAutocorrectDescription = true; + errorSection.isDidYouMean = autocorrect.isDidYouMean; + addErrorSection(move(errorSection)); this->autocorrects.emplace_back(move(autocorrect)); } diff --git a/core/Error.h b/core/Error.h index ab040d35f5..972db98920 100644 --- a/core/Error.h +++ b/core/Error.h @@ -74,6 +74,8 @@ struct ErrorLine { struct ErrorSection { std::string header; std::vector messages; + bool isAutocorrectDescription = false; + bool isDidYouMean = false; ErrorSection(std::string_view header) : header(header) {} ErrorSection(std::string_view header, const std::initializer_list &messages) : header(header), messages(messages) {} @@ -82,6 +84,33 @@ struct ErrorSection { ErrorSection(const std::initializer_list &messages) : ErrorSection("", messages) {} ErrorSection(const std::vector &messages) : ErrorSection("", messages) {} std::string toString(const GlobalState &gs) const; + + class NoOpCollector { + public: + void addErrorDetails(NoOpCollector e) {} + NoOpCollector newCollector() const { + return *this; + } + }; + + class Collector { + public: + std::string message; + std::vector children; + + Collector() = default; + Collector(const Collector &) = delete; + Collector &operator=(const Collector &) = delete; + Collector(Collector &&other) = default; + + void addErrorDetails(Collector &&e); + Collector newCollector() const { + return Collector(); + } + std::optional toErrorSection() const; + + constexpr static NoOpCollector NO_OP = NoOpCollector(); + }; }; class Error { @@ -160,6 +189,7 @@ class ErrorBuilder { std::string formatted = ErrorColors::format(msg, std::forward(args)...); _setHeader(move(formatted)); } + void addErrorSections(const ErrorSection::Collector &&errorDetailsCollector); void addAutocorrect(AutocorrectSuggestion &&autocorrect); template diff --git a/core/ErrorCollector.cc b/core/ErrorCollector.cc index cce59e0187..2f76d6566f 100644 --- a/core/ErrorCollector.cc +++ b/core/ErrorCollector.cc @@ -16,7 +16,7 @@ void ErrorCollector::flushErrors(spdlog::logger &logger, const core::GlobalState } } -vector> ErrorCollector::drainErrors() { +[[nodiscard]] vector> ErrorCollector::drainErrors() { return move(collectedErrors); } diff --git a/core/ErrorQueueMessage.h b/core/ErrorQueueMessage.h index 1619b8428e..a4e1097f87 100644 --- a/core/ErrorQueueMessage.h +++ b/core/ErrorQueueMessage.h @@ -8,7 +8,7 @@ namespace sorbet::core { class Error; struct ErrorQueueMessage { - enum class Kind { Error, Flush, QueryResponse }; + enum class Kind { Error, QueryResponse }; Kind kind; core::FileRef whatFile; // The text of the error. Is a `nullopt` if the error is silenced. diff --git a/core/FileRef.h b/core/FileRef.h index 59bec0da8f..10c9781292 100644 --- a/core/FileRef.h +++ b/core/FileRef.h @@ -47,6 +47,11 @@ class FileRef final { const File &dataAllowingUnsafe(const GlobalState &gs) const; File &dataAllowingUnsafe(GlobalState &gs) const; + // Normally, using .data requires that the file have been read. + // But File::Flag data is populated when the File is created, before needing to read the + // file. This helper exposes that without tripping an ENFORCE. + bool isPackage(const GlobalState &gs) const; + private: uint32_t _id; }; diff --git a/core/Files.cc b/core/Files.cc index 6bee2ea2fc..806525f1a9 100644 --- a/core/Files.cc +++ b/core/Files.cc @@ -1,7 +1,9 @@ #include "core/Files.h" +#include "common/FileOps.h" #include "core/Context.h" #include "core/FileHash.h" #include "core/GlobalState.h" +#include "core/SigilTraits.h" #include #include "absl/strings/match.h" @@ -16,211 +18,75 @@ namespace { constexpr auto EXTERNAL_PREFIX = "external/com_stripe_ruby_typer/"sv; -struct SigilInfo { - LocOffsets loc; - StrictLevel strictLevel; -}; - -SigilInfo strictSigilInfo(string_view source) { - /* - * StrictLevel::None: - * StrictLevel::False: # typed: false - * StrictLevel::True: # typed: true - * StrictLevel::Strict: # typed: strict - * StrictLevel::String: # typed: strong - * StrictLevel::Autogenerated: # typed: autogenerated - */ - size_t start = 0; - while (true) { - start = source.find("typed:", start); - if (start == string_view::npos) { - return {LocOffsets::none(), StrictLevel::None}; - } +template class ParseSigil { +public: + static Sigil parse(string_view source) { + size_t start = 0; + while (true) { + start = source.find(SigilTraits::SIGIL_PREFIX, start); + if (start == string_view::npos) { + return SigilTraits::NONE; + } - auto comment_start = start; - while (comment_start > 0) { - --comment_start; - auto c = source[comment_start]; - if (c == ' ') { + auto comment_start = start; + while (comment_start > 0) { + --comment_start; + auto c = source[comment_start]; + if (c == ' ') { + continue; + } else { + break; + } + } + if (source[comment_start] != '#') { + ++start; continue; - } else { - break; } - } - if (source[comment_start] != '#') { - ++start; - continue; - } - start += 6; - while (start < source.size() && source[start] == ' ') { - ++start; - } + start += SigilTraits::SIGIL_PREFIX.size(); + while (start < source.size() && source[start] == ' ') { + ++start; + } - if (start >= source.size()) { - return {LocOffsets::none(), StrictLevel::None}; - } - auto end = start + 1; - while (end < source.size() && source[end] != ' ' && source[end] != '\n') { - ++end; - } - if (source[end - 1] == '\r') { - end -= 1; - } + if (start >= source.size()) { + return SigilTraits::NONE; + } + auto end = start + 1; + while (end < source.size() && source[end] != ' ' && source[end] != '\n') { + ++end; + } + if (source[end - 1] == '\r') { + end -= 1; + } - string_view suffix = source.substr(start, end - start); - LocOffsets loc{static_cast(start), static_cast(end)}; - if (suffix == "ignore") { - return {loc, StrictLevel::Ignore}; - } else if (suffix == "false") { - return {loc, StrictLevel::False}; - } else if (suffix == "true") { - return {loc, StrictLevel::True}; - } else if (suffix == "strict") { - return {loc, StrictLevel::Strict}; - } else if (suffix == "strong") { - return {loc, StrictLevel::Strong}; - } else if (suffix == "autogenerated") { - return {loc, StrictLevel::Autogenerated}; - } else if (suffix == "__STDLIB_INTERNAL") { - return {loc, StrictLevel::Stdlib}; - } else { - // TODO(nelhage): We should report an error here to help catch - // typos. This would require refactoring so this function has - // access to GlobalState or can return errors to someone who - // does. - } + string_view suffix = source.substr(start, end - start); + auto result = SigilTraits::fromString(suffix); + if (result != SigilTraits::NONE) { + return result; + } else { + // TODO(nelhage): We should report an error here to help catch + // typos. This would require refactoring so this function has + // access to GlobalState or can return errors to someone who + // does. + } - start = end; + start = end; + } } -} +}; } // namespace StrictLevel File::fileStrictSigil(string_view source) { - return strictSigilInfo(source).strictLevel; -} - -LocOffsets File::locStrictSigil(string_view source) { - return strictSigilInfo(source).loc; + return ParseSigil::parse(source); } CompiledLevel File::fileCompiledSigil(string_view source) { - size_t start = 0; - while (true) { - start = source.find("compiled:", start); - - if (start == string_view::npos) { - return CompiledLevel::None; - } - - auto comment_start = start; - while (comment_start > 0) { - --comment_start; - auto c = source[comment_start]; - if (c == ' ') { - continue; - } else { - break; - } - } - - if (source[comment_start] != '#') { - ++start; - continue; - } - - // skip over 'compiled:' - start += 9; - while (start < source.size() && source[start] == ' ') { - ++start; - } - - if (start >= source.size()) { - return CompiledLevel::None; - } - - auto end = start + 1; - while (end < source.size() && source[end] != ' ' && source[end] != '\n') { - ++end; - } - if (source[end - 1] == '\r') { - end -= 1; - } - - string_view suffix = source.substr(start, end - start); - if (suffix == "false") { - return CompiledLevel::False; - } else if (suffix == "true") { - return CompiledLevel::True; - } else { - // TODO(nelhage): We should report an error here to help catch - // typos. This would require refactoring so this function has - // access to GlobalState or can return errors to someone who - // does. - } - - start = end; - } + return ParseSigil::parse(source); } PackagedLevel File::filePackagedSigil(string_view source) { - size_t start = 0; - while (true) { - start = source.find("packaged:", start); - if (start == string_view::npos) { - return PackagedLevel::None; - } - - auto comment_start = start; - while (comment_start > 0) { - --comment_start; - auto c = source[comment_start]; - if (c == ' ') { - continue; - } else { - break; - } - } - - if (source[comment_start] != '#') { - ++start; - continue; - } - - start += 9; - while (start < source.size() && source[start] == ' ') { - ++start; - } - - if (start >= source.size()) { - return PackagedLevel::None; - } - - auto end = start + 1; - while (end < source.size() && source[end] != ' ' && source[end] != '\n') { - ++end; - } - if (source[end - 1] == '\r') { - end -= 1; - } - - string_view suffix = source.substr(start, end - start); - if (suffix == "false") { - return PackagedLevel::False; - } else if (suffix == "true") { - return PackagedLevel::True; - } else { - // TODO(nelhage): We should report an error here to help catch - // typos. This would require refactoring so this function has - // access to GlobalState or can return errors to someone who - // does. - } - - start = end; - } - - return PackagedLevel::None; + return ParseSigil::parse(source); } bool isTestPath(string_view path) { @@ -302,6 +168,12 @@ File &FileRef::dataAllowingUnsafe(GlobalState &gs) const { return *(gs.files[_id]); } +bool FileRef::isPackage(const GlobalState &gs) const { + ENFORCE(gs.files[_id]); + ENFORCE(gs.files[_id]->sourceType != File::Type::TombStone); + return dataAllowingUnsafe(gs).isPackage(); +} + string_view File::path() const { return this->path_; } @@ -328,12 +200,20 @@ bool File::isStdlib() const { return this->originalSigil == StrictLevel::Stdlib; } -bool File::isPackage() const { - return flags.isPackage; +namespace { + +constexpr string_view OVERLOADS_TEST_RB = "overloads_test.rb"sv; + +} + +bool File::permitOverloadDefinitions() const { + return this->isRBI() || FileOps::getFileName(this->path()) == OVERLOADS_TEST_RB || this->isStdlib(); } -void File::setIsPackage(bool isPackage) { - this->flags.isPackage = isPackage; +bool File::isPackage() const { + // If the `__package.rb` file is at `typed: ignore`, then we haven't even parsed it. + // Any loop over "all package files" really only wants "all non-ignored package files." + return flags.isPackage && this->strictLevel != StrictLevel::Ignore; } bool File::isOpenInClient() const { diff --git a/core/Files.h b/core/Files.h index 5462641db4..df260b4bb7 100644 --- a/core/Files.h +++ b/core/Files.h @@ -36,7 +36,6 @@ class File final { friend class ::sorbet::core::serialize::SerializerImpl; static StrictLevel fileStrictSigil(std::string_view source); - static LocOffsets locStrictSigil(std::string_view source); static CompiledLevel fileCompiledSigil(std::string_view source); static PackagedLevel filePackagedSigil(std::string_view source); @@ -49,11 +48,12 @@ class File final { bool isStdlib() const; bool isPackageRBI() const; + bool permitOverloadDefinitions() const; + static bool isRBIPath(std::string_view path); static bool isPackagePath(std::string_view path); bool isPackage() const; - void setIsPackage(bool isPackage); // Whether the file is open in the LSP client. (Always false if not running under LSP.) bool isOpenInClient() const; diff --git a/core/FoundDefinitions.h b/core/FoundDefinitions.h index d8ec297466..25ee22c580 100644 --- a/core/FoundDefinitions.h +++ b/core/FoundDefinitions.h @@ -36,7 +36,7 @@ class FoundDefinitionRef final { private: struct Storage { Kind kind : 4; - // When kind != Symbol, `id` stores indices into the assorted vectors on `FoundDefitions`. + // When kind != Symbol, `id` stores indices into the assorted vectors on `FoundDefinitions`. // The 28-bit limit means that a single file cannot have more than 2^28 definitions in that file. // // When kind == Symbol, `id` stores ClassOrModule IDs. This means that a FoundDefinitionRef @@ -230,22 +230,6 @@ class FoundDefinitions final { // Contains all class and instance variables defined in the file. std::vector _fields; - FoundDefinitionRef addNonClassConstant(FoundDefinitionRef ref) { - DEBUG_ONLY(switch (ref.kind()) { - case FoundDefinitionRef::Kind::StaticField: - case FoundDefinitionRef::Kind::TypeMember: - break; - case FoundDefinitionRef::Kind::Class: - case FoundDefinitionRef::Kind::Method: - case FoundDefinitionRef::Kind::Field: - case FoundDefinitionRef::Kind::Empty: - case FoundDefinitionRef::Kind::Symbol: - ENFORCE(false, "Attempted to give unexpected FoundDefinitionRef kind to addDefinition"); - }); - _nonClassConstants.emplace_back(ref); - return ref; - } - public: FoundDefinitions() = default; FoundDefinitions(FoundDefinitions &&names) = default; @@ -267,13 +251,17 @@ class FoundDefinitions final { FoundDefinitionRef addStaticField(FoundStaticField &&staticField) { const uint32_t idx = _staticFields.size(); _staticFields.emplace_back(std::move(staticField)); - return addNonClassConstant(FoundDefinitionRef(FoundDefinitionRef::Kind::StaticField, idx)); + auto ref = FoundDefinitionRef(FoundDefinitionRef::Kind::StaticField, idx); + _nonClassConstants.emplace_back(ref); + return ref; } FoundDefinitionRef addTypeMember(FoundTypeMember &&typeMember) { const uint32_t idx = _typeMembers.size(); _typeMembers.emplace_back(std::move(typeMember)); - return addNonClassConstant(FoundDefinitionRef(FoundDefinitionRef::Kind::TypeMember, idx)); + auto ref = FoundDefinitionRef(FoundDefinitionRef::Kind::TypeMember, idx); + _nonClassConstants.emplace_back(ref); + return ref; } FoundDefinitionRef addField(FoundField &&field) { diff --git a/core/GlobalState.cc b/core/GlobalState.cc index 3c45abfce9..5bf292220e 100644 --- a/core/GlobalState.cc +++ b/core/GlobalState.cc @@ -85,6 +85,13 @@ struct MethodBuilder { return *this; } + MethodBuilder &defaultKeywordArg(NameRef name) { + auto &arg = gs.enterMethodArgumentSymbol(Loc::none(), method, name); + arg.flags.isDefault = true; + arg.flags.isKeyword = true; + return *this; + } + MethodBuilder &repeatedArg(NameRef name) { auto &arg = gs.enterMethodArgumentSymbol(Loc::none(), method, name); arg.flags.isRepeated = true; @@ -365,6 +372,8 @@ void GlobalState::initEmpty() { ENFORCE(klass == Symbols::Class()); klass = synthesizeClass(core::Names::Constants::BasicObject(), 0); ENFORCE(klass == Symbols::BasicObject()); + method = enterMethod(*this, Symbols::BasicObject(), Names::initialize()).build(); + ENFORCE(method == Symbols::BasicObject_initialize()); klass = synthesizeClass(core::Names::Constants::Kernel(), 0, true); ENFORCE(klass == Symbols::Kernel()); klass = synthesizeClass(core::Names::Constants::Range()); @@ -411,12 +420,16 @@ void GlobalState::initEmpty() { klass = Symbols::Sorbet_Private_Static().data(*this)->singletonClass(*this); ENFORCE(klass == Symbols::Sorbet_Private_StaticSingleton()); klass = enterClassSymbol(Loc::none(), Symbols::Sorbet_Private_Static(), core::Names::Constants::StubModule()); + klass.data(*this)->setIsModule(true); ENFORCE(klass == Symbols::StubModule()); klass = enterClassSymbol(Loc::none(), Symbols::Sorbet_Private_Static(), core::Names::Constants::StubMixin()); + klass.data(*this)->setIsModule(true); ENFORCE(klass == Symbols::StubMixin()); klass = enterClassSymbol(Loc::none(), Symbols::Sorbet_Private_Static(), core::Names::Constants::PlaceholderMixin()); + klass.data(*this)->setIsModule(true); ENFORCE(klass == Symbols::PlaceholderMixin()); klass = enterClassSymbol(Loc::none(), Symbols::Sorbet_Private_Static(), core::Names::Constants::StubSuperClass()); + klass.data(*this)->setIsModule(false); ENFORCE(klass == Symbols::StubSuperClass()); klass = enterClassSymbol(Loc::none(), Symbols::T(), core::Names::Constants::Enumerable()); ENFORCE(klass == Symbols::T_Enumerable()); @@ -556,6 +569,15 @@ void GlobalState::initEmpty() { klass.data(*this)->setIsModule(false); ENFORCE(klass == Symbols::T_Private_Methods_DeclBuilder()); + method = enterMethod(*this, Symbols::T_Private_Methods_DeclBuilder(), Names::abstract()).build(); + ENFORCE(method == Symbols::T_Private_Methods_DeclBuilder_abstract()); + method = enterMethod(*this, Symbols::T_Private_Methods_DeclBuilder(), Names::overridable()).build(); + ENFORCE(method == Symbols::T_Private_Methods_DeclBuilder_overridable()); + method = enterMethod(*this, Symbols::T_Private_Methods_DeclBuilder(), Names::override_()) + .defaultKeywordArg(Names::allowIncompatible()) + .build(); + ENFORCE(method == Symbols::T_Private_Methods_DeclBuilder_override()); + // T.class_of(T::Sig::WithoutRuntime) klass = Symbols::T_Sig_WithoutRuntime().data(*this)->singletonClass(*this); ENFORCE(klass == Symbols::T_Sig_WithoutRuntimeSingleton()); @@ -617,11 +639,6 @@ void GlobalState::initEmpty() { method = this->staticInitForClass(core::Symbols::root(), Loc::none()); ENFORCE(method == Symbols::rootStaticInit()); - method = enterMethod(*this, Symbols::PackageSpecSingleton(), Names::autoloaderCompatibility()) - .arg(Names::arg0()) - .build(); - ENFORCE(method == Symbols::PackageSpec_autoloader_compatibility()); - method = enterMethod(*this, Symbols::PackageSpecSingleton(), Names::visibleTo()).arg(Names::arg0()).build(); ENFORCE(method == Symbols::PackageSpec_visible_to()); @@ -634,12 +651,6 @@ void GlobalState::initEmpty() { klass = Symbols::Sorbet_Private_Static_ResolvedSig().data(*this)->singletonClass(*this); ENFORCE(klass == Symbols::Sorbet_Private_Static_ResolvedSigSingleton()); - klass = enterClassSymbol(Loc::none(), Symbols::T_Private(), core::Names::Constants::Compiler()); - klass.data(*this)->setIsModule(true); // explicitly set isModule so we can immediately call singletonClass - ENFORCE(klass == Symbols::T_Private_Compiler()); - klass = Symbols::T_Private_Compiler().data(*this)->singletonClass(*this); - ENFORCE(klass == Symbols::T_Private_CompilerSingleton()); - // Magic classes for special proc bindings klass = enterClassSymbol(Loc::none(), Symbols::Magic(), core::Names::Constants::BindToAttachedClass()); ENFORCE(klass == Symbols::MagicBindToAttachedClass()); @@ -664,6 +675,9 @@ void GlobalState::initEmpty() { method = enterMethod(*this, Symbols::T_Generic(), Names::squareBrackets()).repeatedTopArg(Names::args()).build(); ENFORCE(method == Symbols::T_Generic_squareBrackets()); + method = enterMethod(*this, Symbols::Kernel(), Names::lambdaTLet()).typedArg(Names::type(), Types::top()).build(); + ENFORCE(method == Symbols::Kernel_lambdaTLet()); + typeArgument = enterTypeArgument(Loc::none(), Symbols::noMethod(), Names::Constants::TodoTypeArgument(), Variance::CoVariant); ENFORCE(typeArgument == Symbols::todoTypeArgument()); @@ -820,10 +834,10 @@ void GlobalState::initEmpty() { .untypedArg(Names::arg0()) .buildWithResultUntyped(); - // Synthesize .(arg0: T.untyped) => T.untyped + // Synthesize .(arg0: T.untyped) => T.nilable(String) method = enterMethod(*this, Symbols::MagicSingleton(), Names::definedInstanceVar()) .untypedArg(Names::arg0()) - .buildWithResultUntyped(); + .buildWithResult(Types::any(*this, Types::nilClass(), Types::String())); // Synthesize .params(args: T.untyped) => DeclBuilderForProcs method = enterMethod(*this, Symbols::DeclBuilderForProcsSingleton(), Names::params()) @@ -1282,6 +1296,13 @@ TypeArgumentRef GlobalState::enterTypeArgument(Loc loc, MethodRef owner, NameRef if (typeArg.dataAllowingNone(*this)->name == name) { ENFORCE(typeArg.dataAllowingNone(*this)->flags.hasFlags(flags), "existing symbol has wrong flags"); counterInc("symbols.hit"); + if (!symbolTableFrozen) { + typeArg.data(*this)->addLoc(*this, loc); + } else { + // Sometimes this method is called when the symbol table is frozen for the purposes of sanity + // checking. Don't mutate the symbol table in those cases. This loc should already be there. + ENFORCE(!loc.exists() || absl::c_count(typeArg.data(*this)->locs(), loc) == 1); + } return typeArg; } } @@ -1410,7 +1431,17 @@ FieldRef GlobalState::enterStaticFieldSymbol(Loc loc, ClassOrModuleRef owner, Na if (store.exists()) { ENFORCE(store.isStaticField(*this), "existing symbol is not a static field"); counterInc("symbols.hit"); - return store.asFieldRef(); + + // Ensures that locs get properly updated on the fast path + auto fieldRef = store.asFieldRef(); + if (!symbolTableFrozen) { + fieldRef.data(*this)->addLoc(*this, loc); + } else { + // Sometimes this method is called when the symbol table is frozen for the purposes of sanity + // checking. Don't mutate the symbol table in those cases. This loc should already be there. + ENFORCE(!loc.exists() || absl::c_count(fieldRef.data(*this)->locs(), loc) == 1); + } + return fieldRef; } ENFORCE(!symbolTableFrozen); @@ -2114,6 +2145,8 @@ unique_ptr GlobalState::deepCopy(bool keepId) const { result->sleepInSlowPathSeconds = this->sleepInSlowPathSeconds; result->requiresAncestorEnabled = this->requiresAncestorEnabled; result->ruby3KeywordArgs = this->ruby3KeywordArgs; + result->typedSuper = this->typedSuper; + result->suppressPayloadSuperclassRedefinitionFor = this->suppressPayloadSuperclassRedefinitionFor; result->trackUntyped = this->trackUntyped; result->printingFileTable = this->printingFileTable; result->isSCIPRuby = this->isSCIPRuby; @@ -2216,6 +2249,8 @@ unique_ptr GlobalState::copyForIndex() const { result->sleepInSlowPathSeconds = this->sleepInSlowPathSeconds; result->requiresAncestorEnabled = this->requiresAncestorEnabled; result->ruby3KeywordArgs = this->ruby3KeywordArgs; + result->typedSuper = this->typedSuper; + result->suppressPayloadSuperclassRedefinitionFor = this->suppressPayloadSuperclassRedefinitionFor; result->trackUntyped = this->trackUntyped; result->kvstoreUuid = this->kvstoreUuid; result->errorUrlBase = this->errorUrlBase; @@ -2376,30 +2411,25 @@ const packages::PackageDB &GlobalState::packageDB() const { return packageDB_; } -void GlobalState::setPackagerOptions(const std::vector &secondaryTestPackageNamespaces, - const std::vector &extraPackageFilesDirectoryUnderscorePrefixes, +void GlobalState::setPackagerOptions(const std::vector &extraPackageFilesDirectoryUnderscorePrefixes, const std::vector &extraPackageFilesDirectorySlashPrefixes, const std::vector &packageSkipRBIExportEnforcementDirs, - const std::vector &skipImportVisibilityCheckFor, + const std::vector &allowRelaxedPackagerChecksFor, std::string errorHint) { - ENFORCE(packageDB_.secondaryTestPackageNamespaceRefs_.size() == 0); ENFORCE(!packageDB_.frozen); - for (const string &ns : secondaryTestPackageNamespaces) { - packageDB_.secondaryTestPackageNamespaceRefs_.emplace_back(enterNameConstant(ns)); - } - + packageDB_.enabled_ = true; packageDB_.extraPackageFilesDirectoryUnderscorePrefixes_ = extraPackageFilesDirectoryUnderscorePrefixes; packageDB_.extraPackageFilesDirectorySlashPrefixes_ = extraPackageFilesDirectorySlashPrefixes; packageDB_.skipRBIExportEnforcementDirs_ = packageSkipRBIExportEnforcementDirs; - std::vector skipImportVisibilityCheckFor_; - for (const string &pkgName : skipImportVisibilityCheckFor) { + std::vector allowRelaxedPackagerChecksFor_; + for (const string &pkgName : allowRelaxedPackagerChecksFor) { std::vector pkgNameParts = absl::StrSplit(pkgName, "::"); auto mangledName = core::packages::MangledName::mangledNameFromParts(*this, pkgNameParts); - skipImportVisibilityCheckFor_.emplace_back(mangledName); + allowRelaxedPackagerChecksFor_.emplace_back(mangledName); } - packageDB_.skipImportVisibilityCheckFor_ = skipImportVisibilityCheckFor_; + packageDB_.allowRelaxedPackagerChecksFor_ = allowRelaxedPackagerChecksFor_; packageDB_.errorHint_ = errorHint; } @@ -2543,6 +2573,9 @@ MethodRef GlobalState::staticInitForClass(ClassOrModuleRef klass, Loc loc) { auto blkLoc = core::Loc::none(loc.file()); auto &blkSym = enterMethodArgumentSymbol(blkLoc, sym, core::Names::blkArg()); blkSym.flags.isBlock = true; + } else { + // Ensures that locs get properly updated on the fast path + sym.data(*this)->addLoc(*this, loc); } return sym; } @@ -2562,6 +2595,9 @@ MethodRef GlobalState::staticInitForFile(Loc loc) { auto blkLoc = core::Loc::none(loc.file()); auto &blkSym = this->enterMethodArgumentSymbol(blkLoc, sym, core::Names::blkArg()); blkSym.flags.isBlock = true; + } else { + // Ensures that locs get properly updated on the fast path + sym.data(*this)->addLoc(*this, loc); } return sym; } diff --git a/core/GlobalState.h b/core/GlobalState.h index 6ffdb6d472..3765841a65 100644 --- a/core/GlobalState.h +++ b/core/GlobalState.h @@ -8,6 +8,7 @@ #include "core/Loc.h" #include "core/Names.h" #include "core/Symbols.h" +#include "core/TrackUntyped.h" #include "core/lsp/Query.h" #include "core/packages/PackageDB.h" #include "core/packages/PackageInfo.h" @@ -161,8 +162,7 @@ class GlobalState final { FileRef findFileByPath(std::string_view path) const; const packages::PackageDB &packageDB() const; - void setPackagerOptions(const std::vector &secondaryTestPackageNamespaces, - const std::vector &extraPackageFilesDirectoryUnderscorePrefixes, + void setPackagerOptions(const std::vector &extraPackageFilesDirectoryUnderscorePrefixes, const std::vector &extraPackageFilesDirectorySlashPrefixes, const std::vector &packageSkipRBIExportEnforcementDirs, const std::vector &skipImportVisibilityCheckFor, std::string errorHint); @@ -234,7 +234,7 @@ class GlobalState final { bool logRecordedFilepaths = false; bool autocorrect = false; bool didYouMean = true; - bool trackUntyped = false; + TrackUntyped trackUntyped = TrackUntyped::Nowhere; bool printingFileTable = false; // We have a lot of internal names of form `` that's chosen with `<` and `>` as you can't make @@ -300,6 +300,10 @@ class GlobalState final { // If 'true', enforce use of Ruby 3.0-style keyword args. bool ruby3KeywordArgs = false; + // If 'true', attempt to typecheck calls to `super` as often as possible. + // Some calls to `super` are not type checked due to incomplete/imperfect information. + bool typedSuper = true; + // If 'true', we're running in scip-ruby mode. bool isSCIPRuby = true; @@ -309,6 +313,8 @@ class GlobalState final { UnorderedMap> unresolvedFields; // --- end scip-ruby specific state + std::vector suppressPayloadSuperclassRedefinitionFor; + // When present, this indicates that single-package rbi generation is being performed, and contains metadata about // the packages that are imported by the one whose interface is being generated. std::optional singlePackageImports; diff --git a/core/Loc.cc b/core/Loc.cc index c28fcf975d..0c50bd9846 100644 --- a/core/Loc.cc +++ b/core/Loc.cc @@ -23,6 +23,12 @@ LocOffsets LocOffsets::join(LocOffsets other) const { return LocOffsets{min(this->beginPos(), other.beginPos()), max(this->endPos(), other.endPos())}; } +bool LocOffsets::contains(const LocOffsets &other) const { + ENFORCE_NO_TIMER(this->exists()); + ENFORCE_NO_TIMER(other.exists()); + return other.beginPos() >= beginPos() && other.endPos() <= endPos(); +} + Loc Loc::join(Loc other) const { if (!this->exists()) { return other; @@ -108,7 +114,7 @@ string leftPad(string s, int l) { constexpr unsigned int WINDOW_SIZE = 10; // how many lines of source to print constexpr unsigned int WINDOW_HALF_SIZE = WINDOW_SIZE / 2; -static_assert((WINDOW_SIZE & 1) == 0, "WINDOW_SIZE should be divisable by 2"); +static_assert((WINDOW_SIZE & 1) == 0, "WINDOW_SIZE should be divisible by 2"); void addLocLine(stringstream &buf, int line, const File &file, int tabs, int posWidth, bool censorForSnapshotTests) { printTabs(buf, tabs); @@ -310,7 +316,7 @@ optional Loc::source(const GlobalState &gs) const { bool Loc::contains(const Loc &other) const { ENFORCE_NO_TIMER(this->exists()); ENFORCE_NO_TIMER(other.exists()); - return file() == other.file() && other.beginPos() >= beginPos() && other.endPos() <= endPos(); + return file() == other.file() && offsets().contains(other.offsets()); } bool Loc::operator==(const Loc &rhs) const { @@ -390,4 +396,16 @@ pair Loc::findStartOfLine(const GlobalState &gs) const { return make_pair(Loc(this->file(), startOffset, startOffset), padding); } +Loc Loc::truncateToFirstLine(const GlobalState &gs) const { + auto [beginPos, endPos] = this->position(gs); + if (beginPos.line == endPos.line) { + return *this; + } + + const auto &lineBreaks = this->file().data(gs).lineBreaks(); + // Detail::line is 1-indexed. We want one after the 0-indexed line, so line - 1 + 1 = line + auto firstNewline = lineBreaks[beginPos.line]; + return Loc(this->file(), this->beginPos(), firstNewline); +} + } // namespace sorbet::core diff --git a/core/Loc.h b/core/Loc.h index ff40b76ba1..419d8a002f 100644 --- a/core/Loc.h +++ b/core/Loc.h @@ -135,6 +135,12 @@ class Loc final { // - how many characters of the start of this line are whitespace. // std::pair findStartOfLine(const GlobalState &gs) const; + + // If the given loc spans multiple lines, return a new location which has been truncated to + // one line (excluding the newline character which ends the first line). + // + // Otherwise, return the current loc unchanged. + Loc truncateToFirstLine(const GlobalState &gs) const; }; CheckSize(Loc, 12, 4); diff --git a/core/LocOffsets.h b/core/LocOffsets.h index 94d421aec8..7c0d1b82d7 100644 --- a/core/LocOffsets.h +++ b/core/LocOffsets.h @@ -42,6 +42,8 @@ struct LocOffsets { return LocOffsets{endPos(), endPos()}; } + bool contains(const LocOffsets &other) const; + std::string showRaw(const Context ctx) const; std::string showRaw(const MutableContext ctx) const; std::string showRaw(const GlobalState &gs, const FileRef file) const; @@ -56,6 +58,7 @@ CheckSize(LocOffsets, 8, 4); template H AbslHashValue(H h, const LocOffsets &m) { return H::combine(std::move(h), m.beginLoc, m.endLoc); } + } // namespace sorbet::core #endif // SORBET_CORE_LOCOFFSETS_H diff --git a/core/NameRef.h b/core/NameRef.h index 48f417e66b..a602343e70 100644 --- a/core/NameRef.h +++ b/core/NameRef.h @@ -84,7 +84,7 @@ class NameRef final : private DebugOnlyCheck { public: // The value `0` implies that there is no NameKind tag present (NameKind begin at 1) and is a special-value used to - // indicate a non-existant NameRef. + // indicate a non-existent NameRef. constexpr NameRef() : _id(0){}; // WellKnown is a tag to statically indicate that the caller is deliberately @@ -190,6 +190,8 @@ class NameRef final : private DebugOnlyCheck { // (file-level static init). bool isAnyStaticInitName(const GlobalState &gs) const; + bool isUniqueNameOf(const GlobalState &gs, NameRef name) const; + // All the names that Environment::updateKnowledge treats as special for the purposes of // updating control flow-sensitive type knowledge. // diff --git a/core/NameSubstitution.h b/core/NameSubstitution.h index 7a48d5440a..fd2c4fc3bd 100644 --- a/core/NameSubstitution.h +++ b/core/NameSubstitution.h @@ -26,17 +26,17 @@ class NameSubstitution final { switch (from.kind()) { case NameKind::UTF8: ENFORCE(from.utf8Index() < utf8NameSubstitution.size(), - "utf8 name substitution index out of bounds, got {} where subsitution size is {}", + "utf8 name substitution index out of bounds, got {} where substitution size is {}", std::to_string(from.rawId()), std::to_string(utf8NameSubstitution.size())); return utf8NameSubstitution[from.utf8Index()]; case NameKind::CONSTANT: ENFORCE(from.constantIndex() < constantNameSubstitution.size(), - "constant name substitution index out of bounds, got {} where subsitution size is {}", + "constant name substitution index out of bounds, got {} where substitution size is {}", std::to_string(from.rawId()), std::to_string(constantNameSubstitution.size())); return constantNameSubstitution[from.constantIndex()]; case NameKind::UNIQUE: ENFORCE(from.uniqueIndex() < uniqueNameSubstitution.size(), - "unique name substitution index out of bounds, got {} where subsitution size is {}", + "unique name substitution index out of bounds, got {} where substitution size is {}", std::to_string(from.rawId()), std::to_string(uniqueNameSubstitution.size())); return uniqueNameSubstitution[from.uniqueIndex()]; } diff --git a/core/Names.cc b/core/Names.cc index 57fe2a4cc2..5eab847ef5 100644 --- a/core/Names.cc +++ b/core/Names.cc @@ -72,6 +72,9 @@ string NameRef::showRaw(const GlobalState &gs) const { case UniqueNameKind::TEnum: kind = "E"; break; + case UniqueNameKind::Struct: + kind = "U"; + break; case UniqueNameKind::Packager: kind = "G"; break; @@ -186,6 +189,7 @@ bool NameRef::isClassName(const GlobalState &gs) const { case UniqueNameKind::MangleRename: case UniqueNameKind::MangleRenameOverload: case UniqueNameKind::TEnum: + case UniqueNameKind::Struct: return dataUnique(gs)->original.isClassName(gs); case UniqueNameKind::ResolverMissingClass: case UniqueNameKind::Packager: @@ -220,6 +224,7 @@ bool NameRef::isValidConstantName(const GlobalState &gs) const { switch (dataUnique(gs)->uniqueNameKind) { case UniqueNameKind::ResolverMissingClass: case UniqueNameKind::TEnum: + case UniqueNameKind::Struct: case UniqueNameKind::Packager: return true; case UniqueNameKind::Parser: @@ -249,6 +254,10 @@ bool NameRef::isAnyStaticInitName(const GlobalState &gs) const { } } +bool NameRef::isUniqueNameOf(const GlobalState &gs, NameRef name) const { + return this->kind() == NameKind::UNIQUE && this->dataUnique(gs)->original == name; +} + bool NameRef::isUpdateKnowledgeName() const { switch (this->rawId()) { case Names::bang().rawId(): diff --git a/core/Names.h b/core/Names.h index 4c7d3d2f00..d20d9a2fbb 100644 --- a/core/Names.h +++ b/core/Names.h @@ -32,6 +32,7 @@ enum class UniqueNameKind : uint8_t { ResolverMissingClass, // used by resolver when we want to enter a stub class into a static field. see // test/resolver/stub_missing_class_alias.rb TEnum, + Struct, Packager, }; diff --git a/core/PackagedLevel.h b/core/PackagedLevel.h index cbd7234541..ebf27cc13a 100644 --- a/core/PackagedLevel.h +++ b/core/PackagedLevel.h @@ -1,6 +1,7 @@ #ifndef CORE_PACKAGED_LEVEL_H #define CORE_PACKAGED_LEVEL_H +#include "core/SigilTraits.h" #include namespace sorbet::core { @@ -11,6 +12,23 @@ enum class PackagedLevel : uint8_t { None = 2, }; -} +template <> class SigilTraits { +public: + static constexpr PackagedLevel NONE = PackagedLevel::None; + + static constexpr std::string_view SIGIL_PREFIX = "packaged:"; + + static PackagedLevel fromString(std::string_view s) { + if (s == "false") { + return PackagedLevel::False; + } else if (s == "true") { + return PackagedLevel::True; + } else { + return PackagedLevel::None; + } + } +}; + +} // namespace sorbet::core #endif diff --git a/core/ShowOptions.h b/core/ShowOptions.h index 6848d3bff9..29ed53acdc 100644 --- a/core/ShowOptions.h +++ b/core/ShowOptions.h @@ -5,14 +5,30 @@ namespace sorbet::core { // Options for controlling how the various `show` methods work. struct ShowOptions final { - bool showForRBI : 1; + bool useValidSyntax : 1; + bool concretizeIfAbstract : 1; + bool forceSelfPrefix : 1; - ShowOptions() : showForRBI{false} {} + ShowOptions() : useValidSyntax{false}, concretizeIfAbstract{false}, forceSelfPrefix{false} {} - // Show types for printing out to an rbi file. - ShowOptions withShowForRBI() { + // Only generate generate or suggest syntactically valid code. + ShowOptions withUseValidSyntax() { ShowOptions res{*this}; - res.showForRBI = true; + res.useValidSyntax = true; + return res; + } + + // Replace the `abstract` flag with `override` + ShowOptions withConcretizeIfAbstract() { + ShowOptions res{*this}; + res.concretizeIfAbstract = true; + return res; + } + + // Always use `self.` for the prefix of a method definition. + ShowOptions withForceSelfPrefix() { + ShowOptions res{*this}; + res.forceSelfPrefix = true; return res; } }; diff --git a/core/SigilTraits.h b/core/SigilTraits.h new file mode 100644 index 0000000000..464749bbc6 --- /dev/null +++ b/core/SigilTraits.h @@ -0,0 +1,23 @@ +#ifndef SORBET_CORE_SIGIL_TRAITS_H +#define SORBET_CORE_SIGIL_TRAITS_H + +#include + +namespace sorbet::core { + +template class SigilTraits { +public: + // The special sigil level that corresponds to the sigil being absent from the file. + static const Sigil NONE; + + // A string like `typed:` or `compiled:` + // If there's meant to be a trailing `:` in the sigil, it should be in this string. + static const std::string_view SIGIL_PREFIX; + + // Produce a Sigil from its string representation `s` + static Sigil fromString(std::string_view s); +}; + +} // namespace sorbet::core + +#endif diff --git a/core/StrictLevel.h b/core/StrictLevel.h index e7eb99a821..5767caa137 100644 --- a/core/StrictLevel.h +++ b/core/StrictLevel.h @@ -1,6 +1,7 @@ #ifndef SORBET_CORE_STRICT_LEVEL_H #define SORBET_CORE_STRICT_LEVEL_H +#include "core/SigilTraits.h" #include namespace sorbet::core { @@ -43,6 +44,34 @@ enum class StrictLevel : uint8_t { // members in classes. Useful for .rbi files from the core and stdlib. Stdlib = 11, }; + +template <> class SigilTraits { +public: + static constexpr StrictLevel NONE = StrictLevel::None; + + static constexpr std::string_view SIGIL_PREFIX = "typed:"; + + static StrictLevel fromString(std::string_view s) { + if (s == "ignore") { + return StrictLevel::Ignore; + } else if (s == "false") { + return StrictLevel::False; + } else if (s == "true") { + return StrictLevel::True; + } else if (s == "strict") { + return StrictLevel::Strict; + } else if (s == "strong") { + return StrictLevel::Strong; + } else if (s == "autogenerated") { + return StrictLevel::Autogenerated; + } else if (s == "__STDLIB_INTERNAL") { + return StrictLevel::Stdlib; + } else { + return StrictLevel::None; + } + } +}; + } // namespace sorbet::core #endif diff --git a/core/SymbolRef.h b/core/SymbolRef.h index 13bc43f89e..86e12e55b5 100644 --- a/core/SymbolRef.h +++ b/core/SymbolRef.h @@ -491,7 +491,7 @@ class SymbolRef final { } bool inline exists() const { - // 0th index is reserved on all symbol vectors for the non existant symbol. + // 0th index is reserved on all symbol vectors for the nonexistent symbol. return unsafeTableIndex() != 0; } @@ -542,7 +542,7 @@ class SymbolRef final { core::Loc loc(const GlobalState &gs) const; bool isPrintable(const GlobalState &gs) const; using LOC_store = InlinedVector; - const LOC_store &locs(const GlobalState &gs) const; + absl::Span locs(const GlobalState &gs) const; void removeLocsForFile(GlobalState &gs, core::FileRef file) const; const TypePtr &resultType(const GlobalState &gs) const; void setResultType(GlobalState &gs, const TypePtr &typePtr) const; @@ -590,6 +590,10 @@ class Symbols { return ClassOrModuleRef(); } + static MethodRef noMethod() { + return MethodRef(); + } + static ClassOrModuleRef top() { return ClassOrModuleRef::fromRaw(1); } @@ -670,6 +674,10 @@ class Symbols { return ClassOrModuleRef::fromRaw(20); } + static MethodRef BasicObject_initialize() { + return MethodRef::fromRaw(1); + } + static ClassOrModuleRef Kernel() { return ClassOrModuleRef::fromRaw(21); } @@ -831,10 +839,6 @@ class Symbols { return ClassOrModuleRef::fromRaw(58); } - static MethodRef noMethod() { - return MethodRef(); - } - static FieldRef noField() { return FieldRef::fromRaw(0); } @@ -848,7 +852,7 @@ class Symbols { } static MethodRef Sorbet_Private_Static_ReturnTypeInference_guessed_type_type_parameter_holder() { - return MethodRef::fromRaw(1); + return MethodRef::fromRaw(2); } static TypeArgumentRef @@ -874,7 +878,7 @@ class Symbols { } static MethodRef Sorbet_Private_Static_badAliasMethodStub() { - return MethodRef::fromRaw(2); + return MethodRef::fromRaw(3); } static ClassOrModuleRef T_Helpers() { @@ -934,7 +938,7 @@ class Symbols { } static MethodRef sig() { - return MethodRef::fromRaw(3); + return MethodRef::fromRaw(4); } static ClassOrModuleRef Enumerator_Lazy() { @@ -973,12 +977,24 @@ class Symbols { return ClassOrModuleRef::fromRaw(82); } + static MethodRef T_Private_Methods_DeclBuilder_abstract() { + return MethodRef::fromRaw(5); + } + + static MethodRef T_Private_Methods_DeclBuilder_overridable() { + return MethodRef::fromRaw(6); + } + + static MethodRef T_Private_Methods_DeclBuilder_override() { + return MethodRef::fromRaw(7); + } + static ClassOrModuleRef T_Sig_WithoutRuntimeSingleton() { return ClassOrModuleRef::fromRaw(83); } static MethodRef sigWithoutRuntime() { - return MethodRef::fromRaw(4); + return MethodRef::fromRaw(8); } static ClassOrModuleRef T_NonForcingConstants() { @@ -986,7 +1002,7 @@ class Symbols { } static MethodRef SorbetPrivateStaticSingleton_sig() { - return MethodRef::fromRaw(5); + return MethodRef::fromRaw(9); } static ClassOrModuleRef PackageSpecRegistry() { @@ -1002,19 +1018,19 @@ class Symbols { } static MethodRef PackageSpec_import() { - return MethodRef::fromRaw(6); + return MethodRef::fromRaw(10); } static MethodRef PackageSpec_test_import() { - return MethodRef::fromRaw(7); + return MethodRef::fromRaw(11); } static MethodRef PackageSpec_export() { - return MethodRef::fromRaw(8); + return MethodRef::fromRaw(12); } static MethodRef PackageSpec_restrict_to_service() { - return MethodRef::fromRaw(9); + return MethodRef::fromRaw(13); } static ClassOrModuleRef Encoding() { @@ -1026,27 +1042,23 @@ class Symbols { } static MethodRef Class_new() { - return MethodRef::fromRaw(10); + return MethodRef::fromRaw(14); } static MethodRef todoMethod() { - return MethodRef::fromRaw(11); + return MethodRef::fromRaw(15); } static MethodRef rootStaticInit() { - return MethodRef::fromRaw(12); - } - - static MethodRef PackageSpec_autoloader_compatibility() { - return MethodRef::fromRaw(13); + return MethodRef::fromRaw(16); } static MethodRef PackageSpec_visible_to() { - return MethodRef::fromRaw(14); + return MethodRef::fromRaw(17); } static MethodRef PackageSpec_export_all() { - return MethodRef::fromRaw(15); + return MethodRef::fromRaw(18); } static ClassOrModuleRef Sorbet_Private_Static_ResolvedSig() { @@ -1057,44 +1069,40 @@ class Symbols { return ClassOrModuleRef::fromRaw(91); } - static ClassOrModuleRef T_Private_Compiler() { + static ClassOrModuleRef MagicBindToAttachedClass() { return ClassOrModuleRef::fromRaw(92); } - static ClassOrModuleRef T_Private_CompilerSingleton() { + static ClassOrModuleRef MagicBindToSelfType() { return ClassOrModuleRef::fromRaw(93); } - static ClassOrModuleRef MagicBindToAttachedClass() { + static ClassOrModuleRef T_Types() { return ClassOrModuleRef::fromRaw(94); } - static ClassOrModuleRef MagicBindToSelfType() { + static ClassOrModuleRef T_Types_Base() { return ClassOrModuleRef::fromRaw(95); } - static ClassOrModuleRef T_Types() { + static ClassOrModuleRef Data() { return ClassOrModuleRef::fromRaw(96); } - static ClassOrModuleRef T_Types_Base() { + static ClassOrModuleRef T_Class() { return ClassOrModuleRef::fromRaw(97); } - static ClassOrModuleRef Data() { - return ClassOrModuleRef::fromRaw(98); - } - - static ClassOrModuleRef T_Class() { - return ClassOrModuleRef::fromRaw(99); + static MethodRef T_Generic_squareBrackets() { + return MethodRef::fromRaw(19); } - static MethodRef T_Generic_squareBrackets() { - return MethodRef::fromRaw(16); + static MethodRef Kernel_lambdaTLet() { + return MethodRef::fromRaw(20); } static ClassOrModuleRef Magic_UntypedSource() { - return ClassOrModuleRef::fromRaw(101); + return ClassOrModuleRef::fromRaw(99); } static FieldRef Magic_UntypedSource_super() { diff --git a/core/Symbols.cc b/core/Symbols.cc index 8fa45bde4a..5548d353b5 100644 --- a/core/Symbols.cc +++ b/core/Symbols.cc @@ -23,10 +23,10 @@ namespace sorbet::core { using namespace std; const int Symbols::MAX_SYNTHETIC_CLASS_SYMBOLS = 215; -const int Symbols::MAX_SYNTHETIC_METHOD_SYMBOLS = 50; +const int Symbols::MAX_SYNTHETIC_METHOD_SYMBOLS = 54; const int Symbols::MAX_SYNTHETIC_FIELD_SYMBOLS = 20; const int Symbols::MAX_SYNTHETIC_TYPEARGUMENT_SYMBOLS = 4; -const int Symbols::MAX_SYNTHETIC_TYPEMEMBER_SYMBOLS = 73; +const int Symbols::MAX_SYNTHETIC_TYPEMEMBER_SYMBOLS = 72; namespace { constexpr string_view COLON_SEPARATOR = "::"sv; @@ -426,7 +426,7 @@ string FieldRef::show(const GlobalState &gs, ShowOptions options) const { string TypeArgumentRef::show(const GlobalState &gs, ShowOptions options) const { auto sym = data(gs); - if (options.showForRBI) { + if (options.useValidSyntax) { return fmt::format("T.type_parameter(:{})", sym->name.show(gs)); } else { return fmt::format("T.type_parameter(:{}) (of {})", sym->name.show(gs), sym->owner.show(gs)); @@ -438,7 +438,7 @@ string TypeMemberRef::show(const GlobalState &gs, ShowOptions options) const { if (sym->name == core::Names::Constants::AttachedClass()) { auto owner = sym->owner.asClassOrModuleRef(); auto attached = owner.data(gs)->attachedClass(gs); - if (options.showForRBI || !attached.exists()) { + if (options.useValidSyntax || !attached.exists()) { // Attached wont exist for a number of cases: // - owner is a module that doesn't use has_attached_class! // - owner is a singleton class of a module @@ -446,7 +446,7 @@ string TypeMemberRef::show(const GlobalState &gs, ShowOptions options) const { } return fmt::format("T.attached_class (of {})", attached.show(gs, options)); } - if (options.showForRBI) { + if (options.useValidSyntax) { return sym->name.show(gs); } return showInternal(gs, sym->owner, sym->name, COLON_SEPARATOR); @@ -528,7 +528,7 @@ uint16_t ClassOrModule::addMixinPlaceholder(const GlobalState &gs) { } SymbolRef ClassOrModule::findMember(const GlobalState &gs, NameRef name) const { - auto ret = findMemberNoDealias(gs, name); + auto ret = findMemberNoDealias(name); if (ret.exists()) { return ret.dealias(gs); } @@ -543,7 +543,7 @@ MethodRef ClassOrModule::findMethod(const GlobalState &gs, NameRef name) const { return Symbols::noMethod(); } -SymbolRef ClassOrModule::findMemberNoDealias(const GlobalState &gs, NameRef name) const { +SymbolRef ClassOrModule::findMemberNoDealias(NameRef name) const { histogramInc("find_member_scope_size", members().size()); auto fnd = members().find(name); if (fnd == members().end()) { @@ -552,8 +552,8 @@ SymbolRef ClassOrModule::findMemberNoDealias(const GlobalState &gs, NameRef name return fnd->second; } -MethodRef ClassOrModule::findMethodNoDealias(const GlobalState &gs, NameRef name) const { - auto sym = findMemberNoDealias(gs, name); +MethodRef ClassOrModule::findMethodNoDealias(NameRef name) const { + auto sym = findMemberNoDealias(name); if (!sym.isMethod()) { return Symbols::noMethod(); } @@ -578,14 +578,19 @@ MethodRef ClassOrModule::findMethodTransitive(const GlobalState &gs, NameRef nam return Symbols::noMethod(); } -bool singleFileDefinition(const GlobalState &gs, const core::SymbolRef::LOC_store &locs, core::FileRef file) { +MethodRef ClassOrModule::findParentMethodTransitive(const GlobalState &gs, NameRef name) const { + auto dealias = true; + auto sym = findParentMemberTransitiveInternal(gs, name, 100, dealias); + if (sym.exists() && sym.isMethod()) { + return sym.asMethodRef(); + } + return Symbols::noMethod(); +} + +bool singleFileDefinition(const GlobalState &gs, absl::Span locs, core::FileRef file) { bool result = false; for (auto &loc : locs) { - if (loc.file().data(gs).isRBI()) { - continue; - } - if (loc.file() != file) { return false; } @@ -596,12 +601,8 @@ bool singleFileDefinition(const GlobalState &gs, const core::SymbolRef::LOC_stor return result; } -// Returns true if the given symbol is only defined in a given file (not accounting for RBIs). +// Returns true if the given symbol is only defined in a given file. bool SymbolRef::isOnlyDefinedInFile(const GlobalState &gs, core::FileRef file) const { - if (file.data(gs).isRBI()) { - return false; - } - return singleFileDefinition(gs, locs(gs), file); } @@ -745,6 +746,32 @@ MethodRef ClassOrModule::findConcreteMethodTransitive(const GlobalState &gs, Nam return findConcreteMethodTransitiveInternal(gs, this->ref(gs), name, 100); } +SymbolRef ClassOrModule::findParentMemberTransitiveInternal(const GlobalState &gs, NameRef name, int maxDepth, + bool dealias) const { + SymbolRef result; + if (flags.isLinearizationComputed) { + for (auto it = this->mixins().begin(); it != this->mixins().end(); ++it) { + ENFORCE(it->exists()); + result = dealias ? it->data(gs)->findMember(gs, name) : it->data(gs)->findMemberNoDealias(name); + if (result.exists()) { + return result; + } + } + } else { + for (auto it = this->mixins().rbegin(); it != this->mixins().rend(); ++it) { + ENFORCE(it->exists()); + result = it->data(gs)->findMemberTransitiveInternal(gs, name, maxDepth - 1, dealias); + if (result.exists()) { + return result; + } + } + } + if (this->superClass().exists()) { + return this->superClass().data(gs)->findMemberTransitiveInternal(gs, name, maxDepth - 1, dealias); + } + return Symbols::noSymbol(); +} + SymbolRef ClassOrModule::findMemberTransitiveInternal(const GlobalState &gs, NameRef name, int maxDepth, bool dealias) const { if (maxDepth == 0) { @@ -770,32 +797,12 @@ SymbolRef ClassOrModule::findMemberTransitiveInternal(const GlobalState &gs, Nam Exception::raise("findMemberTransitive hit a loop while resolving"); } - SymbolRef result = dealias ? findMember(gs, name) : findMemberNoDealias(gs, name); + SymbolRef result = dealias ? findMember(gs, name) : findMemberNoDealias(name); if (result.exists()) { return result; } - if (flags.isLinearizationComputed) { - for (auto it = this->mixins().begin(); it != this->mixins().end(); ++it) { - ENFORCE(it->exists()); - result = dealias ? it->data(gs)->findMember(gs, name) : it->data(gs)->findMemberNoDealias(gs, name); - if (result.exists()) { - return result; - } - result = core::Symbols::noSymbol(); - } - } else { - for (auto it = this->mixins().rbegin(); it != this->mixins().rend(); ++it) { - ENFORCE(it->exists()); - result = it->data(gs)->findMemberTransitiveInternal(gs, name, maxDepth - 1, dealias); - if (result.exists()) { - return result; - } - } - } - if (this->superClass().exists()) { - return this->superClass().data(gs)->findMemberTransitiveInternal(gs, name, maxDepth - 1, dealias); - } - return Symbols::noSymbol(); + + return findParentMemberTransitiveInternal(gs, name, maxDepth, dealias); } vector ClassOrModule::findMemberFuzzyMatch(const GlobalState &gs, NameRef name, @@ -1081,7 +1088,7 @@ bool isHiddenFromPrinting(const GlobalState &gs, SymbolRef symbol) { return false; } -void printLocs(const GlobalState &gs, fmt::memory_buffer &buf, const InlinedVector &locs, bool showRaw) { +void printLocs(const GlobalState &gs, fmt::memory_buffer &buf, absl::Span locs, bool showRaw) { if (!locs.empty()) { fmt::format_to(std::back_inserter(buf), " @ "); if (locs.size() > 1) { @@ -1600,7 +1607,7 @@ bool SymbolRef::isPrintable(const GlobalState &gs) const { } } -const InlinedVector &SymbolRef::locs(const GlobalState &gs) const { +absl::Span SymbolRef::locs(const GlobalState &gs) const { switch (kind()) { case SymbolRef::Kind::ClassOrModule: return asClassOrModuleRef().data(gs)->locs(); @@ -1883,10 +1890,10 @@ void ClassOrModule::recordSealedSubclass(GlobalState &gs, ClassOrModuleRef subcl } } -const InlinedVector &ClassOrModule::sealedLocs(const GlobalState &gs) const { +absl::Span ClassOrModule::sealedLocs(const GlobalState &gs) const { ENFORCE(this->flags.isSealed, "Class is not marked sealed: {}", ref(gs).show(gs)); auto sealedSubclasses = this->lookupSingletonClass(gs).data(gs)->findMethod(gs, core::Names::sealedSubclasses()); - auto &result = sealedSubclasses.data(gs)->locs(); + auto result = sealedSubclasses.data(gs)->locs(); ENFORCE(result.size() > 0); return result; } @@ -2593,19 +2600,19 @@ Loc TypeParameter::loc() const { return Loc::none(); } -const InlinedVector &Method::locs() const { +absl::Span Method::locs() const { return locs_; } -const InlinedVector &ClassOrModule::locs() const { +absl::Span ClassOrModule::locs() const { return locs_; } -const InlinedVector &Field::locs() const { +absl::Span Field::locs() const { return locs_; } -const InlinedVector &TypeParameter::locs() const { +absl::Span TypeParameter::locs() const { return locs_; } diff --git a/core/Symbols.h b/core/Symbols.h index 500aca0440..823303fb00 100644 --- a/core/Symbols.h +++ b/core/Symbols.h @@ -95,7 +95,7 @@ class Method final { CheckSize(Flags, 2, 1); Loc loc() const; - const SymbolRef::LOC_store &locs() const; + absl::Span locs() const; void addLoc(const core::GlobalState &gs, core::Loc loc); void removeLocsForFile(core::FileRef file); uint32_t hash(const GlobalState &gs) const; @@ -244,7 +244,7 @@ class Field final { CheckSize(Flags, 1, 1); Loc loc() const; - const SymbolRef::LOC_store &locs() const; + absl::Span locs() const; void addLoc(const core::GlobalState &gs, core::Loc loc); void removeLocsForFile(core::FileRef file); @@ -321,7 +321,7 @@ class TypeParameter final { CheckSize(Flags, 1, 1); Loc loc() const; - const SymbolRef::LOC_store &locs() const; + absl::Span locs() const; void addLoc(const core::GlobalState &gs, core::Loc loc); void removeLocsForFile(core::FileRef file); @@ -399,7 +399,7 @@ class ClassOrModule final { CheckSize(Flags, 2, 1); Loc loc() const; - const SymbolRef::LOC_store &locs() const; + absl::Span locs() const; void addLoc(const core::GlobalState &gs, core::Loc loc); void removeLocsForFile(core::FileRef file); @@ -512,13 +512,20 @@ class ClassOrModule final { } } + inline void unsetClassOrModuleLinearizationComputed() { + flags.isLinearizationComputed = false; + } + SymbolRef findMember(const GlobalState &gs, NameRef name) const; MethodRef findMethod(const GlobalState &gs, NameRef name) const; - SymbolRef findMemberNoDealias(const GlobalState &gs, NameRef name) const; - MethodRef findMethodNoDealias(const GlobalState &gs, NameRef name) const; + SymbolRef findMemberNoDealias(NameRef name) const; + MethodRef findMethodNoDealias(NameRef name) const; SymbolRef findMemberTransitive(const GlobalState &gs, NameRef name) const; SymbolRef findMemberTransitiveNoDealias(const GlobalState &gs, NameRef name) const; MethodRef findMethodTransitive(const GlobalState &gs, NameRef name) const; + // A version of findMemberTransitive that skips looking in the members of the current symbol, + // instead looking only in the members of any parent. + MethodRef findParentMethodTransitive(const GlobalState &gs, NameRef name) const; MethodRef findConcreteMethodTransitive(const GlobalState &gs, NameRef name) const; /* transitively finds a member with the most similar name */ @@ -549,7 +556,7 @@ class ClassOrModule final { void recordSealedSubclass(GlobalState &gs, ClassOrModuleRef subclass); // Returns the locations that are allowed to subclass the sealed class. - const SymbolRef::LOC_store &sealedLocs(const GlobalState &gs) const; + absl::Span sealedLocs(const GlobalState &gs) const; TypePtr sealedSubclassesToUnion(const GlobalState &ctx) const; @@ -588,8 +595,8 @@ class ClassOrModule final { return superClass_; } - inline void setSuperClass(ClassOrModuleRef claz) { - superClass_ = claz; + inline void setSuperClass(ClassOrModuleRef klass) { + superClass_ = klass; } Flags flags; @@ -644,14 +651,11 @@ class ClassOrModule final { std::vector &seen); SymbolRef findMemberTransitiveInternal(const GlobalState &gs, NameRef name, int maxDepth, bool dealias) const; - - inline void unsetClassOrModuleLinearizationComputed() { - flags.isLinearizationComputed = false; - } + SymbolRef findParentMemberTransitiveInternal(const GlobalState &gs, NameRef name, int maxDepth, bool dealias) const; void addMixinAt(ClassOrModuleRef sym, std::optional index); }; -CheckSize(ClassOrModule, 128, 8); +CheckSize(ClassOrModule, 120, 8); } // namespace sorbet::core #endif // SORBET_SYMBOLS_H diff --git a/core/TrackUntyped.h b/core/TrackUntyped.h new file mode 100644 index 0000000000..bf61073ddd --- /dev/null +++ b/core/TrackUntyped.h @@ -0,0 +1,14 @@ +#ifndef SORBET_CORE_TRACK_UNTYPED_H +#define SORBET_CORE_TRACK_UNTYPED_H + +#include + +namespace sorbet::core { +enum class TrackUntyped : uint8_t { + Nowhere, + EverywhereButTests, + Everywhere, +}; +} // namespace sorbet::core + +#endif diff --git a/core/TypeConstraint.cc b/core/TypeConstraint.cc index d459717789..b2a62a8a02 100644 --- a/core/TypeConstraint.cc +++ b/core/TypeConstraint.cc @@ -33,7 +33,7 @@ bool TypeConstraint::solve(const GlobalState &gs) { return true; } - // instatiate types to upper bound approximations + // instantiate types to upper bound approximations for (auto &k : upperBounds) { auto &tv = k.first; auto &bound = k.second; @@ -265,7 +265,7 @@ string TypeConstraint::toString(const core::GlobalState &gs) const { fmt::memory_buffer buf; fmt::format_to(std::back_inserter(buf), "bounds: [{}]\n", fmt::map_join( - collated.begin(), collated.end(), ", ", [&gs](auto entry) -> auto { + collated.begin(), collated.end(), ", ", [&gs](auto entry) -> auto{ const auto &[sym, bounds] = entry; const auto &[lowerBound, upperBound] = bounds; auto lower = lowerBound != nullptr ? lowerBound.show(gs) : "_"; @@ -274,7 +274,7 @@ string TypeConstraint::toString(const core::GlobalState &gs) const { })); fmt::format_to(std::back_inserter(buf), "solution: [{}]\n", fmt::map_join( - this->solution.begin(), this->solution.end(), ", ", [&gs](auto pair) -> auto { + this->solution.begin(), this->solution.end(), ", ", [&gs](auto pair) -> auto{ return fmt::format("{}: {}", pair.first.show(gs), pair.second.show(gs)); })); return to_string(buf); diff --git a/core/TypeErrorDiagnostics.cc b/core/TypeErrorDiagnostics.cc index 78af95bd1e..dbd6ecd2bf 100644 --- a/core/TypeErrorDiagnostics.cc +++ b/core/TypeErrorDiagnostics.cc @@ -35,8 +35,12 @@ namespace { } } // namespace -void TypeErrorDiagnostics::explainTypeMismatch(const GlobalState &gs, ErrorBuilder &e, const TypePtr &expected, +void TypeErrorDiagnostics::explainTypeMismatch(const GlobalState &gs, ErrorBuilder &e, + const ErrorSection::Collector &collector, const TypePtr &expected, const TypePtr &got) { + e.addErrorSections(std::move(collector)); + // TODO: the rest of this function should eventually be moved to isSubTypeUnderConstraint, + // as calls to collector.addErrorDetails auto expectedSelfTypeParam = isa_type(expected); auto gotClassType = isa_type(got); if (expectedSelfTypeParam && gotClassType) { @@ -81,12 +85,13 @@ void TypeErrorDiagnostics::maybeAutocorrect(const GlobalState &gs, ErrorBuilder auto withoutNil = Types::dropNil(gs, actualType); if (!withoutNil.isBottom() && - Types::isSubTypeUnderConstraint(gs, *constr, withoutNil, expectedType, UntypedMode::AlwaysCompatible)) { + Types::isSubTypeUnderConstraint(gs, *constr, withoutNil, expectedType, UntypedMode::AlwaysCompatible, + ErrorSection::Collector::NO_OP)) { e.replaceWith("Wrap in `T.must`", loc, "T.must({})", loc.source(gs).value()); } else if (isa_type(actualType) && !isa_type(expectedType) && - core::Types::isSubTypeUnderConstraint(gs, *constr, - core::Symbols::T_Types_Base().data(gs)->externalType(), - expectedType, UntypedMode::AlwaysCompatible)) { + core::Types::isSubTypeUnderConstraint( + gs, *constr, core::Symbols::T_Types_Base().data(gs)->externalType(), expectedType, + UntypedMode::AlwaysCompatible, ErrorSection::Collector::NO_OP)) { e.replaceWith("Wrap in `T::Utils.coerce`", loc, "T::Utils.coerce({})", loc.source(gs).value()); } } @@ -122,15 +127,21 @@ void TypeErrorDiagnostics::insertTypeArguments(const GlobalState &gs, ErrorBuild void TypeErrorDiagnostics::explainUntyped(const GlobalState &gs, ErrorBuilder &e, ErrorClass what, const TypeAndOrigins &untyped, Loc originForUninitialized) { e.addErrorSection(untyped.explainGot(gs, originForUninitialized)); - if (what == core::errors::Infer::UntypedValue) { - e.addErrorNote("Support for `{}` is minimal. Consider using `{}` instead.", "typed: strong", "typed: strict"); + + if constexpr (sorbet::track_untyped_blame_mode || sorbet::debug_mode) { + auto blameSymbol = untyped.type.untypedBlame(); + if (blameSymbol.exists()) { + auto loc = blameSymbol.loc(gs); + if (loc.exists()) { + e.addErrorLine(loc, "Blames to `{}`, defined here", blameSymbol.show(gs)); + } else { + e.addErrorNote("Blames to `{}`", blameSymbol.show(gs)); + } + } else { + e.addErrorNote("Blames to `{}`", ""); + } } -} -void TypeErrorDiagnostics::explainUntyped(const GlobalState &gs, ErrorBuilder &e, ErrorClass what, TypePtr untyped, - Loc origin, Loc originForUninitialized) { - auto untypedTpo = TypeAndOrigins{untyped, origin}; - e.addErrorSection(untypedTpo.explainGot(gs, originForUninitialized)); if (what == core::errors::Infer::UntypedValue) { e.addErrorNote("Support for `{}` is minimal. Consider using `{}` instead.", "typed: strong", "typed: strict"); } @@ -177,7 +188,7 @@ TypeErrorDiagnostics::editForDSLMethod(const GlobalState &gs, FileRef fileToEdit } auto inCurrentFile = [&](const auto &loc) { return loc.file() == fileToEdit; }; - auto &classLocs = inWhat->locs(); + auto classLocs = inWhat->locs(); auto classLoc = absl::c_find_if(classLocs, inCurrentFile); if (classLoc == classLocs.end()) { diff --git a/core/TypeErrorDiagnostics.h b/core/TypeErrorDiagnostics.h index 6c663deb14..a83aced068 100644 --- a/core/TypeErrorDiagnostics.h +++ b/core/TypeErrorDiagnostics.h @@ -20,8 +20,8 @@ class TypeErrorDiagnostics final { static void maybeAutocorrect(const GlobalState &gs, ErrorBuilder &e, Loc loc, const TypeConstraint &constr, const TypePtr &expectedType, const TypePtr &actualType); - static void explainTypeMismatch(const GlobalState &gs, ErrorBuilder &e, const TypePtr &expected, - const TypePtr &got); + static void explainTypeMismatch(const GlobalState &gs, ErrorBuilder &e, const ErrorSection::Collector &collector, + const TypePtr &expected, const TypePtr &got); static void insertTypeArguments(const GlobalState &gs, ErrorBuilder &e, ClassOrModuleRef klass, core::Loc replaceLoc); @@ -29,9 +29,6 @@ class TypeErrorDiagnostics final { static void explainUntyped(const GlobalState &gs, ErrorBuilder &e, ErrorClass what, const TypeAndOrigins &untyped, Loc originForUninitialized); - static void explainUntyped(const GlobalState &gs, ErrorBuilder &e, ErrorClass what, TypePtr untyped, Loc origin, - Loc originForUninitialized); - static std::optional editForDSLMethod(const GlobalState &gs, FileRef fileToEdit, Loc defaultInsertLoc, ClassOrModuleRef inWhat, ClassOrModuleRef dslOwner, std::string_view dsl); diff --git a/core/TypePtr.cc b/core/TypePtr.cc index 00d4d5badb..ecf06bd4df 100644 --- a/core/TypePtr.cc +++ b/core/TypePtr.cc @@ -221,6 +221,44 @@ bool TypePtr::hasUntyped() const { } } +bool TypePtr::hasTopLevelVoid() const { + switch (tag()) { + case Tag::TypeVar: + case Tag::NamedLiteralType: + case Tag::IntegerLiteralType: + case Tag::FloatLiteralType: + case Tag::SelfType: + case Tag::AliasType: + case Tag::SelfTypeParam: + case Tag::LambdaParam: + case Tag::MetaType: + case Tag::BlamedUntyped: + case Tag::UnresolvedAppliedType: + case Tag::UnresolvedClassType: + // These cannot have void. + return false; + + case Tag::AppliedType: + case Tag::TupleType: + case Tag::ShapeType: + // Unlike hasUntyped, we do not look in type args for this method. + return false; + + case Tag::ClassType: { + auto c = cast_type_nonnull(*this); + return c.symbol == Symbols::void_(); + } + case Tag::OrType: { + auto &o = cast_type_nonnull(*this); + return o.left.hasTopLevelVoid() || o.right.hasTopLevelVoid(); + } + case Tag::AndType: { + auto &a = cast_type_nonnull(*this); + return a.left.hasTopLevelVoid() || a.right.hasTopLevelVoid(); + } + } +} + core::SymbolRef TypePtr::untypedBlame() const { ENFORCE(hasUntyped()); if (isa_type(*this)) { diff --git a/core/TypePtr.h b/core/TypePtr.h index 6cc47e4e9f..4f185a9289 100644 --- a/core/TypePtr.h +++ b/core/TypePtr.h @@ -71,8 +71,12 @@ class TypePtr final { // A mapping from type to the type returned by `cast_type_nonnull`. template struct TypeToCastType {}; - template struct TypeToCastType { using type = T; }; - template struct TypeToCastType { using type = const T &; }; + template struct TypeToCastType { + using type = T; + }; + template struct TypeToCastType { + using type = const T &; + }; // Required for typecase. template static bool isa(const TypePtr &what); @@ -84,7 +88,7 @@ class TypePtr final { template static typename TypeToCastType::value>::type cast(TypePtr &&what) = delete; - // Disallowing this and requiring people to cast to `const TypePtr &` is simplier + // Disallowing this and requiring people to cast to `const TypePtr &` is simpler // than creating a parallel `TypeToCastType` for non-const argument types. template static auto cast(TypePtr &what) = delete; @@ -255,6 +259,20 @@ class TypePtr final { bool hasUntyped() const; + // This is a hacky, unprincipled method that simply looks for void inside the type at the top + // level. + // + // Before using this method or attempting to cargo-cult it, instead you almost certainly want to + // take the principled approach of composing calls methods like `Types::all` or `isSubType` or + // `dropSubtypesOf` (etc.). + // + // We can't use those for this method because: + // + // - Checking whether a type uses `void` can't use `isSubType`, because that will say that + // `Object` etc. "use" void. + // - Using `dropSubtypesOf` sometimes expand sealed classes to a union of subclasses. + bool hasTopLevelVoid() const; + void sanityCheck(const GlobalState &gs) const { if constexpr (!debug_mode) return; diff --git a/core/Types.h b/core/Types.h index f5f43dc51c..4b3991e290 100644 --- a/core/Types.h +++ b/core/Types.h @@ -71,7 +71,7 @@ class Types final { /** Greater lower bound: the widest type that is subtype of both t1 and t2 */ static TypePtr all(const GlobalState &gs, const TypePtr &t1, const TypePtr &t2); - /** Lower upper bound: the narrowest type that is supper type of both t1 and t2 */ + /** Lower upper bound: the narrowest type that is super type of both t1 and t2 */ static TypePtr any(const GlobalState &gs, const TypePtr &t1, const TypePtr &t2); /** @@ -79,20 +79,40 @@ class Types final { * * The parameter `mode` controls whether or not `T.untyped` is * considered to be a super type or subtype of all other types */ + + /** + * The `errorDetailsCollector` parameter is used to pass additional details out of isSubType + * about why subtyping failed, which can then be shown to the user. See ErrorSection::Collector + * in core/Error.h for the API. + * + * If this call is going to be used to determine if an error should be shown, you should pass in + * an instance of ErrorSection::Collector. Otherwise, you should pass in + * ErrorSection::Collector::NO_OP, as passing an ErrorSection::Collector will slow down subtype + * checking to collect the additional information. + */ + template static bool isSubTypeUnderConstraint(const GlobalState &gs, TypeConstraint &constr, const TypePtr &t1, - const TypePtr &t2, UntypedMode mode); + const TypePtr &t2, UntypedMode mode, T &errorDetailsCollector); /** is every instance of t1 an instance of t2 when not allowed to modify constraint */ - static bool isSubType(const GlobalState &gs, const TypePtr &t1, const TypePtr &t2); + template + static bool isSubType(const GlobalState &gs, const TypePtr &t1, const TypePtr &t2, T &errorDetailsCollector); + /** is every instance of t1 an instance of t2 when not allowed to modify constraint */ + static bool isSubType(const GlobalState &gs, const TypePtr &t1, const TypePtr &t2) { + return isSubType(gs, t1, t2, ErrorSection::Collector::NO_OP); + }; static bool equiv(const GlobalState &gs, const TypePtr &t1, const TypePtr &t2); + template static bool equivUnderConstraint(const GlobalState &gs, TypeConstraint &constr, const TypePtr &t1, - const TypePtr &t2); + const TypePtr &t2, T &errorDetailsCollector); /** check that t1 <: t2, but do not consider `T.untyped` as super type or a subtype of all other types */ static bool isAsSpecificAs(const GlobalState &gs, const TypePtr &t1, const TypePtr &t2); static bool equivNoUntyped(const GlobalState &gs, const TypePtr &t1, const TypePtr &t2); + + template static bool equivNoUntypedUnderConstraint(const GlobalState &gs, TypeConstraint &constr, const TypePtr &t1, - const TypePtr &t2); + const TypePtr &t2, T &errorDetailsCollector); static TypePtr top(); static TypePtr bottom(); @@ -116,9 +136,11 @@ class Types final { static TypePtr nilableProcClass(); static TypePtr declBuilderForProcsSingletonClass(); static TypePtr falsyTypes(); + static absl::Span falsySymbols(); static TypePtr todo(); - static TypePtr dropSubtypesOf(const GlobalState &gs, const TypePtr &from, ClassOrModuleRef klass); + static TypePtr dropSubtypesOf(const GlobalState &gs, const TypePtr &from, + absl::Span klasses); static TypePtr approximateSubtract(const GlobalState &gs, const TypePtr &from, const TypePtr &what); static bool canBeTruthy(const GlobalState &gs, const TypePtr &what); static bool canBeFalsy(const GlobalState &gs, const TypePtr &what); @@ -141,7 +163,7 @@ class Types final { static TypePtr replaceSelfType(const GlobalState &gs, const TypePtr &what, const TypePtr &receiver); /** Get rid of type variables in `what` and return a type that we deem close enough to continue * typechecking. We should be careful to only used this type when we are trying to guess a type. - * We should do proper instatiation and subtype test after we have guessed type variables with + * We should do proper instantiation and subtype test after we have guessed type variables with * tc.solve(). If the constraint has already been solved, use `instantiate` instead. */ static TypePtr approximate(const GlobalState &gs, const TypePtr &what, const TypeConstraint &tc); @@ -334,16 +356,22 @@ template <> inline bool TypePtr::isa(const TypePtr &what) { } // Required to support cast specialization. -template <> struct TypePtr::TypeToIsInlined { static constexpr bool value = false; }; +template <> struct TypePtr::TypeToIsInlined { + static constexpr bool value = false; +}; template <> inline TypePtr const &TypePtr::cast(const TypePtr &what) { return what; } -#define TYPE_IMPL(name, isInlined) \ - class name; \ - template <> struct TypePtr::TypeToTag { static constexpr TypePtr::Tag value = TypePtr::Tag::name; }; \ - template <> struct TypePtr::TypeToIsInlined { static constexpr bool value = isInlined; }; \ +#define TYPE_IMPL(name, isInlined) \ + class name; \ + template <> struct TypePtr::TypeToTag { \ + static constexpr TypePtr::Tag value = TypePtr::Tag::name; \ + }; \ + template <> struct TypePtr::TypeToIsInlined { \ + static constexpr bool value = isInlined; \ + }; \ class __attribute__((aligned(8))) name #define TYPE(name) TYPE_IMPL(name, false) @@ -707,17 +735,19 @@ TYPE(OrType) final : public Refcounted { friend TypePtr Types::Boolean(); friend class NameSubstitution; friend class serialize::SerializerImpl; + template friend bool Types::isSubTypeUnderConstraint(const GlobalState &gs, TypeConstraint &constr, const TypePtr &t1, - const TypePtr &t2, UntypedMode mode); + const TypePtr &t2, UntypedMode mode, T &errorDetailsCollector); friend TypePtr lubDistributeOr(const GlobalState &gs, const TypePtr &t1, const TypePtr &t2); friend TypePtr lubGround(const GlobalState &gs, const TypePtr &t1, const TypePtr &t2); friend TypePtr Types::lub(const GlobalState &gs, const TypePtr &t1, const TypePtr &t2); friend TypePtr Types::glb(const GlobalState &gs, const TypePtr &t1, const TypePtr &t2); friend TypePtr filterOrComponents(const TypePtr &originalType, const InlinedVector &typeFilter); - friend TypePtr Types::dropSubtypesOf(const GlobalState &gs, const TypePtr &from, ClassOrModuleRef klass); + friend TypePtr Types::dropSubtypesOf(const GlobalState &gs, const TypePtr &from, + absl::Span klasses); friend TypePtr Types::unwrapSelfTypeParam(Context ctx, const TypePtr &t1); friend class ClassOrModule; // the actual method is `recordSealedSubclass(Mutableconst GlobalState &gs, SymbolRef - // subclass)`, but refering to it introduces a cycle + // subclass)`, but referring to it introduces a cycle static TypePtr make_shared(const TypePtr &left, const TypePtr &right); }; @@ -757,8 +787,9 @@ TYPE(AndType) final : public Refcounted { friend class serialize::SerializerImpl; friend class TypeConstraint; + template friend bool Types::isSubTypeUnderConstraint(const GlobalState &gs, TypeConstraint &constr, const TypePtr &t1, - const TypePtr &t2, UntypedMode mode); + const TypePtr &t2, UntypedMode mode, T &errorDetailsCollector); friend TypePtr lubGround(const GlobalState &gs, const TypePtr &t1, const TypePtr &t2); friend TypePtr glbDistributeAnd(const GlobalState &gs, const TypePtr &t1, const TypePtr &t2); friend TypePtr glbGround(const GlobalState &gs, const TypePtr &t1, const TypePtr &t2); @@ -970,6 +1001,7 @@ struct DispatchArgs { // are cases where we call dispatchCall with no intention of showing the errors to the user. Producing those // unreported errors is expensive! bool suppressErrors; + NameRef enclosingMethodForSuper; DispatchArgs(const DispatchArgs &) = delete; DispatchArgs &operator=(const DispatchArgs &) = delete; @@ -1006,6 +1038,18 @@ struct DispatchArgs { } Loc blockLoc(const GlobalState &gs) const; + Loc errLoc() const { + auto funLoc = this->funLoc(); + auto recvLoc = this->receiverLoc(); + if (funLoc.exists() && !funLoc.empty()) { + return funLoc; + } else if (this->name == Names::squareBrackets() && recvLoc.exists() && !recvLoc.empty()) { + return core::Loc(this->locs.file, recvLoc.endPos(), this->callLoc().endPos()); + } else { + return this->callLoc(); + } + } + DispatchArgs withSelfAndThisRef(const TypePtr &newSelfRef) const; DispatchArgs withThisRef(const TypePtr &newThisRef) const; DispatchArgs withErrorsSuppressed() const; @@ -1018,7 +1062,8 @@ struct DispatchComponent { TypePtr sendTp; TypePtr blockReturnType; TypePtr blockPreType; - ArgInfo blockSpec; // used only by LoadSelf to change type of self inside method. + ClassOrModuleRef rebind; + Loc rebindLoc; std::unique_ptr constr; }; @@ -1033,7 +1078,7 @@ struct DispatchResult { DispatchResult(TypePtr returnType, TypePtr receiverType, core::MethodRef method) : returnType(returnType), main(DispatchComponent{ - std::move(receiverType), method, {}, std::move(returnType), nullptr, nullptr, ArgInfo{}, nullptr}){}; + std::move(receiverType), method, {}, std::move(returnType), nullptr, nullptr, {}, {}, nullptr}){}; DispatchResult(TypePtr returnType, DispatchComponent comp) : returnType(std::move(returnType)), main(std::move(comp)){}; DispatchResult(TypePtr returnType, DispatchComponent comp, std::unique_ptr secondary, diff --git a/core/errors/infer.cc b/core/errors/infer.cc index d7fe3e7016..8ec986b5d1 100644 --- a/core/errors/infer.cc +++ b/core/errors/infer.cc @@ -5,11 +5,16 @@ namespace sorbet::core::errors::Infer { ErrorClass errorClassForUntyped(const GlobalState &gs, FileRef file, const TypePtr &untyped) { prodCounterInc("types.input.untyped.usages"); - if (!gs.trackUntyped) { + if (gs.trackUntyped == TrackUntyped::Nowhere) { return UntypedValue; } - auto isOpenInClient = file.data(gs).isOpenInClient(); + const auto &fileData = file.data(gs); + if (gs.trackUntyped == TrackUntyped::EverywhereButTests && fileData.isPackagedTest()) { + return UntypedValue; + } + + auto isOpenInClient = fileData.isOpenInClient(); if (gs.printingFileTable) { // Note: this metric, despite being a prod metric, will not get reported in the normal way // to the metrics file, the web trace file, nor statsd. We call getAndClearHistogram BEFORE @@ -32,7 +37,7 @@ ErrorClass errorClassForUntyped(const GlobalState &gs, FileRef file, const TypeP prodHistogramInc("untyped.blames", untyped.untypedBlame().rawId()); } - if (isOpenInClient && file.data(gs).strictLevel < core::StrictLevel::Strong) { + if (isOpenInClient && fileData.strictLevel < core::StrictLevel::Strong) { return UntypedValueInformation; } else { return UntypedValue; diff --git a/core/errors/infer.h b/core/errors/infer.h index c03148c52c..06629462b7 100644 --- a/core/errors/infer.h +++ b/core/errors/infer.h @@ -51,6 +51,10 @@ constexpr ErrorClass DigExtraArgs{7044, StrictLevel::True}; constexpr ErrorClass IncorrectlyAssumedType{7045, StrictLevel::True}; constexpr ErrorClass NonOverlappingEqual{7046, StrictLevel::True}; constexpr ErrorClass UntypedValueInformation{7047, StrictLevel::True}; +constexpr ErrorClass UnknownSuperMethod{7048, StrictLevel::True}; +constexpr ErrorClass TypecheckOverloadBody{7049, StrictLevel::True}; +constexpr ErrorClass MustOnUntyped{7050, StrictLevel::Strict}; +constexpr ErrorClass BranchOnVoid{7051, StrictLevel::True}; // N.B infer does not run for untyped call at all. StrictLevel::False here would be meaningless ErrorClass errorClassForUntyped(const GlobalState &gs, FileRef file, const TypePtr &ptr); diff --git a/core/errors/namer.h b/core/errors/namer.h index a00eab59c4..33e3fb5b68 100644 --- a/core/errors/namer.h +++ b/core/errors/namer.h @@ -3,7 +3,7 @@ #include "core/Error.h" namespace sorbet::core::errors::Namer { -constexpr ErrorClass IncludeMutipleParam{4001, StrictLevel::False}; +constexpr ErrorClass IncludeMultipleParam{4001, StrictLevel::False}; constexpr ErrorClass AncestorNotConstant{4002, StrictLevel::False}; constexpr ErrorClass IncludePassedBlock{4003, StrictLevel::False}; // constexpr ErrorClass DynamicConstantDefinition{4004, StrictLevel::True}; @@ -17,7 +17,7 @@ constexpr ErrorClass InvalidTypeDefinition{4011, StrictLevel::False}; constexpr ErrorClass ModuleKindRedefinition{4012, StrictLevel::False}; constexpr ErrorClass InterfaceClass{4013, StrictLevel::False}; constexpr ErrorClass DynamicConstant{4014, StrictLevel::False}; -constexpr ErrorClass InvalidClassOwner{4015, StrictLevel::False}; +// constexpr ErrorClass InvalidClassOwner{4015, StrictLevel::False}; constexpr ErrorClass RootTypeMember{4016, StrictLevel::False}; // constexpr ErrorClass DynamicConstantAssignment{4017, StrictLevel::False}; // constexpr ErrorClass RepeatedArgument{4018, StrictLevel::False}; diff --git a/core/errors/packager.h b/core/errors/packager.h index bef6907c99..a773ad8f21 100644 --- a/core/errors/packager.h +++ b/core/errors/packager.h @@ -18,7 +18,7 @@ constexpr ErrorClass InvalidPackageExpression{3710, StrictLevel::False}; constexpr ErrorClass PackageFileMustBeStrict{3711, StrictLevel::False}; constexpr ErrorClass InvalidPackageName{3712, StrictLevel::False}; constexpr ErrorClass DefinitionPackageMismatch{3713, StrictLevel::False}; -constexpr ErrorClass ImportConflict{3714, StrictLevel::False}; +// constexpr ErrorClass ImportConflict{3714, StrictLevel::False}; // constexpr ErrorClass InvalidExportForTest{3715, StrictLevel::False}; constexpr ErrorClass ExportConflict{3716, StrictLevel::False}; constexpr ErrorClass UsedPackagePrivateName{3717, StrictLevel::False}; diff --git a/core/errors/resolver.h b/core/errors/resolver.h index a53793f7ab..27b10ce1c6 100644 --- a/core/errors/resolver.h +++ b/core/errors/resolver.h @@ -27,7 +27,7 @@ constexpr ErrorClass AbstractMethodOutsideAbstract{5021, StrictLevel::False}; constexpr ErrorClass ConcreteMethodInInterface{5022, StrictLevel::False}; constexpr ErrorClass BadAbstractMethod{5023, StrictLevel::False}; constexpr ErrorClass RecursiveTypeAlias{5024, StrictLevel::False}; -constexpr ErrorClass TypeAliasInGenericClass{5025, StrictLevel::False}; +// constexpr ErrorClass TypeAliasInGenericClass{5025, StrictLevel::False}; constexpr ErrorClass BadStdlibGeneric{5026, StrictLevel::False}; constexpr ErrorClass OutOfOrderConstantAccess{5027, StrictLevel::False}; @@ -80,6 +80,7 @@ constexpr ErrorClass BindNonBlockParameter{5071, StrictLevel::False}; constexpr ErrorClass TypeMemberScopeMismatch{5072, StrictLevel::False}; constexpr ErrorClass AbstractClassInstantiated{5073, StrictLevel::True}; constexpr ErrorClass HasAttachedClassIncluded{5074, StrictLevel::False}; +constexpr ErrorClass TypeAliasToTypeMember{5075, StrictLevel::False}; } // namespace sorbet::core::errors::Resolver #endif diff --git a/core/errors/rewriter.h b/core/errors/rewriter.h index 1e095b72d4..fbb7a2eda5 100644 --- a/core/errors/rewriter.h +++ b/core/errors/rewriter.h @@ -4,7 +4,7 @@ namespace sorbet::core::errors::Rewriter { constexpr ErrorClass BadAttrArg{3501, StrictLevel::True}; -constexpr ErrorClass BadWrapInstance{3502, StrictLevel::True}; +// constexpr ErrorClass BadWrapInstance{3502, StrictLevel::True}; constexpr ErrorClass PrivateMethodMismatch{3503, StrictLevel::False}; constexpr ErrorClass BadAttrType{3504, StrictLevel::True}; constexpr ErrorClass BadModuleFunction{3505, StrictLevel::True}; diff --git a/core/lsp/QueryResponse.h b/core/lsp/QueryResponse.h index 668a5e7032..fbaa602f3a 100644 --- a/core/lsp/QueryResponse.h +++ b/core/lsp/QueryResponse.h @@ -67,18 +67,10 @@ CheckSize(LiteralResponse, 48, 8); class ConstantResponse final { public: using Scopes = InlinedVector; - ConstantResponse(core::SymbolRef symbol, core::SymbolRef symbolBeforeDealias, core::Loc termLoc, Scopes scopes, - core::NameRef name, core::TypeAndOrigins retType, core::MethodRef enclosingMethod) - : symbol(symbol), symbolBeforeDealias(symbolBeforeDealias), termLoc(termLoc), scopes(scopes), name(name), + ConstantResponse(core::SymbolRef symbolBeforeDealias, core::Loc termLoc, Scopes scopes, core::NameRef name, + core::TypeAndOrigins retType, core::MethodRef enclosingMethod) + : symbolBeforeDealias(symbolBeforeDealias), termLoc(termLoc), scopes(scopes), name(name), enclosingMethod(enclosingMethod), retType(std::move(retType)) {} - const core::SymbolRef symbol; - // You probably don't want this. Almost all of Sorbet's type system operates on dealiased - // symbols transparently (e.g., for a constant like `X = Integer`, Sorbet reports that `''` is - // not an `Integer`, not that `''` is not an `X`). - // - // But for some interactive features, it's important to respond using names the user typed. - // If you're thinking about using this, probably just use `symbol` instead, and if you still - // think you need this, double check with a Sorbet contributor. const core::SymbolRef symbolBeforeDealias; const core::Loc termLoc; const Scopes scopes; @@ -86,7 +78,7 @@ class ConstantResponse final { const core::MethodRef enclosingMethod; const core::TypeAndOrigins retType; }; -CheckSize(ConstantResponse, 88, 8); +CheckSize(ConstantResponse, 80, 8); class FieldResponse final { public: diff --git a/core/lsp/TypecheckEpochManager.h b/core/lsp/TypecheckEpochManager.h index 326fa88cae..773441dce6 100644 --- a/core/lsp/TypecheckEpochManager.h +++ b/core/lsp/TypecheckEpochManager.h @@ -19,21 +19,21 @@ class TypecheckEpochManager final { mutable absl::Mutex epochMutex; // Contains the current edit version (epoch) that the processing thread is typechecking or has // typechecked last. Is bumped by the typechecking thread. - std::atomic currentlyProcessingLSPEpoch GUARDED_BY(epochMutex); + std::atomic currentlyProcessingLSPEpoch ABSL_GUARDED_BY(epochMutex); // Should always be `>= currentlyProcessingLSPEpoch` (modulo overflows). // If value in `lspEpochInvalidator` is different from `currentlyProcessingLSPEpoch`, then LSP wants the current // request to be cancelled. Is bumped by the preprocessor thread (which determines cancellations). - std::atomic lspEpochInvalidator GUARDED_BY(epochMutex); + std::atomic lspEpochInvalidator ABSL_GUARDED_BY(epochMutex); // Should always be >= currentlyProcessingLSPEpoch. Is bumped by the typechecking thread. // Contains the epoch of the last committed slow path. // If lastCommittedLSPEpoch != currentlyProcessingLSPEpoch, then GlobalState is currently running a slow path. - std::atomic lastCommittedLSPEpoch GUARDED_BY(epochMutex); + std::atomic lastCommittedLSPEpoch ABSL_GUARDED_BY(epochMutex); // Thread ID of the typechecking thread. Lazily set. mutable std::optional typecheckingThreadId; // Thread ID of the preprocess thread. Lazily set. mutable std::optional messageProcessingThreadId; - TypecheckingStatus getStatusInternal() const EXCLUSIVE_LOCKS_REQUIRED(epochMutex); + TypecheckingStatus getStatusInternal() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(epochMutex); public: static void assertConsistentThread(std::optional &expectedThreadId, std::string_view method, @@ -50,7 +50,7 @@ class TypecheckEpochManager final { // Run only from processing thread. bool tryCancelSlowPath(uint32_t newEpoch); // Run only from the typechecking thread. - // Tries to commit the given epoch. Returns true if the commit succeeeded, or false if it was canceled. + // Tries to commit the given epoch. Returns true if the commit succeeded, or false if it was canceled. // The presence of PreemptionTaskManager determines if this commit is preemptible. bool tryCommitEpoch(core::GlobalState &gs, uint32_t epoch, bool isCancelable, std::optional> preemptionManager, diff --git a/core/packages/MangledName.cc b/core/packages/MangledName.cc index 6bebd4a75a..0ad0013e8a 100644 --- a/core/packages/MangledName.cc +++ b/core/packages/MangledName.cc @@ -1,26 +1,47 @@ #include "core/packages/MangledName.h" #include "absl/strings/str_join.h" +#include "absl/strings/str_replace.h" #include "core/GlobalState.h" #include "core/Names.h" using namespace std; namespace sorbet::core::packages { -core::NameRef MangledName::mangledNameFromParts(core::GlobalState &gs, std::vector &parts) { +MangledName MangledName::mangledNameFromParts(core::GlobalState &gs, const std::vector &parts) { // Foo::Bar => Foo_Bar_Package auto mangledName = absl::StrCat(absl::StrJoin(parts, "_"), core::PACKAGE_SUFFIX); auto utf8Name = gs.enterNameUTF8(mangledName); auto packagerName = gs.freshNameUnique(core::UniqueNameKind::Packager, utf8Name, 1); - return gs.enterNameConstant(packagerName); + return MangledName(gs.enterNameConstant(packagerName)); } -core::NameRef MangledName::mangledNameFromParts(core::GlobalState &gs, std::vector &parts) { +MangledName MangledName::mangledNameFromParts(core::GlobalState &gs, const std::vector &parts) { // Foo::Bar => Foo_Bar_Package auto mangledName = absl::StrCat(absl::StrJoin(parts, "_", NameFormatter(gs)), core::PACKAGE_SUFFIX); auto utf8Name = gs.enterNameUTF8(mangledName); auto packagerName = gs.freshNameUnique(core::UniqueNameKind::Packager, utf8Name, 1); - return gs.enterNameConstant(packagerName); + return MangledName(gs.enterNameConstant(packagerName)); +} + +MangledName MangledName::mangledNameFromHuman(const core::GlobalState &gs, string_view nameStr) { + auto mangled = absl::StrCat(absl::StrReplaceAll(nameStr, {{"::", "_"}}), core::PACKAGE_SUFFIX); + auto utf8Name = gs.lookupNameUTF8(mangled); + if (!utf8Name.exists()) { + return MangledName(); + } + + auto packagerName = gs.lookupNameUnique(core::UniqueNameKind::Packager, utf8Name, 1); + if (!packagerName.exists()) { + return MangledName(); + } + + auto cnst = gs.lookupNameConstant(packagerName); + if (!cnst.exists()) { + return MangledName(); + } + + return MangledName(cnst); } } // namespace sorbet::core::packages diff --git a/core/packages/MangledName.h b/core/packages/MangledName.h index 92d6b6a473..42938fb337 100644 --- a/core/packages/MangledName.h +++ b/core/packages/MangledName.h @@ -1,16 +1,41 @@ #ifndef SORBET_CORE_PACKAGES_MANGLEDNAME_H #define SORBET_CORE_PACKAGES_MANGLEDNAME_H -#include "core/GlobalState.h" #include "core/LocOffsets.h" #include "core/NameRef.h" #include +namespace sorbet::core { +class GlobalState; +} // namespace sorbet::core + namespace sorbet::core::packages { class MangledName final { + explicit MangledName(core::NameRef mangledName) : mangledName(mangledName) {} + public: - static core::NameRef mangledNameFromParts(core::GlobalState &gs, std::vector &parts); - static core::NameRef mangledNameFromParts(core::GlobalState &gs, std::vector &parts); + core::NameRef mangledName; + + MangledName() = default; + + // ["Foo", "Bar"] => :Foo_Bar_Package + static MangledName mangledNameFromParts(core::GlobalState &gs, const std::vector &parts); + // [:Foo, :Bar] => :Foo_Bar_Package + static MangledName mangledNameFromParts(core::GlobalState &gs, const std::vector &parts); + // "Foo::Bar" -> :Foo_Bar_Package + static MangledName mangledNameFromHuman(const core::GlobalState &gs, std::string_view human); + + bool operator==(const MangledName &rhs) const { + return mangledName == rhs.mangledName; + } + + bool operator!=(const MangledName &rhs) const { + return !(rhs == *this); + } + + bool exists() const { + return this->mangledName.exists(); + } }; class NameFormatter final { @@ -26,5 +51,9 @@ class NameFormatter final { out->append(p.first.shortName(gs)); } }; + +template H AbslHashValue(H h, const MangledName &m) { + return H::combine(std::move(h), m.mangledName); +} } // namespace sorbet::core::packages #endif diff --git a/core/packages/PackageDB.cc b/core/packages/PackageDB.cc index dea5b61f00..fdeb4aa335 100644 --- a/core/packages/PackageDB.cc +++ b/core/packages/PackageDB.cc @@ -1,6 +1,5 @@ #include "core/packages/PackageDB.h" #include "absl/strings/match.h" -#include "absl/strings/str_replace.h" #include "common/sort/sort.h" #include "core/AutocorrectSuggestion.h" #include "core/GlobalState.h" @@ -13,8 +12,8 @@ namespace sorbet::core::packages { namespace { class NonePackage final : public PackageInfo { public: - core::NameRef mangledName() const { - return NameRef::noName(); + MangledName mangledName() const { + return MangledName(); } const vector &fullName() const { @@ -58,11 +57,6 @@ class NonePackage final : public PackageInfo { return false; } - bool legacyAutoloaderCompatibility() const { - notImplemented(); - return true; - } - bool exportAll() const { notImplemented(); return false; @@ -82,11 +76,11 @@ class NonePackage final : public PackageInfo { std::vector> testImports() const { return vector>(); } - std::vector> visibleTo() const { - return vector>(); + std::vector visibleTo() const { + return vector(); } - std::optional importsPackage(core::NameRef mangledName) const { + std::optional importsPackage(MangledName mangledName) const { notImplemented(); return nullopt; } @@ -120,7 +114,7 @@ UnfreezePackages::~UnfreezePackages() { db.frozen = true; } -NameRef PackageDB::enterPackage(unique_ptr pkg) { +MangledName PackageDB::enterPackage(unique_ptr pkg) { ENFORCE(!frozen); ENFORCE(writerThread == this_thread::get_id(), "PackageDB writes are not thread safe"); auto nr = pkg->mangledName(); @@ -143,17 +137,17 @@ NameRef PackageDB::enterPackage(unique_ptr pkg) { return nr; } -const NameRef PackageDB::getPackageNameForFile(FileRef file) const { +const MangledName PackageDB::getPackageNameForFile(FileRef file) const { if (this->packageForFile_.size() <= file.id()) { - return NameRef::noName(); + return MangledName(); } return this->packageForFile_[file.id()]; } -void PackageDB::setPackageNameForFile(FileRef file, NameRef mangledName) { +void PackageDB::setPackageNameForFile(FileRef file, MangledName mangledName) { if (this->packageForFile_.size() <= file.id()) { - this->packageForFile_.resize(file.id() + 1, NameRef::noName()); + this->packageForFile_.resize(file.id() + 1, MangledName()); } this->packageForFile_[file.id()] = mangledName; @@ -196,25 +190,14 @@ const PackageInfo &PackageDB::getPackageForFile(const core::GlobalState &gs, cor } const PackageInfo &PackageDB::getPackageInfo(const core::GlobalState &gs, std::string_view nameStr) const { - auto mangled = absl::StrCat(absl::StrReplaceAll(nameStr, {{"::", "_"}}), core::PACKAGE_SUFFIX); - auto utf8Name = gs.lookupNameUTF8(mangled); - if (!utf8Name.exists()) { - return NONE_PKG; - } - - auto packagerName = gs.lookupNameUnique(core::UniqueNameKind::Packager, utf8Name, 1); - if (!packagerName.exists()) { - return NONE_PKG; - } - - auto cnst = gs.lookupNameConstant(packagerName); + auto cnst = core::packages::MangledName::mangledNameFromHuman(gs, nameStr); if (!cnst.exists()) { return NONE_PKG; } return getPackageInfo(cnst); } -const PackageInfo &PackageDB::getPackageInfo(core::NameRef mangledName) const { +const PackageInfo &PackageDB::getPackageInfo(MangledName mangledName) const { auto it = packages_.find(mangledName); if (it == packages_.end()) { return NONE_PKG; @@ -222,18 +205,10 @@ const PackageInfo &PackageDB::getPackageInfo(core::NameRef mangledName) const { return *it->second; } -bool PackageDB::empty() const { - return packages_.empty(); -} - -const vector &PackageDB::packages() const { +const vector &PackageDB::packages() const { return mangledNames; } -const std::vector &PackageDB::secondaryTestPackageNamespaceRefs() const { - return secondaryTestPackageNamespaceRefs_; -} - const std::vector &PackageDB::skipRBIExportEnforcementDirs() const { return skipRBIExportEnforcementDirs_; } @@ -250,8 +225,8 @@ const std::string_view PackageDB::errorHint() const { return errorHint_; } -bool PackageDB::skipImportVisibilityCheckFor(core::NameRef mangledName) const { - return absl::c_find(skipImportVisibilityCheckFor_, mangledName) != skipImportVisibilityCheckFor_.end(); +bool PackageDB::allowRelaxedPackagerChecksFor(MangledName mangledName) const { + return absl::c_find(allowRelaxedPackagerChecksFor_, mangledName) != allowRelaxedPackagerChecksFor_.end(); } PackageDB PackageDB::deepCopy() const { @@ -261,7 +236,7 @@ PackageDB PackageDB::deepCopy() const { for (auto const &[nr, pkgInfo] : this->packages_) { result.packages_[nr] = pkgInfo->deepCopy(); } - result.secondaryTestPackageNamespaceRefs_ = this->secondaryTestPackageNamespaceRefs_; + result.enabled_ = this->enabled_; result.extraPackageFilesDirectoryUnderscorePrefixes_ = this->extraPackageFilesDirectoryUnderscorePrefixes_; result.extraPackageFilesDirectorySlashPrefixes_ = this->extraPackageFilesDirectorySlashPrefixes_; result.packagesByPathPrefix = this->packagesByPathPrefix; diff --git a/core/packages/PackageDB.h b/core/packages/PackageDB.h index 404c99e292..b0ee64a45a 100644 --- a/core/packages/PackageDB.h +++ b/core/packages/PackageDB.h @@ -22,26 +22,27 @@ class PackageDB final { friend class core::GlobalState; public: - NameRef enterPackage(std::unique_ptr pkg); + static constexpr NameRef TEST_NAMESPACE = core::Names::Constants::Test(); - // Fetch the mangled package name for a file, returning a core::NameRef::noName() that doesn't exist if there is no - // associated packge for the file. - const NameRef getPackageNameForFile(FileRef file) const; + MangledName enterPackage(std::unique_ptr pkg); + + // Fetch the mangled package name for a file, returning a MangledName that doesn't exist if there is no + // associated package for the file. + const MangledName getPackageNameForFile(FileRef file) const; // Set the associated package for the file. - void setPackageNameForFile(FileRef file, NameRef mangledName); + void setPackageNameForFile(FileRef file, MangledName mangledName); const PackageInfo &getPackageForFile(const core::GlobalState &gs, core::FileRef file) const; - const PackageInfo &getPackageInfo(core::NameRef mangledName) const; + const PackageInfo &getPackageInfo(MangledName mangledName) const; // Lookup `PackageInfo` from the string representation of the un-mangled package name. const PackageInfo &getPackageInfo(const core::GlobalState &gs, std::string_view str) const; - bool empty() const; // Get mangled names for all packages. // Packages are ordered lexicographically with respect to the NameRef's that make up their // namespaces. - const std::vector &packages() const; + const std::vector &packages() const; PackageDB deepCopy() const; @@ -53,30 +54,34 @@ class PackageDB final { PackageDB &operator=(const PackageDB &) = delete; PackageDB &operator=(PackageDB &&) = default; - const std::vector &secondaryTestPackageNamespaceRefs() const; + // Whether the --stripe-packages mode is active. + bool enabled() const { + return this->enabled_; + } + const std::vector &extraPackageFilesDirectoryUnderscorePrefixes() const; const std::vector &extraPackageFilesDirectorySlashPrefixes() const; const std::vector &skipRBIExportEnforcementDirs() const; const std::string_view errorHint() const; - bool skipImportVisibilityCheckFor(const core::NameRef mangledName) const; + bool allowRelaxedPackagerChecksFor(const MangledName mangledName) const; private: - std::vector secondaryTestPackageNamespaceRefs_; + bool enabled_ = false; std::vector extraPackageFilesDirectoryUnderscorePrefixes_; std::vector extraPackageFilesDirectorySlashPrefixes_; std::string errorHint_; std::vector skipRBIExportEnforcementDirs_; - std::vector skipImportVisibilityCheckFor_; + std::vector allowRelaxedPackagerChecksFor_; // This vector is kept in sync with the size of the file table in the global state by // `Packager::setPackageNameOnFiles`. A `FileRef` being out of bounds in this vector is treated as the file having // no associated package. - std::vector packageForFile_; + std::vector packageForFile_; - UnorderedMap> packages_; - UnorderedMap packagesByPathPrefix; - std::vector mangledNames; + UnorderedMap> packages_; + UnorderedMap packagesByPathPrefix; + std::vector mangledNames; bool frozen = true; std::thread::id writerThread; diff --git a/core/packages/PackageInfo.h b/core/packages/PackageInfo.h index 27a74574e1..b94fb1043f 100644 --- a/core/packages/PackageInfo.h +++ b/core/packages/PackageInfo.h @@ -3,6 +3,7 @@ #include "core/NameRef.h" #include "core/SymbolRef.h" +#include "core/packages/MangledName.h" #include #include @@ -21,15 +22,28 @@ enum class ImportType { Test, }; +enum class VisibleToType { + Normal, + Wildcard, +}; + +struct VisibleTo { + std::vector packageName; + VisibleToType visibleToType; + + VisibleTo(std::vector packageName, VisibleToType visibleToType) + : packageName(packageName), visibleToType(visibleToType){}; +}; + class PackageInfo { public: - virtual core::NameRef mangledName() const = 0; + virtual MangledName mangledName() const = 0; virtual const std::vector &fullName() const = 0; virtual const std::vector &pathPrefixes() const = 0; virtual std::vector> exports() const = 0; virtual std::vector> imports() const = 0; virtual std::vector> testImports() const = 0; - virtual std::vector> visibleTo() const = 0; + virtual std::vector visibleTo() const = 0; virtual std::unique_ptr deepCopy() const = 0; virtual core::Loc fullLoc() const = 0; virtual core::Loc declLoc() const = 0; @@ -41,7 +55,7 @@ class PackageInfo { core::ClassOrModuleRef getPackageScope(const core::GlobalState &gs) const; core::ClassOrModuleRef getPackageTestScope(const core::GlobalState &gs) const; - virtual std::optional importsPackage(core::NameRef mangledName) const = 0; + virtual std::optional importsPackage(MangledName mangledName) const = 0; // autocorrects virtual std::optional addImport(const core::GlobalState &gs, const PackageInfo &pkg, @@ -64,7 +78,6 @@ class PackageInfo { }; virtual bool ownsSymbol(const core::GlobalState &gs, core::SymbolRef symbol) const = 0; - virtual bool legacyAutoloaderCompatibility() const = 0; virtual bool exportAll() const = 0; virtual bool visibleToTests() const = 0; @@ -79,15 +92,15 @@ class PackageInfo { class ImportInfo final { public: // The mangled name of the package whose imports are described. - core::NameRef package; + MangledName package; // Imported packages whose name is a prefix of `package`. For example, if the package `Foo::Bar` imports `Foo` that // package's name would be in `parentImports` because its name is a prefix of `Foo::Bar`. - std::vector parentImports; + std::vector parentImports; // The mangled names of packages that are imported by this package, minus any imports that fall in the parent // namespace of this package. - std::vector regularImports; + std::vector regularImports; static ImportInfo fromPackage(const core::GlobalState &gs, const PackageInfo &info); }; diff --git a/core/proto/BUILD b/core/proto/BUILD index 197b9fc332..a587b1a5e7 100644 --- a/core/proto/BUILD +++ b/core/proto/BUILD @@ -23,5 +23,6 @@ cc_library( "//core", "//proto", "//proto/pay-server", + "@com_google_protobuf//src/google/protobuf/json", ], ) diff --git a/core/proto/proto.cc b/core/proto/proto.cc index ec007bb528..cf69e039ca 100644 --- a/core/proto/proto.cc +++ b/core/proto/proto.cc @@ -1,7 +1,7 @@ // have to be included first as they violate our poisons #include "core/proto/proto.h" -#include -#include +#include "src/google/protobuf/io/zero_copy_stream_impl.h" +#include "src/google/protobuf/util/type_resolver_util.h" #include "absl/strings/match.h" #include "absl/strings/str_cat.h" @@ -61,6 +61,9 @@ com::stripe::rubytyper::Name Proto::toProto(const GlobalState &gs, NameRef name) case UniqueNameKind::TEnum: protoName.set_unique(com::stripe::rubytyper::Name::OPUS_ENUM); break; + case UniqueNameKind::Struct: + protoName.set_unique(com::stripe::rubytyper::Name::STRUCT); + break; case UniqueNameKind::Packager: protoName.set_unique(com::stripe::rubytyper::Name::PACKAGER); break; @@ -391,7 +394,7 @@ com::stripe::rubytyper::FileTable Proto::filesToProto(const GlobalState &gs, const UnorderedMap &untypedUsages, bool showFull) { com::stripe::rubytyper::FileTable files; const auto &packageDB = gs.packageDB(); - auto stripePackages = !packageDB.empty(); + auto stripePackages = packageDB.enabled(); for (int i = 1; i < gs.filesUsed(); ++i) { core::FileRef file(i); if (file.data(gs).isPayload()) { @@ -429,13 +432,14 @@ com::stripe::rubytyper::FileTable Proto::filesToProto(const GlobalState &gs, string Proto::toJSON(const google::protobuf::Message &message) { string jsonString; - google::protobuf::util::JsonPrintOptions options; + google::protobuf::json::PrintOptions options; options.add_whitespace = true; - // Enabling this option caused proto to consume ~10G of RAM keeping track of which fields it has - // and has not emitted. - options.always_print_primitive_fields = false; options.preserve_proto_field_names = true; - google::protobuf::util::MessageToJsonString(message, &jsonString, options); + auto status = google::protobuf::json::MessageToJsonString(message, &jsonString, options); + if (!status.ok()) { + cerr << "error converting to proto json: " << status.message() << '\n'; + abort(); + } return jsonString; } @@ -448,11 +452,8 @@ void Proto::toJSON(const google::protobuf::Message &message, ostream &out) { google::protobuf::io::ArrayInputStream istream(binaryProto.data(), binaryProto.size()); google::protobuf::io::OstreamOutputStream ostream(&out); - google::protobuf::util::JsonPrintOptions options; + google::protobuf::json::PrintOptions options; options.add_whitespace = true; - // Enabling this option caused proto to consume ~10G of RAM keeping track of which fields it has - // and has not emitted. - options.always_print_primitive_fields = false; options.preserve_proto_field_names = true; const google::protobuf::DescriptorPool *pool = message.GetDescriptor()->file()->pool(); @@ -460,9 +461,9 @@ void Proto::toJSON(const google::protobuf::Message &message, ostream &out) { google::protobuf::util::NewTypeResolverForDescriptorPool(kTypeUrlPrefix, pool)); string url = absl::StrCat(kTypeUrlPrefix, "/", message.GetDescriptor()->full_name()); - auto status = google::protobuf::util::BinaryToJsonStream(resolver.get(), url, &istream, &ostream, options); + auto status = google::protobuf::json::BinaryToJsonStream(resolver.get(), url, &istream, &ostream, options); if (!status.ok()) { - cerr << "error converting to proto json: " << status.error_message() << '\n'; + cerr << "error converting to proto json: " << status.message() << '\n'; abort(); } } diff --git a/core/proto/proto.h b/core/proto/proto.h index b88f5bda5d..be781b5ea5 100644 --- a/core/proto/proto.h +++ b/core/proto/proto.h @@ -7,7 +7,7 @@ #include "proto/Symbol.pb.h" #include "proto/Type.pb.h" #include "proto/pay-server/SourceMetrics.pb.h" -#include +#include #include "core/core.h" #include diff --git a/core/serialize/serialize.cc b/core/serialize/serialize.cc index 6d5a093206..c5a440b46e 100644 --- a/core/serialize/serialize.cc +++ b/core/serialize/serialize.cc @@ -138,6 +138,13 @@ uint8_t UnPickler::getU1() { return res; } +// Note that this does not necessarily write 4 bytes: +// smaller numbers may take fewer bytes. +// +// NOTE(froydnj): SQLite has a different encoding for varints: +// https://sqlite.org/src4/doc/trunk/www/varint.wiki +// Given that varint decoding is a signification fraction of cache reading time, +// it may make sense to experiment using this in Sorbet. void Pickler::putU4(uint32_t u) { if (u == 0) { if (zeroCounter != 0) { @@ -1042,7 +1049,8 @@ void SerializerImpl::unpickleGS(UnPickler &p, GlobalState &result) { void SerializerImpl::pickle(Pickler &p, LocOffsets loc) { p.putU4(loc.beginLoc); - p.putU4(loc.endLoc); + // The length of the loc likely encodes as a smaller varint. + p.putU4(loc.endLoc - loc.beginLoc); } void SerializerImpl::pickle(Pickler &p, Loc loc) { @@ -1057,7 +1065,9 @@ Loc SerializerImpl::unpickleLoc(UnPickler &p) { } LocOffsets SerializerImpl::unpickleLocOffsets(UnPickler &p) { - return LocOffsets{p.getU4(), p.getU4()}; + uint32_t start = p.getU4(); + uint32_t length = p.getU4(); + return LocOffsets{start, start + length}; } vector Serializer::store(const GlobalState &gs) { diff --git a/core/sig_finder/sig_finder.cc b/core/sig_finder/sig_finder.cc index 0a22e3f107..bdb6802080 100644 --- a/core/sig_finder/sig_finder.cc +++ b/core/sig_finder/sig_finder.cc @@ -29,8 +29,8 @@ core::SymbolRef getEffectiveOwner(core::Context ctx) { } // namespace -void SigFinder::preTransformClassDef(core::Context ctx, ast::ExpressionPtr &tree) { - auto loc = core::Loc(ctx.file, tree.loc()); +void SigFinder::preTransformClassDef(core::Context ctx, const ast::ClassDef &tree) { + auto loc = ctx.locAt(tree.loc); if (!this->narrowestClassDefRange.exists()) { // No narrowestClassDefRange yet, so take the loc of the first ClassDef we see @@ -51,15 +51,14 @@ void SigFinder::preTransformClassDef(core::Context ctx, ast::ExpressionPtr &tree this->scopeContainsQueryLoc.emplace_back(loc.contains(this->queryLoc)); } -void SigFinder::postTransformClassDef(core::Context ctx, ast::ExpressionPtr &tree) { +void SigFinder::postTransformClassDef(core::Context ctx, const ast::ClassDef &tree) { ENFORCE(!this->scopeContainsQueryLoc.empty()); this->scopeContainsQueryLoc.pop_back(); } -void SigFinder::preTransformMethodDef(core::Context ctx, ast::ExpressionPtr &tree) { +void SigFinder::preTransformMethodDef(core::Context ctx, const ast::MethodDef &tree) { if (this->result_.has_value()) { - if (this->result_->origSend->loc.endPos() <= tree.loc().beginPos() && - tree.loc().endPos() <= queryLoc.beginPos()) { + if (this->result_->origSend->loc.endPos() <= tree.loc.beginPos() && tree.loc.endPos() <= queryLoc.beginPos()) { // There is a method definition between the current result sig and the queryLoc, // so the sig we found is not for the right method. this->result_ = nullopt; @@ -67,10 +66,9 @@ void SigFinder::preTransformMethodDef(core::Context ctx, ast::ExpressionPtr &tre } } -void SigFinder::postTransformRuntimeMethodDefinition(core::Context ctx, ast::ExpressionPtr &tree) { +void SigFinder::postTransformRuntimeMethodDefinition(core::Context ctx, const ast::RuntimeMethodDefinition &tree) { if (this->result_.has_value()) { - if (this->result_->origSend->loc.endPos() <= tree.loc().beginPos() && - tree.loc().endPos() <= queryLoc.beginPos()) { + if (this->result_->origSend->loc.endPos() <= tree.loc.beginPos() && tree.loc.endPos() <= queryLoc.beginPos()) { // There is a method definition between the current result sig and the queryLoc, // so the sig we found is not for the right method. this->result_ = nullopt; @@ -78,9 +76,7 @@ void SigFinder::postTransformRuntimeMethodDefinition(core::Context ctx, ast::Exp } } -void SigFinder::preTransformSend(core::Context ctx, ast::ExpressionPtr &tree) { - auto &send = ast::cast_tree_nonnull(tree); - +void SigFinder::preTransformSend(core::Context ctx, const ast::Send &send) { if (!resolver::TypeSyntax::isSig(ctx, send)) { return; } @@ -93,7 +89,7 @@ void SigFinder::preTransformSend(core::Context ctx, ast::ExpressionPtr &tree) { return; } - auto currentLoc = ctx.locAt(tree.loc()); + auto currentLoc = ctx.locAt(send.loc); if (!currentLoc.exists()) { // Defensive in case location information is disabled (e.g., certain fuzzer modes) return; @@ -131,10 +127,10 @@ void SigFinder::preTransformSend(core::Context ctx, ast::ExpressionPtr &tree) { } } -optional SigFinder::findSignature(core::Context ctx, ast::ExpressionPtr &tree, +optional SigFinder::findSignature(core::Context ctx, const ast::ExpressionPtr &tree, core::Loc queryLoc) { SigFinder sigFinder(queryLoc); - ast::TreeWalk::apply(ctx, sigFinder, tree); + ast::ConstTreeWalk::apply(ctx, sigFinder, tree); return move(sigFinder.result_); } diff --git a/core/sig_finder/sig_finder.h b/core/sig_finder/sig_finder.h index 240d03bf1d..d11e2e2965 100644 --- a/core/sig_finder/sig_finder.h +++ b/core/sig_finder/sig_finder.h @@ -24,13 +24,13 @@ class SigFinder { : queryLoc(queryLoc), narrowestClassDefRange(core::Loc::none()), scopeContainsQueryLoc(std::vector{}), result_(std::nullopt) {} - void preTransformClassDef(core::Context ctx, ast::ExpressionPtr &tree); - void postTransformClassDef(core::Context ctx, ast::ExpressionPtr &tree); - void preTransformMethodDef(core::Context ctx, ast::ExpressionPtr &tree); - void postTransformRuntimeMethodDefinition(core::Context ctx, ast::ExpressionPtr &tree); - void preTransformSend(core::Context ctx, ast::ExpressionPtr &tree); + void preTransformClassDef(core::Context ctx, const ast::ClassDef &tree); + void postTransformClassDef(core::Context ctx, const ast::ClassDef &tree); + void preTransformMethodDef(core::Context ctx, const ast::MethodDef &tree); + void postTransformRuntimeMethodDefinition(core::Context ctx, const ast::RuntimeMethodDefinition &tree); + void preTransformSend(core::Context ctx, const ast::Send &tree); - static std::optional findSignature(core::Context ctx, ast::ExpressionPtr &tree, + static std::optional findSignature(core::Context ctx, const ast::ExpressionPtr &tree, core::Loc queryLoc); }; diff --git a/core/source_generator/BUILD.bazel b/core/source_generator/BUILD.bazel new file mode 100644 index 0000000000..7116d2c3c6 --- /dev/null +++ b/core/source_generator/BUILD.bazel @@ -0,0 +1,21 @@ +cc_library( + name = "source_generator", + srcs = glob( + [ + "*.cc", + "*.h", + ], + ), + hdrs = [ + "source_generator.h", + ], + linkstatic = select({ + "//tools/config:linkshared": 0, + "//conditions:default": 1, + }), + visibility = ["//visibility:public"], + deps = [ + "//core", + "@com_google_absl//absl/strings", + ], +) diff --git a/core/source_generator/source_generator.cc b/core/source_generator/source_generator.cc new file mode 100644 index 0000000000..63230f642c --- /dev/null +++ b/core/source_generator/source_generator.cc @@ -0,0 +1,198 @@ +#include "absl/strings/str_cat.h" +#include "core/core.h" + +using namespace std; + +namespace sorbet::core::source_generator { + +core::TypePtr getResultType(const core::GlobalState &gs, const core::TypePtr &type, core::SymbolRef inWhat, + core::TypePtr receiver, const core::TypeConstraint *constr) { + auto resultType = type; + if (core::is_proxy_type(receiver)) { + receiver = receiver.underlying(gs); + } + if (auto *applied = core::cast_type(receiver)) { + /* instantiate generic classes */ + resultType = core::Types::resultTypeAsSeenFrom(gs, resultType, inWhat.enclosingClass(gs), applied->klass, + applied->targs); + } + if (!resultType) { + resultType = core::Types::untypedUntracked(); + } + if (receiver) { + resultType = core::Types::replaceSelfType(gs, resultType, receiver); // instantiate self types + } + if (constr) { + resultType = core::Types::instantiate(gs, resultType, *constr); // instantiate generic methods + } + return resultType; +} + +// iff a sig has more than this many parameters, then print it as a multi-line sig. +constexpr int MAX_PRETTY_SIG_ARGS = 4; +// iff a `def` would be this wide or wider, expand it to be a multi-line def. +constexpr int MAX_PRETTY_WIDTH = 80; + +string prettySigForMethod(const core::GlobalState &gs, core::MethodRef method, const core::TypePtr &receiver, + core::TypePtr retType, const core::TypeConstraint *constraint, const ShowOptions options) { + ENFORCE(method.exists()); + ENFORCE(method.data(gs)->dealiasMethod(gs) == method); + // handle this case anyways so that we don't crash in prod when this method is misused + if (!method.exists()) { + return ""; + } + + if (!retType) { + retType = getResultType(gs, method.data(gs)->resultType, method, receiver, constraint); + } + string methodReturnType = + (retType == core::Types::void_()) ? "void" : absl::StrCat("returns(", retType.show(gs, options), ")"); + vector typeAndArgNames; + + vector flags; + auto sym = method.data(gs); + string sigCall = "sig"; + if (sym->flags.isFinal) { + sigCall = "sig(:final)"; + } + if (sym->flags.isAbstract && options.concretizeIfAbstract) { + flags.emplace_back("override"); + } else if (sym->flags.isAbstract) { + flags.emplace_back("abstract"); + } + if (sym->flags.isOverridable) { + flags.emplace_back("overridable"); + } + if (sym->flags.isOverride) { + flags.emplace_back("override"); + } + for (auto &argSym : method.data(gs)->arguments) { + // Don't display synthetic arguments (like blk). + if (!argSym.isSyntheticBlockArgument()) { + typeAndArgNames.emplace_back( + absl::StrCat(argSym.argumentName(gs), ": ", + getResultType(gs, argSym.type, method, receiver, constraint).show(gs, options))); + } + } + + string flagString = ""; + if (!flags.empty()) { + flagString = fmt::format("{}.", fmt::join(flags, ".")); + } + string paramsString = ""; + if (!typeAndArgNames.empty()) { + paramsString = fmt::format("params({}).", fmt::join(typeAndArgNames, ", ")); + } + + auto oneline = fmt::format("{} {{ {}{}{} }}", sigCall, flagString, paramsString, methodReturnType); + if (oneline.size() <= MAX_PRETTY_WIDTH && typeAndArgNames.size() <= MAX_PRETTY_SIG_ARGS) { + return oneline; + } + + if (!flags.empty()) { + flagString = fmt::format("{}\n .", fmt::join(flags, "\n .")); + } + if (!typeAndArgNames.empty()) { + paramsString = fmt::format("params(\n {}\n )\n .", fmt::join(typeAndArgNames, ",\n ")); + } + return fmt::format("{} do\n {}{}{}\nend", sigCall, flagString, paramsString, methodReturnType); +} + +string prettyDefForMethod(const core::GlobalState &gs, core::MethodRef method, const ShowOptions options) { + ENFORCE(method.exists()); + // handle this case anyways so that we don't crash in prod when this method is misused + if (!method.exists()) { + return ""; + } + auto methodData = method.data(gs); + + string visibility = ""; + if (methodData->flags.isPrivate) { + visibility = "private "; + } else if (methodData->flags.isProtected) { + visibility = "protected "; + } + + auto methodNameRef = methodData->name; + ENFORCE(methodNameRef.exists()); + string methodName = "???"; + if (methodNameRef.exists()) { + methodName = methodNameRef.toString(gs); + } + string methodNamePrefix = ""; + if (options.forceSelfPrefix || + (methodData->owner.exists() && methodData->owner.data(gs)->attachedClass(gs).exists())) { + methodNamePrefix = "self."; + } + vector prettyArgs; + + string defaultArgumentPlaceholder = "…"; + if (options.useValidSyntax && gs.suggestUnsafe.has_value()) { + defaultArgumentPlaceholder = "T.unsafe(nil)"; + } else if (options.useValidSyntax) { + // Setting this variable to the empty string will cause us to omit the default value for certain arguments. + // When this method is called to generate an autocorrect, this will produce a further error that the user must + // resolve manually. This is the best alternative we have given that we've been specifically asked not to + // generate syntactically invalid code. + defaultArgumentPlaceholder = ""; + } + + const auto &arguments = methodData->dealiasMethod(gs).data(gs)->arguments; + ENFORCE(!arguments.empty(), "Should have at least a block arg"); + for (const auto &argSym : arguments) { + // Don't display synthetic arguments (like blk). + if (argSym.isSyntheticBlockArgument()) { + continue; + } + string prefix = ""; + string suffix = ""; + if (argSym.flags.isRepeated) { + if (argSym.flags.isKeyword) { + prefix = "**"; // variadic keyword args + } else { + prefix = "*"; // rest args + } + } else if (argSym.flags.isKeyword) { + if (argSym.flags.isDefault && !defaultArgumentPlaceholder.empty()) { + suffix = fmt::format(": {}", defaultArgumentPlaceholder); // optional keyword (has a default value) + } else { + suffix = ":"; // required keyword + } + } else if (argSym.flags.isBlock) { + prefix = "&"; + } else if (argSym.flags.isDefault && !defaultArgumentPlaceholder.empty()) { + suffix = fmt::format("={}", defaultArgumentPlaceholder); + } + prettyArgs.emplace_back(fmt::format("{}{}{}", prefix, argSym.argumentName(gs), suffix)); + } + + string argListPrefix = ""; + string argListSeparator = ""; + string argListSuffix = ""; + if (prettyArgs.size() > 0) { + argListPrefix = "("; + argListSeparator = ", "; + argListSuffix = ")"; + } + + auto result = fmt::format("{}def {}{}{}{}{}; end", visibility, methodNamePrefix, methodName, argListPrefix, + fmt::join(prettyArgs, argListSeparator), argListSuffix); + if (prettyArgs.size() > 0 && result.length() >= MAX_PRETTY_WIDTH) { + argListPrefix = "(\n "; + argListSeparator = ",\n "; + argListSuffix = "\n)"; + result = fmt::format("{}def {}{}{}{}{}\nend", visibility, methodNamePrefix, methodName, argListPrefix, + fmt::join(prettyArgs, argListSeparator), argListSuffix); + } + return result; +} + +string prettyTypeForMethod(const core::GlobalState &gs, core::MethodRef method, const core::TypePtr &receiver, + const core::TypePtr &retType, const core::TypeConstraint *constraint, + const ShowOptions options) { + return fmt::format( + "{}\n{}", prettySigForMethod(gs, method.data(gs)->dealiasMethod(gs), receiver, retType, constraint, options), + prettyDefForMethod(gs, method, options)); +} + +} // namespace sorbet::core::source_generator diff --git a/core/source_generator/source_generator.h b/core/source_generator/source_generator.h new file mode 100644 index 0000000000..c6e01dc6dc --- /dev/null +++ b/core/source_generator/source_generator.h @@ -0,0 +1,17 @@ +#ifndef SORBET_CORE_SOURCE_GENERATOR_H +#define SORBET_CORE_SOURCE_GENERATOR_H + +#include "core/ShowOptions.h" +#include "core/core.h" + +namespace sorbet::core::source_generator { + +core::TypePtr getResultType(const core::GlobalState &gs, const core::TypePtr &type, core::SymbolRef inWhat, + core::TypePtr receiver, const core::TypeConstraint *constr); + +std::string prettyTypeForMethod(const core::GlobalState &gs, core::MethodRef method, const core::TypePtr &receiver, + const core::TypePtr &retType, const core::TypeConstraint *constraint, + const ShowOptions options); + +} // namespace sorbet::core::source_generator +#endif // SORBET_CORE_SOURCE_GENERATOR_H diff --git a/core/tools/generate_names.cc b/core/tools/generate_names.cc index 0ac3ea0c2f..f43a381cc1 100644 --- a/core/tools/generate_names.cc +++ b/core/tools/generate_names.cc @@ -51,11 +51,11 @@ NameDef names[] = { {"tripleEq", "==="}, {"orOp", "|"}, {"backtick", "`"}, - {"slice"}, {"defined_p", "defined?"}, {"undef"}, {"each"}, {"subclasses"}, + {"transpose"}, // Used in parser for error recovery {"methodNameMissing", ""}, @@ -131,6 +131,7 @@ NameDef names[] = { {"enum_", "enum"}, {"deprecatedEnum", "deprecated_enum"}, {"enums"}, + {"serialize"}, {"nilable"}, {"proc"}, {"untyped"}, @@ -206,6 +207,7 @@ NameDef names[] = { {"updatedProp", "updated_prop"}, {"merchantProp", "merchant_prop"}, {"merchantTokenProp", "merchant_token_prop"}, + {"name"}, {"encryptedProp", "encrypted_prop"}, {"array"}, {"defDelegator", "def_delegator"}, @@ -259,6 +261,7 @@ NameDef names[] = { {"describe"}, {"it"}, {"before"}, + {"beforeAngles", ""}, {"after"}, {"afterAngles", ""}, {"testEach", "test_each"}, @@ -272,8 +275,6 @@ NameDef names[] = { {"skipGetter", "skip_getter"}, {"skipSetter", "skip_setter"}, - {"wrapInstance", "wrap_instance"}, - {"registered"}, {"instanceRegistered", ""}, {"helpers"}, @@ -355,11 +356,13 @@ NameDef names[] = { {"destructureArg", ""}, {"lambda"}, + {"lambdaTLet", ""}, {"nil_p", "nil?"}, {"blank_p", "blank?"}, {"present_p", "present?"}, {"nil"}, {"super", ""}, + {"untypedSuper", ""}, {"empty", ""}, {"buildHash", ""}, @@ -448,7 +451,6 @@ NameDef names[] = { {"testImport", "test_import"}, {"export_", "export"}, {"restrictToService", "restrict_to_service"}, - {"autoloaderCompatibility", "autoloader_compatibility"}, {"legacy"}, {"strict"}, {"visibleTo", "visible_to"}, @@ -457,10 +459,6 @@ NameDef names[] = { {"PackageSpec", "PackageSpec", true}, {"PackageSpecRegistry", "", true}, - // Compiler - {"runningCompiled_p", "running_compiled?"}, - {"compilerVersion", "compiler_version"}, - // GlobalState initEmpty() {"Top", "T.anything", true}, {"Bottom", "T.noreturn", true}, @@ -516,7 +514,7 @@ NameDef names[] = { {"BindToAttachedClass", "", true}, // A magic non user-creatable class for binding procs to self_type {"BindToSelfType", "", true}, - // A magic non user-creatable class for mimicing the decl builder during cfg + // A magic non user-creatable class for mimicking the decl builder during cfg // construction {"DeclBuilderForProcs", "", true}, {"Enumerable", "Enumerable", true}, @@ -530,10 +528,10 @@ NameDef names[] = { {"Encoding", "Encoding", true}, {"getEncoding", ""}, {"Static", "Static", true}, - {"StubModule", "StubModule", true}, - {"StubSuperClass", "StubSuperClass", true}, - {"StubMixin", "StubMixin", true}, - {"PlaceholderMixin", "PlaceholderMixin", true}, + {"StubModule", "", true}, + {"StubSuperClass", "", true}, + {"StubMixin", "", true}, + {"PlaceholderMixin", "", true}, {"Base", "Base", true}, {"Void", "Void", true}, {"TypeAlias", "", true}, @@ -564,7 +562,6 @@ NameDef names[] = { {"VERSION", "VERSION", true}, {"Thread", "Thread", true}, {"Configuration", "Configuration", true}, - {"Compiler", "Compiler", true}, {"Test", "Test", true}, {"Autogen", "Autogen", true}, {"Tokens", "Tokens", true}, @@ -577,9 +574,6 @@ NameDef names[] = { {"Int", "Int", true}, {"Timestamp", "Timestamp", true}, {"Bool", "Bool", true}, - - // used by the compiler - {"returnValue", ""}, }; void emit_name_header(ostream &out, NameDef &name) { diff --git a/core/types/calls.cc b/core/types/calls.cc index 7a9975d2db..90dcd1232a 100644 --- a/core/types/calls.cc +++ b/core/types/calls.cc @@ -134,7 +134,7 @@ DispatchResult ShapeType::dispatchCall(const GlobalState &gs, const DispatchArgs if (method.exists()) { auto *intrinsic = method.data(gs)->getIntrinsic(); if (intrinsic != nullptr) { - DispatchComponent comp{args.selfType, method, {}, nullptr, nullptr, nullptr, ArgInfo{}, nullptr}; + DispatchComponent comp{args.selfType, method, {}, nullptr, nullptr, nullptr, {}, {}, nullptr}; DispatchResult res{nullptr, std::move(comp)}; intrinsic->apply(gs, args, res); if (res.returnType != nullptr) { @@ -151,7 +151,7 @@ DispatchResult TupleType::dispatchCall(const GlobalState &gs, const DispatchArgs if (method.exists()) { auto *intrinsic = method.data(gs)->getIntrinsic(); if (intrinsic != nullptr) { - DispatchComponent comp{args.selfType, method, {}, nullptr, nullptr, nullptr, ArgInfo{}, nullptr}; + DispatchComponent comp{args.selfType, method, {}, nullptr, nullptr, nullptr, {}, {}, nullptr}; DispatchResult res{nullptr, std::move(comp)}; intrinsic->apply(gs, args, res); if (res.returnType != nullptr) { @@ -200,7 +200,7 @@ void addUnconstrainedIsaGenericNote(const GlobalState &gs, ErrorBuilder &e, Symb {{definition.loc(gs), ""}})); } else { - auto showOptions = ShowOptions().withShowForRBI(); + auto showOptions = ShowOptions().withUseValidSyntax(); auto wrapped = fmt::format("T.all({}, Constraint)", definition.show(gs, showOptions)); e.addErrorNote("Consider using `{}` to place a constraint on this type", wrapped); } @@ -214,9 +214,7 @@ DispatchResult SelfTypeParam::dispatchCall(const GlobalState &gs, const Dispatch // Short circuit here to avoid constructing an expensive error message. return emptyResult; } - auto funLoc = args.funLoc(); - auto errLoc = (funLoc.exists() && !funLoc.empty()) ? funLoc : args.callLoc(); - auto e = gs.beginError(errLoc, errors::Infer::CallOnTypeArgument); + auto e = gs.beginError(args.errLoc(), errors::Infer::CallOnTypeArgument); if (e) { auto thisStr = args.thisType.show(gs); if (args.fullType.type != args.thisType) { @@ -241,9 +239,7 @@ DispatchResult SelfTypeParam::dispatchCall(const GlobalState &gs, const Dispatch return emptyResult; } - auto funLoc = args.funLoc(); - auto errLoc = (funLoc.exists() && !funLoc.empty()) ? funLoc : args.callLoc(); - auto e = gs.beginError(errLoc, errors::Infer::CallOnUnboundedTypeMember); + auto e = gs.beginError(args.errLoc(), errors::Infer::CallOnUnboundedTypeMember); if (e) { auto member = typeMember.data(gs)->owner.asClassOrModuleRef().data(gs)->attachedClass(gs).exists() ? "template" @@ -280,7 +276,9 @@ unique_ptr matchArgType(const GlobalState &gs, TypeConstraint &constr, Lo expectedType = Types::replaceSelfType(gs, expectedType, selfType); - if (Types::isSubTypeUnderConstraint(gs, constr, argTpe.type, expectedType, UntypedMode::AlwaysCompatible)) { + core::ErrorSection::Collector errorDetailsCollector; + if (Types::isSubTypeUnderConstraint(gs, constr, argTpe.type, expectedType, UntypedMode::AlwaysCompatible, + errorDetailsCollector)) { if (expectedType.isUntyped()) { // TODO(jez) We should have code like this, but currently there are too many places that // are fine "accepting anything" that are typed as `T.untyped`. @@ -294,7 +292,7 @@ unique_ptr matchArgType(const GlobalState &gs, TypeConstraint &constr, Lo // TypeErrorDiagnostics::explainUntyped(gs, e, what, expectedType, argSym.loc, originForUninitialized); // return e.build(); // } - } else if (argTpe.type.isUntyped()) { + } else if (argTpe.type.isUntyped() && expectedType != Types::top()) { auto what = core::errors::Infer::errorClassForUntyped(gs, argLoc.file(), argTpe.type); if (auto e = gs.beginError(argLoc, what)) { e.setHeader("Argument passed to parameter `{}` is `{}`", argSym.argumentName(gs), "T.untyped"); @@ -319,7 +317,7 @@ unique_ptr matchArgType(const GlobalState &gs, TypeConstraint &constr, Lo e.addErrorSection(TypeAndOrigins::explainExpected(gs, expectedType, argSym.loc, for_)); } e.addErrorSection(argTpe.explainGot(gs, originForUninitialized)); - core::TypeErrorDiagnostics::explainTypeMismatch(gs, e, expectedType, argTpe.type); + core::TypeErrorDiagnostics::explainTypeMismatch(gs, e, errorDetailsCollector, expectedType, argTpe.type); TypeErrorDiagnostics::maybeAutocorrect(gs, e, argLoc, constr, expectedType, argTpe.type); return e.build(); } @@ -342,27 +340,35 @@ unique_ptr missingArg(const GlobalState &gs, Loc argsLoc, Loc receiverLoc return nullptr; } -int getArity(const GlobalState &gs, MethodRef method) { +size_t getArity(const GlobalState &gs, MethodRef method) { ENFORCE(!method.data(gs)->arguments.empty(), "Every method should have at least a block arg."); ENFORCE(method.data(gs)->arguments.back().flags.isBlock, "Last arg should be the block arg."); + const auto &arguments = method.data(gs)->arguments; + if (absl::c_any_of(arguments, [&](const auto &arg) { return arg.flags.isRepeated && !arg.flags.isKeyword; })) { + return SIZE_MAX; + }; + // Don't count the block arg in the arity return method.data(gs)->arguments.size() - 1; } +struct GuessOverloadCandidate { + MethodRef candidate; + shared_ptr constr; +}; + // Guess overload. The way we guess is only arity based - we will return the overload that has the smallest number of // arguments that is >= args.size() MethodRef guessOverload(const GlobalState &gs, ClassOrModuleRef inClass, MethodRef primary, uint16_t numPosArgs, InlinedVector &args, const vector &targs, bool hasBlock) { counterInc("calls.overloaded_invocations"); - ENFORCE(Context::permitOverloadDefinitions(gs, primary.data(gs)->loc().file(), primary), - "overload not permitted here"); MethodRef fallback = primary; vector allCandidates; allCandidates.emplace_back(primary); { // create candidates and sort them by number of arguments(stable by symbol id) - int i = 0; + size_t i = 0; MethodRef current = primary; while (current.data(gs)->flags.isOverloaded) { i++; @@ -376,33 +382,77 @@ MethodRef guessOverload(const GlobalState &gs, ClassOrModuleRef inClass, MethodR } } - fast_sort(allCandidates, [&](MethodRef s1, MethodRef s2) -> bool { - if (getArity(gs, s1) < getArity(gs, s2)) { + fast_sort(allCandidates, [&](const auto &s1, const auto &s2) -> bool { + auto s1Arity = getArity(gs, s1); + auto s2Arity = getArity(gs, s2); + if (s1Arity < s2Arity) { return true; } - if (getArity(gs, s1) == getArity(gs, s2)) { + if (s1Arity == s2Arity) { return s1.id() < s2.id(); } return false; }); } - vector leftCandidates = allCandidates; + vector allCandidatesWithConstraints; + + for (const auto &candidate : allCandidates) { + if (!candidate.data(gs)->flags.isGenericMethod) { + allCandidatesWithConstraints.emplace_back(GuessOverloadCandidate{candidate, nullptr}); + continue; + } + + // Make a new TypeConstraint with everything in the domain solving to `T.untyped`. + // + // This allows Sorbet to consider or reject an overload with a parameter like + // `T::Array[T.type_parameter(:U)]` based on whether the argument is `String`--in that case, + // it doesn't matter what the `T.type_parameter(:U)` is, because `String` is not an `Array`. + auto constr = make_unique(); + for (auto typeArgument : candidate.data(gs)->typeArguments()) { + constr->rememberIsSubtype(gs, typeArgument.data(gs)->resultType, Types::untypedUntracked()); + } + if (!constr->solve(gs)) { + Exception::raise("Constraint should always solve after creating TypeConstraint with only untyped bounds"); + } + + allCandidatesWithConstraints.emplace_back(GuessOverloadCandidate{candidate, move(constr)}); + } + + // Copy the vector + auto leftCandidates = allCandidatesWithConstraints; { auto checkArg = [&](auto i, const TypePtr &arg) { for (auto it = leftCandidates.begin(); it != leftCandidates.end(); /* nothing*/) { - MethodRef candidate = *it; - if (i >= getArity(gs, candidate)) { + const auto &[candidate, constr] = *it; + const auto &arguments = candidate.data(gs)->arguments; + TypePtr argTypeRaw; + auto arity = getArity(gs, candidate); + if (i < arguments.size() - 1) { + argTypeRaw = arguments[i].type; + } else if (arity == SIZE_MAX) { + auto restArg = absl::c_find_if( + arguments, [&](const auto &arg) { return arg.flags.isRepeated && !arg.flags.isKeyword; }); + ENFORCE(restArg != arguments.end()) + argTypeRaw = restArg->type; + } else { it = leftCandidates.erase(it); continue; } - auto argType = Types::resultTypeAsSeenFrom(gs, candidate.data(gs)->arguments[i].type, - candidate.data(gs)->owner, inClass, targs); - if (argType.isFullyDefined() && !Types::isSubType(gs, arg, argType)) { - it = leftCandidates.erase(it); - continue; + auto argType = Types::resultTypeAsSeenFrom(gs, argTypeRaw, candidate.data(gs)->owner, inClass, targs); + if (constr == nullptr) { + if (!Types::isSubType(gs, arg, argType)) { + it = leftCandidates.erase(it); + continue; + } + } else { + if (!Types::isSubTypeUnderConstraint(gs, *constr, arg, argType, UntypedMode::AlwaysCompatible, + ErrorSection::Collector::NO_OP)) { + it = leftCandidates.erase(it); + continue; + } } ++it; } @@ -419,20 +469,29 @@ MethodRef guessOverload(const GlobalState &gs, ClassOrModuleRef inClass, MethodR } } if (leftCandidates.empty()) { - leftCandidates = allCandidates; + leftCandidates = allCandidatesWithConstraints; } else { - fallback = leftCandidates[0]; + fallback = leftCandidates[0].candidate; } { // keep only candidates that have a block iff we are passing one for (auto it = leftCandidates.begin(); it != leftCandidates.end(); /* nothing*/) { - MethodRef candidate = *it; + const auto &[candidate, constr] = *it; const auto &args = candidate.data(gs)->arguments; ENFORCE(!args.empty(), "Should at least have a block argument."); - auto mentionsBlockArg = !args.back().isSyntheticBlockArgument(); - if (mentionsBlockArg != hasBlock) { - it = leftCandidates.erase(it); - continue; + const auto &lastArg = args.back(); + auto mentionsBlockArg = !lastArg.isSyntheticBlockArgument(); + if (hasBlock) { + if (!mentionsBlockArg || lastArg.type == Types::nilClass()) { + it = leftCandidates.erase(it); + continue; + } + } else { + if (mentionsBlockArg && lastArg.type != nullptr && + (!lastArg.type.isFullyDefined() || !Types::isSubType(gs, Types::nilClass(), lastArg.type))) { + it = leftCandidates.erase(it); + continue; + } } ++it; } @@ -442,12 +501,12 @@ MethodRef guessOverload(const GlobalState &gs, ClassOrModuleRef inClass, MethodR struct Comp { const GlobalState &gs; - bool operator()(MethodRef s, int i) const { - return getArity(gs, s) < i; + bool operator()(GuessOverloadCandidate &s, int i) const { + return getArity(gs, s.candidate) < i; } - bool operator()(int i, MethodRef s) const { - return i < getArity(gs, s); + bool operator()(int i, GuessOverloadCandidate &s) const { + return i < getArity(gs, s.candidate); } Comp(const GlobalState &gs) : gs(gs){}; @@ -460,7 +519,7 @@ MethodRef guessOverload(const GlobalState &gs, ClassOrModuleRef inClass, MethodR } if (!leftCandidates.empty()) { - return leftCandidates[0]; + return leftCandidates[0].candidate; } return fallback; } @@ -500,7 +559,7 @@ string prettyArity(const GlobalState &gs, MethodRef method) { } void maybeSuggestUnsafeKwsplat(const core::GlobalState &gs, core::ErrorBuilder &e, core::Loc kwSplatArgLoc) { - if (!kwSplatArgLoc.exists()) { + if (!kwSplatArgLoc.exists() || kwSplatArgLoc.empty()) { return; } @@ -549,6 +608,32 @@ const ShapeType *fromKwargsHash(const GlobalState &gs, const TypePtr &ty) { return hash; } +// Heuristic to try to find leading commas. Not actually required for correctness +// (will still parse even if we delete something but don't find the comma) which is +// why we're fine with this just hard-coding two common cases (easiest to implement). +Loc expandToLeadingComma(const GlobalState &gs, Loc loc) { + if (loc.adjustLen(gs, -1, 1).source(gs) == ",") { + return loc.adjust(gs, -1, 0); + } else if (loc.adjustLen(gs, -2, 2).source(gs) == ", ") { + return loc.adjust(gs, -2, 0); + } else if (loc.adjustLen(gs, -1, 1).source(gs) == " ") { + return loc.adjust(gs, -1, 0); + } else { + return loc; + } +} + +void handleBlockType(const GlobalState &gs, DispatchComponent &component, TypePtr blockType) { + if (!blockType) { + blockType = Types::untyped(component.method); + } + + component.blockReturnType = Types::getProcReturnType(gs, Types::dropNil(gs, blockType)); + blockType = component.constr->isSolved() ? Types::instantiate(gs, blockType, *component.constr) + : Types::approximate(gs, blockType, *component.constr); + component.blockPreType = blockType; +} + // This implements Ruby's argument matching logic (assigning values passed to a // method call to formal parameters of the method). // @@ -559,8 +644,7 @@ const ShapeType *fromKwargsHash(const GlobalState &gs, const TypePtr &ty) { // (with a subtype check on the key type, once we have generics) DispatchResult dispatchCallSymbol(const GlobalState &gs, const DispatchArgs &args, core::ClassOrModuleRef symbol, const vector &targs) { - auto funLoc = args.funLoc(); - auto errLoc = (funLoc.exists() && !funLoc.empty()) ? funLoc : args.callLoc(); + auto errLoc = args.errLoc(); if (symbol == core::Symbols::untyped()) { auto what = core::errors::Infer::errorClassForUntyped(gs, args.locs.file, args.thisType); if (auto e = gs.beginError(errLoc, what)) { @@ -588,20 +672,31 @@ DispatchResult dispatchCallSymbol(const GlobalState &gs, const DispatchArgs &arg } else if (args.name == Names::methodNameMissing()) { // We already reported a parse error earlier in the pipeline return DispatchResult(Types::untypedUntracked(), std::move(args.selfType), Symbols::noMethod()); + } else if (args.name == Names::untypedSuper()) { + // Currently, `super` is only checked for the following cases: + // - The receiver is a class + // - The super call is not inside a block + // No point in paying a full findMethodTransitive just to discover that. + return DispatchResult(Types::untyped(Symbols::Magic_UntypedSource_super()), std::move(args.selfType), + Symbols::noMethod()); } - // TODO(jez) It would be nice to make `core::Symbols::top()` not have `Object` as its ancestor, - // in which case we could simply let the findMethodTransitive run and fail to find any methods + auto targetName = args.name; MethodRef mayBeOverloaded; - if (symbol != core::Symbols::top()) { - mayBeOverloaded = symbol.data(gs)->findMethodTransitive(gs, args.name); + if (targetName == Names::super()) { + targetName = args.enclosingMethodForSuper; + mayBeOverloaded = symbol.data(gs)->findParentMethodTransitive(gs, targetName); + } else if (symbol != core::Symbols::top()) { + // TODO(jez) It would be nice to make `core::Symbols::top()` not have `Object` as its ancestor, + // in which case we could simply let the findMethodTransitive run and fail to find any methods + mayBeOverloaded = symbol.data(gs)->findMethodTransitive(gs, targetName); } if (!mayBeOverloaded.exists() && gs.requiresAncestorEnabled) { // Before raising any error, we look if the method exists in all required ancestors by this symbol auto ancestors = symbol.data(gs)->requiredAncestorsTransitive(gs); for (auto ancst : ancestors) { - mayBeOverloaded = ancst.symbol.data(gs)->findMethodTransitive(gs, args.name); + mayBeOverloaded = ancst.symbol.data(gs)->findMethodTransitive(gs, targetName); if (mayBeOverloaded.exists()) { break; } @@ -609,43 +704,34 @@ DispatchResult dispatchCallSymbol(const GlobalState &gs, const DispatchArgs &arg } if (!mayBeOverloaded.exists()) { - if (args.name == Names::initialize()) { - // Special-case initialize(). We should define this on - // `BasicObject`, but our method-resolution order is wrong, and - // putting it there will inadvertently shadow real definitions in - // some cases, so we special-case it here as a last resort. - auto result = DispatchResult(Types::untypedUntracked(), std::move(args.selfType), Symbols::noMethod()); - if (!args.args.empty() && !args.suppressErrors) { - if (auto e = gs.beginError(args.argsLoc(), errors::Infer::MethodArgumentCountMismatch)) { - e.setHeader("Wrong number of arguments for constructor. Expected: `{}`, got: `{}`", 0, - args.args.size()); - e.replaceWith("Delete args", args.argsLoc(), ""); - result.main.errors.emplace_back(e.build()); - } - } - return result; - } else if (args.name == core::Names::super()) { - return DispatchResult(Types::untyped(Symbols::Magic_UntypedSource_super()), std::move(args.selfType), - Symbols::noMethod()); - } auto result = DispatchResult(Types::untypedUntracked(), std::move(args.selfType), Symbols::noMethod()); if (args.suppressErrors) { // Short circuit here to avoid constructing an expensive error message. return result; } + + auto unknownMethodCode = args.name == Names::super() + // So we can attach super-specific autocorrects at some point in the future + ? errors::Infer::UnknownSuperMethod + : errors::Infer::UnknownMethod; + // This is a hack. We want to always be able to build the error object // so that it is not immediately sent to GlobalState::_error // and recorded. // Instead, the error always should get queued up in the // errors list of the result so that the caller can deal with the error. - auto e = gs.beginError(errLoc, errors::Infer::UnknownMethod); + auto e = gs.beginError(errLoc, unknownMethodCode); if (e) { string thisStr = args.thisType.show(gs); + auto ancestorsOf = args.name == Names::super() ? "ancestors of " : ""; + auto isSetter = targetName.isSetter(gs); + auto methodPrefix = isSetter ? "Setter method" : "Method"; if (args.fullType.type != args.thisType) { - e.setHeader("Method `{}` does not exist on `{}` component of `{}`", args.name.show(gs), thisStr, - args.fullType.type.show(gs)); + e.setHeader("{} `{}` does not exist on {}`{}` component of `{}`", methodPrefix, targetName.show(gs), + ancestorsOf, thisStr, args.fullType.type.show(gs)); } else { - e.setHeader("Method `{}` does not exist on `{}`", args.name.show(gs), thisStr); + e.setHeader("{} `{}` does not exist on {}`{}`", methodPrefix, targetName.show(gs), ancestorsOf, + thisStr); } e.addErrorSection(args.fullType.explainGot(gs, args.originForUninitialized)); @@ -662,6 +748,16 @@ DispatchResult dispatchCallSymbol(const GlobalState &gs, const DispatchArgs &arg auto attachedClass = symbol.data(gs)->attachedClass(gs); TypeErrorDiagnostics::maybeInsertDSLMethod(gs, e, args.locs.file, args.callLoc(), attachedClass, Symbols::T_Sig(), ""); + } else if (args.name == Names::super()) { + // TODO(jez) Special error for super. + // - If identical name exists as self/instance method in parent but we're currently + // an instance/self method, suggest changing enclosing method definition. + // - If similar name exists in parent suggest renaming enclosing method definition + // (intialize -> initialize, etc.) + // - Otherwise, suggest `T.bind` + // Note: super is not a method call, and so all the logic in the case below to + // compute autocorrects to potentially fix up the method call's receiver can't apply. + e.addErrorNote("For help fixing `{}` errors: {}", "super", "https://sorbet.org/docs/typed-super"); } else if (args.receiverLoc().exists() && (gs.suggestUnsafe.has_value() || (args.fullType.type != args.thisType && symbol == Symbols::NilClass()))) { @@ -687,70 +783,125 @@ DispatchResult dispatchCallSymbol(const GlobalState &gs, const DispatchArgs &arg args.receiverLoc().source(gs).value()); } } else { + auto canSkipFuzzyMatch = false; if (symbol.data(gs)->isModule()) { auto objMeth = core::Symbols::Object().data(gs)->findMethodTransitive(gs, args.name); if (objMeth.exists() && objMeth.data(gs)->owner.data(gs)->isModule()) { - e.addErrorNote("Did you mean to `{}` in this module?", - fmt::format("include {}", objMeth.data(gs)->owner.data(gs)->name.show(gs))); + auto objData = objMeth.data(gs); + auto ownerName = objData->owner.data(gs)->name.show(gs); + e.addErrorNote("`{}` is actually defined as a method on `{}`. To call it, either\n" + " `{}` in this module to ensure the method is always there, or\n" + " call the method using `{}` instead.", + args.name.show(gs), ownerName, fmt::format("include {}", ownerName), + fmt::format("{}.{}", ownerName, args.name.show(gs))); + if (args.receiverLoc().exists() && args.receiverLoc().empty()) { + e.replaceWith("Prefix with `Kernel.`", args.receiverLoc(), "Kernel."); + } } - } - auto alternatives = symbol.data(gs)->findMemberFuzzyMatch(gs, args.name); - for (auto alternative : alternatives) { - auto possibleSymbol = alternative.symbol; - if (!possibleSymbol.isClassOrModule() && - (!possibleSymbol.isMethod() || - (possibleSymbol.asMethodRef().data(gs)->flags.isPrivate && !args.isPrivateOk))) { - continue; + } else if (!symbol.data(gs)->attachedClass(gs).exists() && + symbol.data(gs)->lookupSingletonClass(gs).exists()) { + auto singleton = symbol.data(gs)->lookupSingletonClass(gs); + auto methodOnSingleton = singleton.data(gs)->findMethodTransitive(gs, args.name); + if (methodOnSingleton.exists()) { + // isPrivateOk implies that the singleton class of the receiver is also the + // singleton class of the enclosing method, because the receiver is not a + // module and is syntactically `self`. + auto eitherLine = + args.isPrivateOk + ? ErrorLine::fromWithoutLoc( + "Either:\n" + " - use `{}` to call it,\n" + " - remove `{}` from its definition to make it an instance method, or\n" + " - define the current method as a singleton class method using `{}`", + ".class", "self.", "def self.") + : ErrorLine::fromWithoutLoc( + "Either:\n" + " - use `{}` to call it, or\n" + " - remove `{}` from its definition to make it an instance method", + ".class", "self."); + + e.addErrorSection( + ErrorSection("There is a singleton class method with the same name:", + { + ErrorLine::from(methodOnSingleton.data(gs)->loc(), "Defined here"), + move(eitherLine), + })); + + if (args.receiverLoc().exists() && args.receiverLoc().empty()) { + e.replaceWith("Insert `self.class.`", args.funLoc().copyWithZeroLength(), "self.class."); + } else if (args.receiverLoc().exists()) { + e.replaceWith("Insert `.class`", args.receiverLoc().copyEndWithZeroLength(), ".class"); + } + canSkipFuzzyMatch = true; } + } - const auto toReplace = args.name.toString(gs); - if (args.funLoc().source(gs) != toReplace) { - auto suggestedName = possibleSymbol.isClassOrModule() ? possibleSymbol.show(gs) + ".new" - : possibleSymbol.show(gs); - e.addErrorLine(possibleSymbol.loc(gs), "Did you mean: `{}`", suggestedName); - continue; - } + if (!canSkipFuzzyMatch) { + auto alternatives = symbol.data(gs)->findMemberFuzzyMatch(gs, targetName); + for (auto alternative : alternatives) { + auto possibleSymbol = alternative.symbol; + if (!possibleSymbol.isClassOrModule() && !possibleSymbol.isMethod()) { + continue; + } + + if (possibleSymbol.isMethod()) { + auto possibleMethod = possibleSymbol.asMethodRef().data(gs); + if ((possibleMethod->flags.isPrivate || possibleMethod->owner == Symbols::Kernel()) && + !args.isPrivateOk) { + // Special-case Kernel methods, which should be treated as private, but aren't due to + // this bug: https://github.com/sorbet/sorbet/issues/4434 + // (If we fix that bug, we can delete the `Kernel` reference above.) + continue; + } + } - if (possibleSymbol.isClassOrModule()) { - if (possibleSymbol.asClassOrModuleRef().data(gs)->typeArity(gs) > 0) { - // If this call was in type sytnax, we might have already have built an - // autocorrect to turn this from `MyClass(...)` to `MyClass[...]`. + if (isSetter && possibleSymbol.name(gs).lookupWithEq(gs) == args.name) { + e.addErrorLine(possibleSymbol.loc(gs), + "Method `{}` defined here without a corresponding setter", + possibleSymbol.name(gs).show(gs)); continue; } - e.addErrorNote("Ruby uses `.new` to invoke a class's constructor"); - e.replaceWith("Insert `.new`", args.funLoc().copyEndWithZeroLength(), ".new"); - continue; - } - const auto replacement = possibleSymbol.name(gs).toString(gs); - if (replacement != toReplace) { - e.didYouMean(replacement, args.funLoc()); - e.addErrorLine(possibleSymbol.loc(gs), "Defined here"); - continue; - } + const auto toReplace = args.name.toString(gs); + if (args.funLoc().source(gs) != toReplace) { + auto suggestedName = possibleSymbol.isClassOrModule() ? possibleSymbol.show(gs) + ".new" + : possibleSymbol.show(gs); + e.addErrorLine(possibleSymbol.loc(gs), "Did you mean: `{}`", suggestedName); + continue; + } - auto possibleSymbolOwner = possibleSymbol.asMethodRef().data(gs)->owner; - if (possibleSymbolOwner.data(gs)->lookupSingletonClass(gs) == symbol) { - auto defKeyword = "def "; - auto defKeywordLen = char_traits::length(defKeyword); - auto prefixLen = defKeywordLen + toReplace.size(); - auto declLocPrefix = possibleSymbol.loc(gs).adjustLen(gs, 0, prefixLen); - e.addErrorNote("Did you mean to define `{}` as a singleton class method?", toReplace); - if (declLocPrefix.source(gs) == fmt::format("{}{}", defKeyword, toReplace)) { - e.replaceWith("Define method with `self.`", - possibleSymbol.loc(gs).adjustLen(gs, defKeywordLen, 0), "self."); - } else { - e.addErrorLine(possibleSymbol.loc(gs), "`{}` defined here", toReplace); + if (possibleSymbol.isClassOrModule()) { + if (possibleSymbol.asClassOrModuleRef().data(gs)->typeArity(gs) > 0) { + // If this call was in type syntax, we might have already have built an + // autocorrect to turn this from `MyClass(...)` to `MyClass[...]`. + continue; + } + e.addErrorNote("Ruby uses `.new` to invoke a class's constructor"); + e.replaceWith("Insert `.new`", args.funLoc().copyEndWithZeroLength(), ".new"); + continue; } - } else if (symbol.data(gs)->lookupSingletonClass(gs) == possibleSymbolOwner) { - e.addErrorNote("Did you mean to call `{}` which is a singleton class method?", - possibleSymbol.show(gs)); - if (args.receiverLoc().empty()) { - e.replaceWith("Insert `self.class.`", args.funLoc().copyWithZeroLength(), "self.class."); - } else { - e.replaceWith("Insert `.class`", args.receiverLoc().copyEndWithZeroLength(), ".class"); + + const auto replacement = possibleSymbol.name(gs).toString(gs); + if (replacement != toReplace) { + e.didYouMean(replacement, args.funLoc()); + e.addErrorLine(possibleSymbol.loc(gs), "Defined here"); + continue; + } + + auto possibleSymbolOwner = possibleSymbol.asMethodRef().data(gs)->owner; + if (possibleSymbolOwner.data(gs)->lookupSingletonClass(gs) == symbol) { + auto defKeyword = "def "; + auto defKeywordLen = char_traits::length(defKeyword); + auto prefixLen = defKeywordLen + toReplace.size(); + auto declLocPrefix = possibleSymbol.loc(gs).adjustLen(gs, 0, prefixLen); + e.addErrorNote("Did you mean to define `{}` as a singleton class method?", toReplace); + if (declLocPrefix.source(gs) == fmt::format("{}{}", defKeyword, toReplace)) { + e.replaceWith("Define method with `self.`", + possibleSymbol.loc(gs).adjustLen(gs, defKeywordLen, 0), "self."); + } else { + e.addErrorLine(possibleSymbol.loc(gs), "`{}` defined here", toReplace); + } } - e.addErrorLine(possibleSymbol.loc(gs), "`{}` defined here", toReplace); } } } @@ -801,7 +952,7 @@ DispatchResult dispatchCallSymbol(const GlobalState &gs, const DispatchArgs &arg bool hasKwsplat = nonPosArgs & 0x1; auto numKwargs = hasKwsplat ? nonPosArgs - 1 : nonPosArgs; - // p -> params, i.e., what was mentioned in the defintiion + // p -> params, i.e., what was mentioned in the definition auto pit = data->arguments.begin(); auto pend = data->arguments.end(); @@ -862,6 +1013,7 @@ DispatchResult dispatchCallSymbol(const GlobalState &gs, const DispatchArgs &arg // Extract the kwargs hash if there are keyword args present in the send TypePtr kwargs; + UnorderedMap kwargLocs; Loc kwargsLoc; if (numKwargs > 0 || hasKwsplat) { // for cases where the method accepts keyword arguments, none were given, but more positional arguments were @@ -879,10 +1031,13 @@ DispatchResult dispatchCallSymbol(const GlobalState &gs, const DispatchArgs &arg // process inlined keyword arguments { - auto kwit = args.args.begin() + args.numPosArgs; - auto kwend = args.args.begin() + args.numPosArgs + numKwargs; - + auto kwbegin = args.args.begin() + args.numPosArgs; + auto kwit = kwbegin; + auto kwend = kwbegin + numKwargs; while (kwit != kwend) { + auto kwArgIdx = distance(kwbegin, kwit); + auto kwArgLoc = + args.argLoc(args.numPosArgs + kwArgIdx).join(args.argLoc(args.numPosArgs + kwArgIdx + 1)); // if the key isn't a symbol literal, break out as this is not a valid keyword auto &key = *kwit++; if (!isa_type(key->type) || @@ -899,6 +1054,7 @@ DispatchResult dispatchCallSymbol(const GlobalState &gs, const DispatchArgs &arg auto &val = *kwit++; keys.emplace_back(key->type); values.emplace_back(val->type); + kwargLocs[cast_type_nonnull(key->type).asName()] = kwArgLoc; } } @@ -1030,7 +1186,10 @@ DispatchResult dispatchCallSymbol(const GlobalState &gs, const DispatchArgs &arg result.main.errors.emplace_back(e.build()); } } else if (!Types::isSubTypeUnderConstraint(gs, *constr, kwSplatKeyType, Types::Symbol(), - UntypedMode::AlwaysCompatible)) { + UntypedMode::AlwaysCompatible, + ErrorSection::Collector::NO_OP)) { + // ^ Passing in a noOp collector even though this call is used for error reporting, + // because it's unlikely we'll add more details to a subtype check for Symbol // TODO(jez) Highlight untyped code for this error if (auto e = gs.beginError(kwSplatArgLoc, errors::Infer::MethodArgumentMismatch)) { e.setHeader("Expected `{}` but found `{}` for keyword splat keys type", "Symbol", @@ -1049,8 +1208,9 @@ DispatchResult dispatchCallSymbol(const GlobalState &gs, const DispatchArgs &arg kwParamType = Types::untyped(method); } // TODO(jez) Highlight untyped code for this error + core::ErrorSection::Collector errorDetailsCollector; if (Types::isSubTypeUnderConstraint(gs, *constr, kwSplatValueType, kwParamType, - UntypedMode::AlwaysCompatible)) { + UntypedMode::AlwaysCompatible, errorDetailsCollector)) { continue; } @@ -1061,6 +1221,8 @@ DispatchResult dispatchCallSymbol(const GlobalState &gs, const DispatchArgs &arg method.show(gs)); e.addErrorSection(TypeAndOrigins::explainExpected(gs, kwParamType, kwParam->loc, for_)); e.addErrorSection(kwSplatTPO.explainGot(gs, args.originForUninitialized)); + core::TypeErrorDiagnostics::explainTypeMismatch(gs, e, errorDetailsCollector, kwParamType, + kwSplatValueType); e.addErrorNote( "A `{}` passed as a keyword splat must match the type of all keyword parameters\n" " because Sorbet cannot see what specific keys exist in the `{}`.", @@ -1092,7 +1254,7 @@ DispatchResult dispatchCallSymbol(const GlobalState &gs, const DispatchArgs &arg method.show(gs), prettyArity(gs, method), posArgs); } e.addErrorLine(method.data(gs)->loc(), "`{}` defined here", method.show(gs)); - if (args.name == core::Names::any() && + if (targetName == core::Names::any() && symbol == core::Symbols::T().data(gs)->lookupSingletonClass(gs)) { e.addErrorNote("If you want to allow any type as an argument, use `{}`", "T.untyped"); } @@ -1216,9 +1378,20 @@ DispatchResult dispatchCallSymbol(const GlobalState &gs, const DispatchArgs &arg } NameRef arg = key.asName(); - if (auto e = gs.beginError(args.callLoc(), errors::Infer::MethodArgumentCountMismatch)) { + auto kwargErrLoc = kwargsLoc; + auto it = kwargLocs.find(key.asName()); + // TODO(jez) This papers over some stuff around kwsplats which might get in our way + // of getting a loc for known keyword args. In those cases, we simply give up right now. + if (it != kwargLocs.end()) { + kwargErrLoc = it->second; + } + if (auto e = gs.beginError(kwargErrLoc, errors::Infer::MethodArgumentCountMismatch)) { e.setHeader("Unrecognized keyword argument `{}` passed for method `{}`", arg.show(gs), method.show(gs)); + if (kwargErrLoc.exists() && kwargErrLoc != kwargsLoc) { + auto deleteLoc = expandToLeadingComma(gs, kwargErrLoc); + e.replaceWith("Delete unrecognized keyword argument", deleteLoc, ""); + } result.main.errors.emplace_back(e.build()); } } @@ -1261,23 +1434,19 @@ DispatchResult dispatchCallSymbol(const GlobalState &gs, const DispatchArgs &arg auto extraArgsLoc = args.argLoc(maxPossiblePositional).join(lastPositionalArg); if (auto e = gs.beginError(extraArgsLoc, errors::Infer::MethodArgumentCountMismatch)) { - auto deleteLoc = extraArgsLoc; - // Heuristic to try to find leading commas. Not actually required for correctness - // (will still parse even if we delete something but don't find the comma) which is - // why we're fine with this just hard-coding two common cases (easiest to implement). - if (extraArgsLoc.adjustLen(gs, -1, 1).source(gs) == ",") { - deleteLoc = extraArgsLoc.adjust(gs, -1, 0); - } else if (extraArgsLoc.adjustLen(gs, -2, 2).source(gs) == ", ") { - deleteLoc = extraArgsLoc.adjust(gs, -2, 0); - } else if (extraArgsLoc.adjustLen(gs, -1, 1).source(gs) == " ") { - deleteLoc = extraArgsLoc.adjust(gs, -1, 0); - } + auto deleteLoc = expandToLeadingComma(gs, extraArgsLoc); - if (!hasKwargs) { + if (method == Symbols::BasicObject_initialize()) { + e.setHeader("Wrong number of arguments for constructor. Expected: `{}`, got: `{}`", 0, numArgsGiven); + e.addErrorLine(method.data(gs)->loc(), "`{}` defined here", targetName.show(gs)); + e.replaceWith("Delete extra args", deleteLoc, ""); + } else if (!hasKwargs) { e.setHeader("Too many arguments provided for method `{}`. Expected: `{}`, got: `{}`", method.show(gs), prettyArity(gs, method), numArgsGiven); - e.addErrorLine(method.data(gs)->loc(), "`{}` defined here", args.name.show(gs)); - e.replaceWith("Delete extra args", deleteLoc, ""); + e.addErrorLine(method.data(gs)->loc(), "`{}` defined here", targetName.show(gs)); + if (!deleteLoc.empty()) { + e.replaceWith("Delete extra args", deleteLoc, ""); + } } else { // if we have keyword arguments, we should print a more informative message: otherwise, we might give // people some slightly confusing error messages. @@ -1285,7 +1454,7 @@ DispatchResult dispatchCallSymbol(const GlobalState &gs, const DispatchArgs &arg // print a helpful error message e.setHeader("Too many positional arguments provided for method `{}`. Expected: `{}`, got: `{}`", method.show(gs), prettyArity(gs, method), posArgs); - e.addErrorLine(method.data(gs)->loc(), "`{}` defined here", args.name.show(gs)); + e.addErrorLine(method.data(gs)->loc(), "`{}` defined here", targetName.show(gs)); // if there's an obvious first keyword argument that the user hasn't supplied, we can mention it // explicitly @@ -1302,7 +1471,7 @@ DispatchResult dispatchCallSymbol(const GlobalState &gs, const DispatchArgs &arg e.replaceWith(fmt::format("Prefix with `{}:`", possibleArg), extraArgsLoc.copyWithZeroLength(), "{}: ", possibleArg); } - } else { + } else if (!deleteLoc.empty()) { e.replaceWith("Delete extra args", deleteLoc, ""); } } @@ -1320,11 +1489,14 @@ DispatchResult dispatchCallSymbol(const GlobalState &gs, const DispatchArgs &arg // block parameter" error, so we can use the heuristic about isSyntheticBlockArgument. // (Some RBI-only strictness levels are technically higher than strict but don't require // having written a sig. This usually manifests as `def foo(*_); end` with no sig in an RBI.) + // + // We also have to check whether the blockLoc exists and is not empty. (Sometimes the block + // can be imaginary, like from a bare `super` call). if (data->hasSig() && data->loc().exists()) { auto file = data->loc().file(); + auto blockLoc = args.blockLoc(gs); if (file.exists() && file.data(gs).strictLevel >= core::StrictLevel::Strict && - bspec.isSyntheticBlockArgument()) { - auto blockLoc = args.blockLoc(gs); + bspec.isSyntheticBlockArgument() && blockLoc.exists() && !blockLoc.empty()) { if (auto e = gs.beginError(blockLoc, core::errors::Infer::TakesNoBlock)) { e.setHeader("Method `{}` does not take a block", method.show(gs)); for (const auto loc : method.data(gs)->locs()) { @@ -1339,15 +1511,9 @@ DispatchResult dispatchCallSymbol(const GlobalState &gs, const DispatchArgs &arg } TypePtr blockType = Types::resultTypeAsSeenFrom(gs, bspec.type, data->owner, symbol, targs); - if (!blockType) { - blockType = Types::untyped(method); - } - - component.blockReturnType = Types::getProcReturnType(gs, Types::dropNil(gs, blockType)); - blockType = constr->isSolved() ? Types::instantiate(gs, blockType, *constr) - : Types::approximate(gs, blockType, *constr); - component.blockPreType = blockType; - component.blockSpec = bspec.deepCopy(); + handleBlockType(gs, component, blockType); + component.rebind = bspec.rebind; + component.rebindLoc = bspec.loc; } TypePtr &resultType = result.returnType; @@ -1355,7 +1521,7 @@ DispatchResult dispatchCallSymbol(const GlobalState &gs, const DispatchArgs &arg auto *intrinsic = method.data(gs)->getIntrinsic(); if (intrinsic != nullptr) { intrinsic->apply(gs, args, result); - // the call could have overriden constraint + // the call could have overridden constraint if (result.main.constr || constr != &core::TypeConstraint::EmptyFrozenConstraint) { constr = result.main.constr.get(); } @@ -1376,7 +1542,7 @@ DispatchResult dispatchCallSymbol(const GlobalState &gs, const DispatchArgs &arg } } if (args.block == nullptr) { - // if block is there we do not attempt to solve the constaint. CFG adds an explicit solve + // if block is there we do not attempt to solve the constraint. CFG adds an explicit solve // node that triggers constraint solving if (!constr->solve(gs)) { if (auto e = gs.beginError(errLoc, errors::Infer::GenericMethodConstraintUnsolved)) { @@ -1394,7 +1560,7 @@ DispatchResult dispatchCallSymbol(const GlobalState &gs, const DispatchArgs &arg // TODO(jez) Highlight untyped code for this error if (blockType && !core::Types::isSubType(gs, core::Types::nilClass(), blockType)) { if (auto e = gs.beginError(args.callLoc().copyEndWithZeroLength(), errors::Infer::BlockNotPassed)) { - e.setHeader("`{}` requires a block parameter, but no block was passed", args.name.show(gs)); + e.setHeader("`{}` requires a block parameter, but no block was passed", targetName.show(gs)); e.addErrorLine(method.data(gs)->loc(), "defined here"); result.main.errors.emplace_back(e.build()); } @@ -1547,8 +1713,7 @@ DispatchResult badMetaTypeCall(const GlobalState &gs, const DispatchArgs &args, } // namespace DispatchResult MetaType::dispatchCall(const GlobalState &gs, const DispatchArgs &args) const { - auto funLoc = args.funLoc(); - auto errLoc = (funLoc.exists() && !funLoc.empty()) ? funLoc : args.callLoc(); + auto errLoc = args.errLoc(); switch (args.name.rawId()) { case Names::new_().rawId(): { if (!canCallNew(gs, wrapped)) { @@ -1567,7 +1732,8 @@ DispatchResult MetaType::dispatchCall(const GlobalState &gs, const DispatchArgs args.block, args.originForUninitialized, /* isPrivateOk */ true, - args.suppressErrors}; + args.suppressErrors, + args.enclosingMethodForSuper}; auto original = wrapped.dispatchCall(gs, innerArgs); original.returnType = wrapped; original.main.sendTp = wrapped; @@ -1740,7 +1906,7 @@ class T_attached_class : public IntrinsicMethod { public: void apply(const GlobalState &gs, const DispatchArgs &args, DispatchResult &res) const override { // Most syntactically visible calls to `T.attached_class` should be handled by the - // `.attached_class` intrisnic. + // `.attached_class` intrinsic. // // This intrinsic remains just on the off chance that people misuse `T.attached_class` as a // value some other way (e.g., t = T; t.attached_class). @@ -1775,8 +1941,9 @@ class T_must : public IntrinsicMethod { } auto ret = Types::dropNil(gs, args.args[0]->type); if (ret == args.args[0]->type) { - if (auto e = gs.beginError(args.argLoc(0), errors::Infer::InvalidCast)) { - if (args.args[0]->type.isUntyped()) { + auto code = args.args[0]->type.isUntyped() ? errors::Infer::MustOnUntyped : errors::Infer::InvalidCast; + if (auto e = gs.beginError(args.argLoc(0), code)) { + if (code == errors::Infer::MustOnUntyped) { e.setHeader("`{}` called on `{}`, which is redundant", methodName, args.args[0]->type.show(gs)); } else { e.setHeader("`{}` called on `{}`, which is never `{}`", methodName, args.args[0]->type.show(gs), @@ -1786,7 +1953,7 @@ class T_must : public IntrinsicMethod { auto replaceLoc = args.callLoc(); const auto locWithoutTMust = args.argLoc(0); if (replaceLoc.exists() && locWithoutTMust.exists()) { - e.replaceWith(fmt::format("Remove `{}`", methodName), replaceLoc, "{}", + e.replaceWith(fmt::format("Remove redundant `{}`", methodName), replaceLoc, "{}", locWithoutTMust.source(gs).value()); } } @@ -1976,22 +2143,27 @@ class Class_new : public IntrinsicMethod { } void apply(const GlobalState &gs, const DispatchArgs &args, DispatchResult &res) const override { - auto mustExist = true; - ClassOrModuleRef self = unwrapSymbol(gs, args.thisType, mustExist); - - auto attachedClass = self.data(gs)->attachedClass(gs); - if (!attachedClass.exists()) { - // If someone takes `klass: T::Class[T.anything]` and calls `klass.new`, the call is - // actually going to be on an "instance" not a singleton (Class.new, the one on the - // singleton, is the one that defines a new class at runtime). - // - // In that case, there's no attachedClass to look for an `initialize` method on. - // We could _maybe_ imagine trying to dispatch to `initialize` on the `` - // type argument? But I haven't thought about what the consequences of that would be. - ENFORCE(self == Symbols::Class()); + auto *selfApp = cast_type(args.thisType); + if (selfApp == nullptr) { return; } - auto instanceTy = attachedClass.data(gs)->externalType(); + + // If someone takes `klass: T::Class[T.anything]` and calls `klass.new`, the call is + // actually going to be on an "instance" not a singleton (Class.new, the one on the + // singleton, is the one that defines a new class at runtime). + // + // In that case, `Class` is an instance class (there's no `->attachedClass(gs)` to look for + // an `initialize` method on). So let's extract out the type applied to the + // `` type member and forward the dispatch to `initialize` on that type. + // + // Note that this subsumes cases like calls to new on `T.class_of(MyClass)` because class + // singleton classes ARE applied types, obviating the need to call `->attachedClass(gs)` at all. + auto currentAlignment = Types::alignBaseTypeArgs(gs, selfApp->klass, selfApp->targs, Symbols::Class()); + auto it = absl::c_find_if( + currentAlignment, [&](auto tmRef) { return tmRef.data(gs)->name == Names::Constants::AttachedClass(); }); + ENFORCE(it != currentAlignment.end()); + auto instanceTy = selfApp->targs[distance(currentAlignment.begin(), it)]; + // The Ruby VM treats `initialize` as private by default, but allows calling it directly within `new`. DispatchArgs innerArgs{Names::initialize(), args.locs, @@ -2003,23 +2175,44 @@ class Class_new : public IntrinsicMethod { args.block, args.originForUninitialized, /* isPrivateOk */ true, - args.suppressErrors}; + args.suppressErrors, + args.enclosingMethodForSuper}; auto dispatched = instanceTy.dispatchCall(gs, innerArgs); - for (auto &err : res.main.errors) { - dispatched.main.errors.emplace_back(std::move(err)); - } - res.main.errors.clear(); - res.main = move(dispatched.main); - if (!res.main.method.exists()) { + // Sorbet *should* treat a missing `initialize` here as a bug, because it can't know the + // arity/types of the constructor. The most common way `initialize` might be missing is if + // the user has written `Class`, which is treated like `T::Class[T.anything]`. In that case, + // the type means "this could be literally any class," and so the right thing to do would be + // to request a better constructor from the user. + // + // But there's an unknown amount of code relying on this right now (from before `T::Class` + // was built), which in combination with some other bugs, would mean that treating a missing + // `initialize` as an error would introduce low-value friction until we fix two other bugs: + // + // - https://github.com/sorbet/sorbet/issues/1317 + // - https://github.com/sorbet/sorbet/issues/7309 + // + // More information here: + // https://github.com/sorbet/sorbet/pull/7285#issuecomment-1718167511 + // + // There's also the state of things at runtime, described in this comment: + // https://github.com/sorbet/sorbet/pull/6888/files#diff-b565686f17b8592d5c1e544cd639aaac3244869ac059f2db416a2ac24aa94675R9 + // + // So for now, simply swallow "missing `initialize`" errors. + if (dispatched.main.method.exists()) { // If we actually dispatched to some `initialize` method, use that method as the result, // because it will be more interesting to people downstream who want to look at the // result. // // But if this class hasn't defined a custom `initialize` method, still record that we // dispatched to *something*, namely `Class#new`. - res.main.method = core::Symbols::Class_new(); + + for (auto &err : res.main.errors) { + dispatched.main.errors.emplace_back(std::move(err)); + } + res.main = move(dispatched.main); } + res.main.sendTp = instanceTy; } } Class_new; @@ -2071,7 +2264,7 @@ void applySig(const GlobalState &gs, const DispatchArgs &args, DispatchResult &r auto recv = *args.args[0]; res = recv.type.dispatchCall(gs, {core::Names::sig(), callLocs, numPosArgs, dispatchArgsArgs, recv.type, recv, recv.type, args.block, args.originForUninitialized, args.isPrivateOk, - args.suppressErrors}); + args.suppressErrors, args.enclosingMethodForSuper}); } class SorbetPrivateStatic_sig : public IntrinsicMethod { @@ -2316,7 +2509,8 @@ class Magic_callWithSplat : public IntrinsicMethod { args.block, args.originForUninitialized, args.isPrivateOk, - args.suppressErrors}; + args.suppressErrors, + args.enclosingMethodForSuper}; auto dispatched = receiver->type.dispatchCall(gs, innerArgs); for (auto &err : dispatched.main.errors) { res.main.errors.emplace_back(std::move(err)); @@ -2372,7 +2566,8 @@ class Magic_callWithBlock : public IntrinsicMethod { nullptr, originForUninitialized, IMPLICIT_CONVERSION_ALLOWS_PRIVATE, - suppressErrors}; + suppressErrors, + core::NameRef::noName()}; auto dispatched = nonNilBlockType.type.dispatchCall(gs, innerArgs); for (auto &err : dispatched.main.errors) { gs._error(std::move(err)); @@ -2415,7 +2610,7 @@ class Magic_callWithBlock : public IntrinsicMethod { ENFORCE(!methodArgs.empty()); const auto &bspec = methodArgs.back(); ENFORCE(bspec.flags.isBlock); - auto for_ = ErrorColors::format("for block argument `{}` of method `{}`", bspec.argumentName(gs), + auto for_ = ErrorColors::format("block argument `{}` of method `{}`", bspec.argumentName(gs), dispatchComp.method.show(gs)); e.addErrorSection(TypeAndOrigins::explainExpected(gs, blockType, bspec.loc, for_)); } @@ -2429,8 +2624,9 @@ class Magic_callWithBlock : public IntrinsicMethod { auto &constr = dispatched.main.constr; auto &blockPreType = dispatched.main.blockPreType; // TODO(jez) How should this interact with highlight untyped? + core::ErrorSection::Collector errorDetailsCollector; if (blockPreType && !Types::isSubTypeUnderConstraint(gs, *constr, passedInBlockType, blockPreType, - UntypedMode::AlwaysCompatible)) { + UntypedMode::AlwaysCompatible, errorDetailsCollector)) { auto nonNilableBlockType = Types::dropNil(gs, blockPreType); if (isa_type(passedInBlockType) && cast_type_nonnull(passedInBlockType).symbol == Symbols::Proc() && @@ -2459,6 +2655,8 @@ class Magic_callWithBlock : public IntrinsicMethod { if (!dispatched.secondary) { Magic_callWithBlock::showLocationOfArgDefn(gs, e, blockPreType, dispatched.main); } + core::TypeErrorDiagnostics::explainTypeMismatch(gs, e, errorDetailsCollector, blockPreType, + passedInBlockType); } } @@ -2476,7 +2674,7 @@ class Magic_callWithBlock : public IntrinsicMethod { // TODO(jez) How should this interact with highlight untyped? // This subtype check is here to discover the correct generic bounds. Types::isSubTypeUnderConstraint(gs, *constr, passedInBlockType, bspecType, - UntypedMode::AlwaysCompatible); + UntypedMode::AlwaysCompatible, ErrorSection::Collector::NO_OP); } } it = it->secondary.get(); @@ -2531,7 +2729,7 @@ class Magic_callWithBlock : public IntrinsicMethod { auto what = core::errors::Infer::errorClassForUntyped(gs, args.locs.file, receiver->type); if (auto e = gs.beginError(args.argLoc(0), what)) { e.setHeader("Call to method `{}` on `{}`", fn.show(gs), "T.untyped"); - TypeErrorDiagnostics::explainUntyped(gs, e, what, args.fullType, args.originForUninitialized); + TypeErrorDiagnostics::explainUntyped(gs, e, what, *receiver, args.originForUninitialized); } res.returnType = receiver->type; @@ -2580,7 +2778,8 @@ class Magic_callWithBlock : public IntrinsicMethod { link, args.originForUninitialized, args.isPrivateOk, - args.suppressErrors}; + args.suppressErrors, + args.enclosingMethodForSuper}; Magic_callWithBlock::simulateCall(gs, receiver, innerArgs, link, finalBlockType, args.argLoc(2), args.callLoc(), res); @@ -2694,7 +2893,8 @@ class Magic_callWithSplatAndBlock : public IntrinsicMethod { link, args.originForUninitialized, args.isPrivateOk, - args.suppressErrors}; + args.suppressErrors, + args.enclosingMethodForSuper}; Magic_callWithBlock::simulateCall(gs, receiver, innerArgs, link, finalBlockType, args.argLoc(4), args.callLoc(), res); @@ -2747,7 +2947,8 @@ class Magic_suggestUntypedFieldType : public IntrinsicMethod { auto fieldName = fieldNameTy.asName().show(gs); auto suggestType = res.returnType; - if (definingMethodName != core::Names::initialize() && definingMethodName != core::Names::staticInit()) { + if (definingMethodName != core::Names::initialize() && definingMethodName != core::Names::staticInit() && + definingMethodName != core::Names::beforeAngles()) { suggestType = core::Types::any(gs, Types::nilClass(), suggestType); } e.setHeader("The {} variable `{}` must be declared using `{}` when specifying `{}`", fieldKind, fieldName, @@ -2871,6 +3072,7 @@ class Magic_checkAndAnd : public IntrinsicMethod { args.originForUninitialized, args.isPrivateOk, args.suppressErrors, + args.enclosingMethodForSuper, }; auto dispatched = selfTy.type.dispatchCall(gs, innerArgs); @@ -2899,6 +3101,7 @@ class Magic_checkAndAnd : public IntrinsicMethod { // We already reported one visibility error, if relevant /* isPrivateOk */ true, args.suppressErrors, + args.enclosingMethodForSuper, }; auto retried = selfTyAndAnd.type.dispatchCall(gs, newInnerArgs); @@ -2927,14 +3130,18 @@ class Magic_checkAndAnd : public IntrinsicMethod { "&."); auto funLoc = core::Loc(args.locs.file, args.locs.fun); - if (funLoc.exists() && !funLoc.empty() && + if (selfTyAndAnd.origins.size() == 2 && funLoc.exists() && !funLoc.empty() && funLoc.adjustLen(gs, -1, 1).source(gs) == ".") { - auto andAndLoc = args.locs.args[2]; + // Our checkAndAnd desugarer only fires if the LHS and RHS have matching Send + // nodes. In these cases, we know that there are two origins and that the first + // origin is the call on the LHS. + auto lhsLoc = selfTyAndAnd.origins[0]; + ENFORCE(lhsLoc.beginPos() < selfTyAndAnd.origins[1].beginPos()); newErr.addAutocorrect(AutocorrectSuggestion{ "Refactor to use `&.`", { AutocorrectSuggestion::Edit{ - core::Loc(args.locs.file, andAndLoc.beginPos(), recvLoc.beginPos()), + core::Loc(args.locs.file, lhsLoc.beginPos(), recvLoc.beginPos()), "", }, AutocorrectSuggestion::Edit{funLoc.adjustLen(gs, -1, 1), "&."}, @@ -2982,7 +3189,8 @@ class Magic_splat : public IntrinsicMethod { nullptr, args.originForUninitialized, IMPLICIT_CONVERSION_ALLOWS_PRIVATE, - args.suppressErrors}; + args.suppressErrors, + args.enclosingMethodForSuper}; auto dispatched = arg->type.dispatchCall(gs, dispatch); // The VM handles the case of an error when dispatching to_a, so the only @@ -3252,9 +3460,11 @@ class Shape_squareBracketsEq : public IntrinsicMethod { auto valueType = shape.values[*idx]; auto expectedType = valueType; auto actualType = *args.args[1]; - // This check (with the dropLiteral's) mimicks what we do for pinning errors in environment.cc + // This check (with the dropLiteral's) mimics what we do for pinning errors in environment.cc // TODO(jez) How should this interact with highlight untyped? - if (!Types::isSubType(gs, Types::dropLiteral(gs, actualType.type), Types::dropLiteral(gs, expectedType))) { + core::ErrorSection::Collector errorDetailsCollector; + if (!Types::isSubType(gs, Types::dropLiteral(gs, actualType.type), Types::dropLiteral(gs, expectedType), + errorDetailsCollector)) { auto argLoc = args.argLoc(1); if (auto e = gs.beginError(argLoc, errors::Infer::MethodArgumentMismatch)) { @@ -3264,6 +3474,8 @@ class Shape_squareBracketsEq : public IntrinsicMethod { ErrorSection("Shape originates from here:", args.fullType.origins2Explanations(gs, args.originForUninitialized))); e.addErrorSection(actualType.explainGot(gs, args.originForUninitialized)); + core::TypeErrorDiagnostics::explainTypeMismatch(gs, e, errorDetailsCollector, expectedType, + actualType.type); if (args.fullType.origins.size() == 1 && isa_type(arg)) { auto argLit = cast_type_nonnull(arg); @@ -3393,7 +3605,8 @@ class Magic_toHash : public IntrinsicMethod { nullptr, args.originForUninitialized, IMPLICIT_CONVERSION_ALLOWS_PRIVATE, - args.suppressErrors}; + args.suppressErrors, + args.enclosingMethodForSuper}; res = arg->type.dispatchCall(gs, dispatch); } @@ -3433,7 +3646,8 @@ class Magic_mergeHash : public IntrinsicMethod { nullptr, args.originForUninitialized, args.isPrivateOk, - args.suppressErrors}; + args.suppressErrors, + args.enclosingMethodForSuper}; res = accType.dispatchCall(gs, mergeArgs); } @@ -3502,7 +3716,8 @@ class Magic_mergeHashValues : public IntrinsicMethod { nullptr, args.originForUninitialized, args.isPrivateOk, - args.suppressErrors}; + args.suppressErrors, + args.enclosingMethodForSuper}; res = accType.dispatchCall(gs, mergeArgs); } @@ -3529,7 +3744,7 @@ void digImplementation(const GlobalState &gs, const DispatchArgs &args, Dispatch methodToDigWith, baseCaseLocs, 1, /* numPosArgs */ baseCaseArgTypes, args.selfType, {args.selfType, args.fullType.origins}, args.selfType, args.block, args.originForUninitialized, - args.isPrivateOk, args.suppressErrors, + args.isPrivateOk, args.suppressErrors, args.enclosingMethodForSuper, }; auto dispatched = args.selfType.dispatchCall(gs, baseCaseArgs); @@ -3542,7 +3757,7 @@ void digImplementation(const GlobalState &gs, const DispatchArgs &args, Dispatch return; } - auto newSelfType = Types::dropSubtypesOf(gs, dispatched.returnType, core::Symbols::NilClass()); + auto newSelfType = Types::dropNil(gs, dispatched.returnType); if (newSelfType.isBottom()) { // The result was `nil` (or maybe `T.nilable(T.noreturn)` == `nil`, or maybe just plain `T.noreturn`) @@ -3601,6 +3816,7 @@ void digImplementation(const GlobalState &gs, const DispatchArgs &args, Dispatch args.originForUninitialized, false, /* isPrivateOk */ args.suppressErrors, + args.enclosingMethodForSuper, }; auto recursiveDispatch = newSelfType.dispatchCall(gs, digArgs); @@ -3653,7 +3869,8 @@ class Array_flatten : public IntrinsicMethod { nullptr, args.originForUninitialized, IMPLICIT_CONVERSION_ALLOWS_PRIVATE, - args.suppressErrors}; + args.suppressErrors, + args.enclosingMethodForSuper}; auto dispatched = type.dispatchCall(gs, innerArgs); if (dispatched.main.errors.empty()) { @@ -3833,6 +4050,31 @@ class Array_zip : public IntrinsicMethod { } } Array_zip; +class Array_transpose : public IntrinsicMethod { +public: + void apply(const GlobalState &gs, const DispatchArgs &args, DispatchResult &res) const override { + auto *ap = cast_type(args.thisType); + ENFORCE(ap->klass == Symbols::Array() || ap->klass.data(gs)->derivesFrom(gs, Symbols::Array())); + ENFORCE(!ap->targs.empty()); + auto &elementType = ap->targs.front(); + + auto *tuple = cast_type(elementType); + if (tuple == nullptr) { + return; + } + + // The transpose of an array of tuples is a tuple of arrays. + vector transposed; + transposed.reserve(tuple->elems.size()); + + for (auto &elem : tuple->elems) { + transposed.emplace_back(Types::arrayOf(gs, elem)); + } + + res.returnType = make_type(move(transposed)); + } +} Array_transpose; + class Symbol_eqeq : public IntrinsicMethod { public: void apply(const GlobalState &gs, const DispatchArgs &args, DispatchResult &res) const override { @@ -3846,9 +4088,7 @@ class Symbol_eqeq : public IntrinsicMethod { auto isOnlySymbol = Types::isSubType(gs, args.fullType.type, Types::any(gs, Types::nilClass(), Types::Symbol())); if (isOnlySymbol && Types::all(gs, args.fullType.type, args.args[0]->type).isBottom()) { - auto funLoc = args.funLoc(); - auto errLoc = (funLoc.exists() && !funLoc.empty()) ? funLoc : args.callLoc(); - if (auto e = gs.beginError(errLoc, errors::Infer::NonOverlappingEqual)) { + if (auto e = gs.beginError(args.errLoc(), errors::Infer::NonOverlappingEqual)) { e.setHeader("Comparison between `{}` and `{}` is always false", args.fullType.type.show(gs), args.args[0]->type.show(gs)); e.addErrorSection(args.fullType.explainGot(gs, args.originForUninitialized)); @@ -3882,9 +4122,7 @@ class String_eqeq : public IntrinsicMethod { // to because it would likely be a cause for surprise). Types::isSubType(gs, args.args[0]->type, Types::any(gs, Types::nilClass(), Types::Symbol())) && Types::all(gs, args.fullType.type, args.args[0]->type).isBottom()) { - auto funLoc = args.funLoc(); - auto errLoc = (funLoc.exists() && !funLoc.empty()) ? funLoc : args.callLoc(); - if (auto e = gs.beginError(errLoc, errors::Infer::NonOverlappingEqual)) { + if (auto e = gs.beginError(args.errLoc(), errors::Infer::NonOverlappingEqual)) { e.setHeader("Comparison between `{}` and `{}` is always false", args.fullType.type.show(gs), args.args[0]->type.show(gs)); e.addErrorSection(args.fullType.explainGot(gs, args.originForUninitialized)); @@ -3916,6 +4154,31 @@ class Kernel_proc : public IntrinsicMethod { } } Kernel_proc; +class Kernel_lambdaTLet : public IntrinsicMethod { +public: + void apply(const GlobalState &gs, const DispatchArgs &args, DispatchResult &res) const override { + if (args.block == nullptr) { + return; + } + + if (args.args.size() != 1) { + return; + } + + auto procType = Types::unwrapType(gs, args.argLoc(0), args.args[0]->type); + if (!Types::isSubType(gs, procType, Types::nilableProcClass())) { + if (auto e = gs.beginError(args.argLoc(0), core::errors::Infer::CastTypeMismatch)) { + e.setHeader("Lambda type annotation must be either `{}` or a `{}` type (and possibly nilable)", "Proc", + "T.proc"); + e.addErrorLine(args.callLoc(), "For lambda here"); + } + return; + } + + handleBlockType(gs, res.main, procType); + } +} Kernel_lambdaTLet; + class Kernel_raise : public IntrinsicMethod { public: vector dispatchesTo() const override { @@ -3975,6 +4238,7 @@ class Kernel_raise : public IntrinsicMethod { args.originForUninitialized, IMPLICIT_CONVERSION_ALLOWS_PRIVATE, args.suppressErrors, + args.enclosingMethodForSuper, }; auto dispatched = classArg->type.dispatchCall(gs, newArgs); @@ -4018,7 +4282,8 @@ class Enumerable_toH : public IntrinsicMethod { nullptr, args.originForUninitialized, args.isPrivateOk, - args.suppressErrors}; + args.suppressErrors, + args.enclosingMethodForSuper}; auto dispatched = hash.dispatchCall(gs, dispatch); for (auto &err : dispatched.main.errors) { res.main.errors.emplace_back(std::move(err)); @@ -4143,6 +4408,30 @@ class T_Enum_tripleEq : public IntrinsicMethod { } } T_Enum_tripleEq; +class GenericForwarder_tripleEq : public IntrinsicMethod { +public: + void apply(const GlobalState &gs, const DispatchArgs &args, DispatchResult &res) const override { + auto forwarderSingleton = Symbols::noClassOrModule(); + if (auto *app = cast_type(args.thisType)) { + forwarderSingleton = app->klass; + } else { + forwarderSingleton = cast_type_nonnull(args.thisType).symbol; + } + auto forwarderSym = forwarderSingleton.data(gs)->attachedClass(gs); + + if (auto e = gs.beginError(args.errLoc(), core::errors::Infer::MetaTypeDispatchCall)) { + auto realSym = forwarderSym.maybeUnwrapBuiltinGenericForwarder(); + ENFORCE(realSym.exists()); + auto realStr = realSym.show(gs); + e.setHeader("Use `{}` without any `{}` prefix to match on a stdlib generic type", realStr, "T::"); + auto receiverLoc = args.receiverLoc(); + if (receiverLoc.exists()) { + e.replaceWith(fmt::format("Replace with {}", realStr), receiverLoc, "{}", realStr); + } + } + } +} GenericForwarder_tripleEq; + const vector intrinsics{ {Symbols::T(), Intrinsic::Kind::Singleton, Names::untyped(), &T_untyped}, {Symbols::T(), Intrinsic::Kind::Singleton, Names::must(), &T_must}, @@ -4225,12 +4514,14 @@ const vector intrinsics{ {Symbols::Array(), Intrinsic::Kind::Instance, Names::product(), &Array_product}, {Symbols::Array(), Intrinsic::Kind::Instance, Names::compact(), &Array_compact}, {Symbols::Array(), Intrinsic::Kind::Instance, Names::zip(), &Array_zip}, + {Symbols::Array(), Intrinsic::Kind::Instance, Names::transpose(), &Array_transpose}, {Symbols::Symbol(), Intrinsic::Kind::Instance, Names::eqeq(), &Symbol_eqeq}, {Symbols::String(), Intrinsic::Kind::Instance, Names::eqeq(), &String_eqeq}, {Symbols::Kernel(), Intrinsic::Kind::Instance, Names::proc(), &Kernel_proc}, {Symbols::Kernel(), Intrinsic::Kind::Instance, Names::lambda(), &Kernel_proc}, + {Symbols::Kernel(), Intrinsic::Kind::Instance, Names::lambdaTLet(), &Kernel_lambdaTLet}, {Symbols::Kernel(), Intrinsic::Kind::Instance, Names::raise(), &Kernel_raise}, {Symbols::Kernel(), Intrinsic::Kind::Instance, Names::fail(), &Kernel_raise}, @@ -4238,6 +4529,16 @@ const vector intrinsics{ {Symbols::Module(), Intrinsic::Kind::Instance, Names::tripleEq(), &Module_tripleEq}, {Symbols::T_Enum(), Intrinsic::Kind::Instance, Names::tripleEq(), &T_Enum_tripleEq}, + + {Symbols::T_Array(), Intrinsic::Kind::Singleton, Names::tripleEq(), &GenericForwarder_tripleEq}, + {Symbols::T_Hash(), Intrinsic::Kind::Singleton, Names::tripleEq(), &GenericForwarder_tripleEq}, + {Symbols::T_Enumerable(), Intrinsic::Kind::Singleton, Names::tripleEq(), &GenericForwarder_tripleEq}, + {Symbols::T_Enumerator(), Intrinsic::Kind::Singleton, Names::tripleEq(), &GenericForwarder_tripleEq}, + {Symbols::T_Enumerator_Lazy(), Intrinsic::Kind::Singleton, Names::tripleEq(), &GenericForwarder_tripleEq}, + {Symbols::T_Enumerator_Chain(), Intrinsic::Kind::Singleton, Names::tripleEq(), &GenericForwarder_tripleEq}, + {Symbols::T_Range(), Intrinsic::Kind::Singleton, Names::tripleEq(), &GenericForwarder_tripleEq}, + {Symbols::T_Set(), Intrinsic::Kind::Singleton, Names::tripleEq(), &GenericForwarder_tripleEq}, + {Symbols::T_Class(), Intrinsic::Kind::Singleton, Names::tripleEq(), &GenericForwarder_tripleEq}, }; UnorderedMap> computeIntrinsicsDispatchMap() { diff --git a/core/types/printing.cc b/core/types/printing.cc index 106cef5891..059c6e6b0c 100644 --- a/core/types/printing.cc +++ b/core/types/printing.cc @@ -53,12 +53,13 @@ string argTypeForUnresolvedAppliedType(const GlobalState &gs, const TypePtr &t, } // namespace string UnresolvedAppliedType::show(const GlobalState &gs, ShowOptions options) const { - string resolvedString = options.showForRBI ? "" : " (unresolved)"; + string resolvedString = options.useValidSyntax ? "" : " (unresolved)"; return fmt::format("{}[{}]{}", this->klass.show(gs, options), fmt::map_join(targs, ", ", [&](auto targ) { - return options.showForRBI ? argTypeForUnresolvedAppliedType(gs, targ, options) - : targ.show(gs, options); + return options.useValidSyntax + ? argTypeForUnresolvedAppliedType(gs, targ, options) + : targ.show(gs, options); }), resolvedString); } @@ -68,7 +69,7 @@ string NamedLiteralType::toStringWithTabs(const GlobalState &gs, int tabs) const } string NamedLiteralType::show(const GlobalState &gs, ShowOptions options) const { - if (options.showForRBI) { + if (options.useValidSyntax) { // RBI generator: Users type the class name, not `String("value")`. return fmt::format("{}", this->underlying(gs).show(gs, options)); } @@ -96,7 +97,7 @@ string IntegerLiteralType::toStringWithTabs(const GlobalState &gs, int tabs) con } string IntegerLiteralType::show(const GlobalState &gs, ShowOptions options) const { - if (options.showForRBI) { + if (options.useValidSyntax) { // RBI generator: Users type the class name, not `String("value")`. return fmt::format("{}", this->underlying(gs).show(gs, options)); } @@ -113,7 +114,7 @@ string FloatLiteralType::toStringWithTabs(const GlobalState &gs, int tabs) const } string FloatLiteralType::show(const GlobalState &gs, ShowOptions options) const { - if (options.showForRBI) { + if (options.useValidSyntax) { // RBI generator: Users type the class name, not `String("value")`. return fmt::format("{}", this->underlying(gs).show(gs, options)); } @@ -188,15 +189,15 @@ string ShapeType::show(const GlobalState &gs, ShowOptions options) const { keyStr = keyLiteral.asName().show(gs); sepStr = ": "; } else { - keyStr = options.showForRBI ? keyLiteral.showValue(gs) : keyLiteral.show(gs, options); + keyStr = options.useValidSyntax ? keyLiteral.showValue(gs) : keyLiteral.show(gs, options); } } else if (isa_type(key)) { const auto &keyLiteral = cast_type_nonnull(key); - keyStr = options.showForRBI ? keyLiteral.showValue(gs) : keyLiteral.show(gs, options); + keyStr = options.useValidSyntax ? keyLiteral.showValue(gs) : keyLiteral.show(gs, options); } else { ENFORCE(isa_type(key)); const auto &keyLiteral = cast_type_nonnull(key); - keyStr = options.showForRBI ? keyLiteral.showValue(gs) : keyLiteral.show(gs, options); + keyStr = options.useValidSyntax ? keyLiteral.showValue(gs) : keyLiteral.show(gs, options); } fmt::format_to(std::back_inserter(buf), "{}{}{}", keyStr, sepStr, value.show(gs, options)); @@ -214,7 +215,7 @@ string AliasType::toStringWithTabs(const GlobalState &gs, int tabs) const { } string AliasType::show(const GlobalState &gs, ShowOptions options) const { - if (options.showForRBI) { + if (options.useValidSyntax) { return this->symbol.show(gs); } return fmt::format("", this->symbol.showFullName(gs)); @@ -461,7 +462,7 @@ string AppliedType::show(const GlobalState &gs, ShowOptions options) const { int arg_num = 0; fmt::format_to(std::back_inserter(buf), "{}", fmt::map_join( - targs_it, this->targs.end(), ", ", [&](auto targ) -> auto { + targs_it, this->targs.end(), ", ", [&](auto targ) -> auto{ return fmt::format("arg{}: {}", arg_num++, targ.show(gs, options)); })); @@ -477,7 +478,7 @@ string AppliedType::show(const GlobalState &gs, ShowOptions options) const { return to_string(buf); } else { // T.class_of(klass)[arg1, arg2] is never valid syntax in an RBI - if (options.showForRBI && this->klass.data(gs)->isSingletonClass(gs)) { + if (options.useValidSyntax && this->klass.data(gs)->isSingletonClass(gs)) { return this->klass.show(gs, options); } fmt::format_to(std::back_inserter(buf), "{}", this->klass.show(gs, options)); diff --git a/core/types/subtyping.cc b/core/types/subtyping.cc index 074d02c965..c5bccc5802 100644 --- a/core/types/subtyping.cc +++ b/core/types/subtyping.cc @@ -177,7 +177,7 @@ TypePtr glbDistributeAnd(const GlobalState &gs, const TypePtr &t1, const TypePtr return AndType::make_shared(t1, t2); } -// only keep knowledge in t1 that is not already present in t2. Return the same reference if unchaged +// only keep knowledge in t1 that is not already present in t2. Return the same reference if unchanged TypePtr dropLubComponents(const GlobalState &gs, const TypePtr &t1, const TypePtr &t2) { if (auto *a1 = cast_type(t1)) { auto a1a = dropLubComponents(gs, a1->left, t2); @@ -246,7 +246,7 @@ TypePtr Types::lub(const GlobalState &gs, const TypePtr &t1, const TypePtr &t2) } } - if (auto *o2 = cast_type(t2)) { // 3, 5, 6 + if (isa_type(t2)) { // 3, 5, 6 categoryCounterInc("lub", "or>"); return lubDistributeOr(gs, t2, t1); } else if (auto *a2 = cast_type(t2)) { // 2, 4 @@ -548,7 +548,7 @@ TypePtr lubGround(const GlobalState &gs, const TypePtr &t1, const TypePtr &t2) { // if (g1->kind() > g2->kind()) { // force the relation to be symmentric and half the implementation // return lubGround(gs, t2, t1); // } - /** this implementation makes a bet that types are small and very likely to be collapsable. + /** this implementation makes a bet that types are small and very likely to be collapsible. * The more complex types we have, the more likely this bet is to be wrong. */ if (t1 == t2) { @@ -592,7 +592,7 @@ TypePtr glbGround(const GlobalState &gs, const TypePtr &t1, const TypePtr &t2) { if (t1.kind() > t1.kind()) { // force the relation to be symmentric and half the implementation return glbGround(gs, t2, t1); } - /** this implementation makes a bet that types are small and very likely to be collapsable. + /** this implementation makes a bet that types are small and very likely to be collapsible. * The more complex types we have, the more likely this bet is to be wrong. */ if (t1 == t2) { @@ -1038,6 +1038,14 @@ bool classSymbolIsAsGoodAs(const GlobalState &gs, ClassOrModuleRef c1, ClassOrMo return c1 == c2 || c1.data(gs)->derivesFrom(gs, c2); } +void doesNotDeriveFrom(const GlobalState &gs, ErrorSection::Collector &errorDetailsCollector, ClassOrModuleRef left, + ClassOrModuleRef right) { + auto subCollector = errorDetailsCollector.newCollector(); + auto message = ErrorColors::format("`{}` does not derive from `{}`", left.show(gs), right.show(gs)); + subCollector.message = message; + errorDetailsCollector.addErrorDetails(move(subCollector)); +} + void compareToUntyped(const GlobalState &gs, TypeConstraint &constr, const TypePtr &ty, const TypePtr &blame) { ENFORCE(blame.isUntyped()); if (is_proxy_type(ty)) { @@ -1062,7 +1070,7 @@ void compareToUntyped(const GlobalState &gs, TypeConstraint &constr, const TypeP } else if (auto *t = cast_type(ty)) { compareToUntyped(gs, constr, t->left, blame); compareToUntyped(gs, constr, t->right, blame); - } else if (auto *t = cast_type(ty)) { + } else if (isa_type(ty)) { constr.rememberIsSubtype(gs, ty, blame); constr.rememberIsSubtype(gs, blame, ty); } @@ -1070,8 +1078,11 @@ void compareToUntyped(const GlobalState &gs, TypeConstraint &constr, const TypeP // "Single" means "ClassType or ProxyType"; since ProxyTypes are constrained to // be proxies over class types, this means "class or class-like" +template bool isSubTypeUnderConstraintSingle(const GlobalState &gs, TypeConstraint &constr, UntypedMode mode, const TypePtr &t1, - const TypePtr &t2) { + const TypePtr &t2, T &errorDetailsCollector) { + constexpr auto shouldAddErrorDetails = std::is_same::value; + ENFORCE(t1 != nullptr); ENFORCE(t2 != nullptr); @@ -1147,14 +1158,16 @@ bool isSubTypeUnderConstraintSingle(const GlobalState &gs, TypeConstraint &const if (!isSelfTypeT1) { auto self2 = cast_type_nonnull(t2); if (auto *lambdaParam = cast_type(self2.definition.resultType(gs))) { - return Types::isSubTypeUnderConstraint(gs, constr, t1, lambdaParam->lowerBound, mode); + return Types::isSubTypeUnderConstraint(gs, constr, t1, lambdaParam->lowerBound, mode, + errorDetailsCollector); } else { return false; } } else if (!isSelfTypeT2) { auto self1 = cast_type_nonnull(t1); if (auto *lambdaParam = cast_type(self1.definition.resultType(gs))) { - return Types::isSubTypeUnderConstraint(gs, constr, lambdaParam->upperBound, t2, mode); + return Types::isSubTypeUnderConstraint(gs, constr, lambdaParam->upperBound, t2, mode, + errorDetailsCollector); } else { return false; } @@ -1168,7 +1181,8 @@ bool isSubTypeUnderConstraintSingle(const GlobalState &gs, TypeConstraint &const auto *lambda1 = cast_type(self1.definition.resultType(gs)); auto *lambda2 = cast_type(self2.definition.resultType(gs)); return lambda1 && lambda2 && - Types::isSubTypeUnderConstraint(gs, constr, lambda1->upperBound, lambda2->lowerBound, mode); + Types::isSubTypeUnderConstraint(gs, constr, lambda1->upperBound, lambda2->lowerBound, mode, + errorDetailsCollector); } } } @@ -1189,57 +1203,96 @@ bool isSubTypeUnderConstraintSingle(const GlobalState &gs, TypeConstraint &const if (a2 == nullptr) { if (isa_type(t2)) { auto c2 = cast_type_nonnull(t2); - return classSymbolIsAsGoodAs(gs, a1->klass, c2.symbol); + result = classSymbolIsAsGoodAs(gs, a1->klass, c2.symbol); + if constexpr (shouldAddErrorDetails) { + if (!result) { + doesNotDeriveFrom(gs, errorDetailsCollector, a1->klass, c2.symbol); + } + } + return result; } return false; } else { result = classSymbolIsAsGoodAs(gs, a1->klass, a2->klass); } - if (result) { - InlinedVector indexes = Types::alignBaseTypeArgs(gs, a1->klass, a1->targs, a2->klass); - // code below inverts permutation of type params - int j = 0; - for (SymbolRef idx : a2->klass.data(gs)->typeMembers()) { - TypeMemberRef idxTypeMember = idx.asTypeMemberRef(); - int i = 0; - while (indexes[j] != a1->klass.data(gs)->typeMembers()[i]) { - i++; - if (i >= a1->klass.data(gs)->typeMembers().size()) { - return result; - } + if (!result) { + if constexpr (shouldAddErrorDetails) { + doesNotDeriveFrom(gs, errorDetailsCollector, a1->klass, a2->klass); + } + return result; + } + InlinedVector indexes = Types::alignBaseTypeArgs(gs, a1->klass, a1->targs, a2->klass); + // code below inverts permutation of type params + int j = 0; + for (SymbolRef idx : a2->klass.data(gs)->typeMembers()) { + TypeMemberRef idxTypeMember = idx.asTypeMemberRef(); + int i = 0; + while (indexes[j] != a1->klass.data(gs)->typeMembers()[i]) { + i++; + if (i >= a1->klass.data(gs)->typeMembers().size()) { + return result; } + } - ENFORCE(i < a1->klass.data(gs)->typeMembers().size()); + ENFORCE(i < a1->klass.data(gs)->typeMembers().size()); - if (idxTypeMember.data(gs)->flags.isCovariant) { - result = Types::isSubTypeUnderConstraint(gs, constr, a1->targs[i], a2->targs[j], mode); - } else if (idxTypeMember.data(gs)->flags.isInvariant) { - auto &a = a1->targs[i]; - auto &b = a2->targs[j]; - if (mode == UntypedMode::AlwaysCompatible) { - result = Types::equivUnderConstraint(gs, constr, a, b); - } else { - // At the time of writing, we never set mode == UntypedMode::AlwaysIncompatible - // except when `constr` is EmptyFrozenConstraint, so there's no observable - // difference whether we use equivNoUntyped or equivNoUntypedUnderConstraint here. - // May as well do it for symmetry though. - result = Types::equivNoUntypedUnderConstraint(gs, constr, a, b); - } - } else if (idxTypeMember.data(gs)->flags.isContravariant) { - result = Types::isSubTypeUnderConstraint(gs, constr, a2->targs[j], a1->targs[i], mode); + auto &a1i = a1->targs[i]; + auto &a2j = a2->targs[j]; + bool doesMemberMatch = true; + auto subCollector = errorDetailsCollector.newCollector(); + if (idxTypeMember.data(gs)->flags.isCovariant) { + doesMemberMatch = Types::isSubTypeUnderConstraint(gs, constr, a1i, a2j, mode, subCollector); + } else if (idxTypeMember.data(gs)->flags.isInvariant) { + if (mode == UntypedMode::AlwaysCompatible) { + doesMemberMatch = Types::equivUnderConstraint(gs, constr, a1i, a2j, subCollector); + } else { + // At the time of writing, we never set mode == UntypedMode::AlwaysIncompatible + // except when `constr` is EmptyFrozenConstraint, so there's no observable + // difference whether we use equivNoUntyped or equivNoUntypedUnderConstraint here. + // May as well do it for symmetry though. + doesMemberMatch = Types::equivNoUntypedUnderConstraint(gs, constr, a1i, a2j, subCollector); } - if (!result) { + } else if (idxTypeMember.data(gs)->flags.isContravariant) { + doesMemberMatch = Types::isSubTypeUnderConstraint(gs, constr, a2j, a1i, mode, subCollector); + } + if (!doesMemberMatch) { + result = false; + if constexpr (shouldAddErrorDetails) { + string variance; + string joiningText; + switch (idxTypeMember.data(gs)->variance()) { + case Variance::CoVariant: { + variance = "covariant"; + joiningText = "a subtype of"; + break; + } + case Variance::Invariant: { + variance = "invariant"; + joiningText = "equivalent to"; + break; + } + case Variance::ContraVariant: { + variance = "contravariant"; + joiningText = "a supertype of"; + break; + } + } + auto message = ErrorColors::format("`{}` is not {} `{}` for {} type member `{}`", a1i.show(gs), + joiningText, a2j.show(gs), variance, idxTypeMember.show(gs)); + subCollector.message = message; + errorDetailsCollector.addErrorDetails(std::move(subCollector)); + } else { break; } - j++; } - // alight type params. + j++; } + // alight type params. return result; } if (isa_type(t2)) { if (is_proxy_type(t1)) { - return Types::isSubTypeUnderConstraint(gs, constr, t1.underlying(gs), t2, mode); + return Types::isSubTypeUnderConstraint(gs, constr, t1.underlying(gs), t2, mode, errorDetailsCollector); } return false; } @@ -1257,32 +1310,62 @@ bool isSubTypeUnderConstraintSingle(const GlobalState &gs, TypeConstraint &const int i = -1; for (auto &el2 : a2->elems) { ++i; - result = Types::isSubTypeUnderConstraint(gs, constr, a1.elems[i], el2, mode); - if (!result) { - break; + auto subCollector = errorDetailsCollector.newCollector(); + if (!Types::isSubTypeUnderConstraint(gs, constr, a1.elems[i], el2, mode, subCollector)) { + result = false; + if constexpr (shouldAddErrorDetails) { + auto message = ErrorColors::format( + "`{}` is not a subtype of `{}` for index `{}` of `{}`-tuple", + a1.elems[i].show(gs), el2.show(gs), i, a2->elems.size()); + subCollector.message = message; + errorDetailsCollector.addErrorDetails(std::move(subCollector)); + } else { + break; + } } } + } else { + return; } }, [&](const ShapeType &h1) { // Warning: this implements COVARIANT hashes auto *h2 = cast_type(t2); result = h2 != nullptr && h2->keys.size() <= h1.keys.size(); - if (!result) { - return; + if constexpr (shouldAddErrorDetails) { + if (h2 == nullptr) { + return; + } + // If we're using this subtyping call to report an error, we should loop through all the items + // even if there aren't enough keys, so we can report all the missing keys to the user, and + // report an error for the incorrect keys. + } else { + if (!result) { + return; + } } - // have enough keys. + // have enough keys (or we want to keep going for rich error reporting). int i = -1; for (auto &el2 : h2->keys) { ++i; auto optind = h1.indexForKey(el2); + auto subCollector = errorDetailsCollector.newCollector(); if (!optind.has_value()) { result = false; - return; - } - if (!Types::isSubTypeUnderConstraint(gs, constr, h1.values[optind.value()], h2->values[i], - mode)) { + if constexpr (!shouldAddErrorDetails) { + return; + } + } else if (!Types::isSubTypeUnderConstraint(gs, constr, h1.values[optind.value()], + h2->values[i], mode, subCollector)) { result = false; - return; + if constexpr (shouldAddErrorDetails) { + auto message = ErrorColors::format("`{}` is not a subtype of `{}` for key `{}`", + h1.values[i].show(gs), + h2->values[optind.value()].show(gs), el2.show(gs)); + subCollector.message = message; + errorDetailsCollector.addErrorDetails(std::move(subCollector)); + } else { + return; + } } } }, @@ -1324,14 +1407,18 @@ bool isSubTypeUnderConstraintSingle(const GlobalState &gs, TypeConstraint &const return; } - result = Types::equiv(gs, m1.wrapped, m2->wrapped); + // TODO(jez) Should this actually run under EmptyFrozenConstraint? Leaving for + // backwards compatibility, but maybe we should do this under the `constr` + // that's in scope. + result = Types::equivUnderConstraint(gs, TypeConstraint::EmptyFrozenConstraint, m1.wrapped, + m2->wrapped, errorDetailsCollector); }); return result; // both are proxy } else { // only 1st is proxy TypePtr und = t1.underlying(gs); - return isSubTypeUnderConstraintSingle(gs, constr, mode, und, t2); + return isSubTypeUnderConstraintSingle(gs, constr, mode, und, t2, errorDetailsCollector); } } else if (is_proxy_type(t2)) { // non-proxies are never subtypes of proxies. @@ -1349,15 +1436,21 @@ bool isSubTypeUnderConstraintSingle(const GlobalState &gs, TypeConstraint &const } bool Types::isAsSpecificAs(const GlobalState &gs, const TypePtr &t1, const TypePtr &t2) { - return isSubTypeUnderConstraint(gs, TypeConstraint::EmptyFrozenConstraint, t1, t2, UntypedMode::AlwaysIncompatible); + return isSubTypeUnderConstraint(gs, TypeConstraint::EmptyFrozenConstraint, t1, t2, UntypedMode::AlwaysIncompatible, + ErrorSection::Collector::NO_OP); } -bool Types::isSubType(const GlobalState &gs, const TypePtr &t1, const TypePtr &t2) { - return isSubTypeUnderConstraint(gs, TypeConstraint::EmptyFrozenConstraint, t1, t2, UntypedMode::AlwaysCompatible); +template +bool Types::isSubType(const GlobalState &gs, const TypePtr &t1, const TypePtr &t2, T &errorDetailsCollector) { + return isSubTypeUnderConstraint(gs, TypeConstraint::EmptyFrozenConstraint, t1, t2, UntypedMode::AlwaysCompatible, + errorDetailsCollector); } +template bool Types::isSubTypeUnderConstraint(const GlobalState &gs, TypeConstraint &constr, const TypePtr &t1, - const TypePtr &t2, UntypedMode mode) { + const TypePtr &t2, UntypedMode mode, T &errorDetailsCollector) { + constexpr auto shouldAddErrorDetails = std::is_same::value; + if (t1 == t2) { return true; } @@ -1376,13 +1469,34 @@ bool Types::isSubTypeUnderConstraint(const GlobalState &gs, TypeConstraint &cons // Note: order of cases here matters! We can't lose "and" information in t1 early and we can't // lose "or" information in t2 early. if (auto *o1 = cast_type(t1)) { // 7, 8, 9 - return Types::isSubTypeUnderConstraint(gs, constr, o1->left, t2, mode) && - Types::isSubTypeUnderConstraint(gs, constr, o1->right, t2, mode); + return Types::isSubTypeUnderConstraint(gs, constr, o1->left, t2, mode, errorDetailsCollector) && + Types::isSubTypeUnderConstraint(gs, constr, o1->right, t2, mode, errorDetailsCollector); } if (auto *a2 = cast_type(t2)) { // 2, 5 - return Types::isSubTypeUnderConstraint(gs, constr, t1, a2->left, mode) && - Types::isSubTypeUnderConstraint(gs, constr, t1, a2->right, mode); + auto subCollectorLeft = errorDetailsCollector.newCollector(); + auto isSubTypeOfLeft = Types::isSubTypeUnderConstraint(gs, constr, t1, a2->left, mode, subCollectorLeft); + if (!isSubTypeOfLeft) { + if constexpr (shouldAddErrorDetails) { + auto message = ErrorColors::format("`{}` is not a subtype of `{}` (the left side of the `{}`)", + t1.show(gs), a2->left.show(gs), "T.all"); + subCollectorLeft.message = message; + errorDetailsCollector.addErrorDetails(move(subCollectorLeft)); + } + return isSubTypeOfLeft; + } + + auto subCollectorRight = errorDetailsCollector.newCollector(); + auto isSubTypeOfRight = Types::isSubTypeUnderConstraint(gs, constr, t1, a2->right, mode, subCollectorRight); + if constexpr (shouldAddErrorDetails) { + if (!isSubTypeOfRight) { + auto message = ErrorColors::format("`{}` is not a subtype of `{}` (the right side of the `{}`)", + t1.show(gs), a2->right.show(gs), "T.all"); + subCollectorRight.message = message; + errorDetailsCollector.addErrorDetails(move(subCollectorRight)); + } + } + return isSubTypeOfRight; } auto *a1 = cast_type(t1); @@ -1400,10 +1514,12 @@ bool Types::isSubTypeUnderConstraint(const GlobalState &gs, TypeConstraint &cons if (a1o != nullptr) { // This handles `(A | B) & C` -> `(A & C) | (B & C)` - // this could be using glb, but we _know_ that we alredy tried to collapse it (prior + // this could be using glb, but we _know_ that we already tried to collapse it (prior // construction of types did). Thus we use AndType::make_shared instead - return Types::isSubTypeUnderConstraint(gs, constr, AndType::make_shared(a1o->left, *r), t2, mode) && - Types::isSubTypeUnderConstraint(gs, constr, AndType::make_shared(a1o->right, *r), t2, mode); + return Types::isSubTypeUnderConstraint(gs, constr, AndType::make_shared(a1o->left, *r), t2, mode, + errorDetailsCollector) && + Types::isSubTypeUnderConstraint(gs, constr, AndType::make_shared(a1o->right, *r), t2, mode, + errorDetailsCollector); } } if (o2 != nullptr) { @@ -1418,10 +1534,12 @@ bool Types::isSubTypeUnderConstraint(const GlobalState &gs, TypeConstraint &cons if (o2a != nullptr) { // This handles `(A & B) | C` -> `(A | C) & (B | C)` - // this could be using lub, but we _know_ that we alredy tried to collapse it (prior + // this could be using lub, but we _know_ that we already tried to collapse it (prior // construction of types did). Thus we use OrType::make_shared instead - return Types::isSubTypeUnderConstraint(gs, constr, t1, OrType::make_shared(o2a->left, *r), mode) && - Types::isSubTypeUnderConstraint(gs, constr, t1, OrType::make_shared(o2a->right, *r), mode); + return Types::isSubTypeUnderConstraint(gs, constr, t1, OrType::make_shared(o2a->left, *r), mode, + errorDetailsCollector) && + Types::isSubTypeUnderConstraint(gs, constr, t1, OrType::make_shared(o2a->right, *r), mode, + errorDetailsCollector); } } @@ -1448,12 +1566,12 @@ bool Types::isSubTypeUnderConstraint(const GlobalState &gs, TypeConstraint &cons // // Concretely, it's still possible to come up with cases where this heuristic isn't good enough. // For more, see the comment in `no_short_circuit_type_constraint.rb` - auto leftIsSubType = Types::isSubTypeUnderConstraint(gs, constr, t1, o2->left, mode); + auto leftIsSubType = Types::isSubTypeUnderConstraint(gs, constr, t1, o2->left, mode, errorDetailsCollector); auto stillNeedToCheckRight = t1.isUntyped() && o2->left.isFullyDefined() && !o2->right.isFullyDefined(); if (leftIsSubType && !stillNeedToCheckRight) { // Short circuit to save time return true; - } else if (Types::isSubTypeUnderConstraint(gs, constr, t1, o2->right, mode)) { + } else if (Types::isSubTypeUnderConstraint(gs, constr, t1, o2->right, mode, errorDetailsCollector)) { return true; } else if (a1 == nullptr) { // If neither t1 <: o2->left nor t1 <: o2->right, it might mean that we tried to split @@ -1470,36 +1588,88 @@ bool Types::isSubTypeUnderConstraint(const GlobalState &gs, TypeConstraint &cons return constr.rememberIsSubtype(gs, t1, t2); } // See explanation in "// 3" - auto leftIsSubType = Types::isSubTypeUnderConstraint(gs, constr, a1->left, t2, mode); + auto leftIsSubType = Types::isSubTypeUnderConstraint(gs, constr, a1->left, t2, mode, errorDetailsCollector); auto stillNeedToCheckRight = t2.isUntyped() && a1->left.isFullyDefined() && !a1->right.isFullyDefined(); if (leftIsSubType && !stillNeedToCheckRight) { // Short circuit to save time return true; } else { - return Types::isSubTypeUnderConstraint(gs, constr, a1->right, t2, mode); + return Types::isSubTypeUnderConstraint(gs, constr, a1->right, t2, mode, errorDetailsCollector); } } - return isSubTypeUnderConstraintSingle(gs, constr, mode, t1, t2); // 1 + return isSubTypeUnderConstraintSingle(gs, constr, mode, t1, t2, errorDetailsCollector); // 1 } bool Types::equiv(const GlobalState &gs, const TypePtr &t1, const TypePtr &t2) { return isSubType(gs, t1, t2) && isSubType(gs, t2, t1); } -bool Types::equivUnderConstraint(const GlobalState &gs, TypeConstraint &constr, const TypePtr &t1, const TypePtr &t2) { +template +bool Types::equivUnderConstraint(const GlobalState &gs, TypeConstraint &constr, const TypePtr &t1, const TypePtr &t2, + T &errorDetailsCollector) { auto mode = UntypedMode::AlwaysCompatible; - return isSubTypeUnderConstraint(gs, constr, t1, t2, mode) && isSubTypeUnderConstraint(gs, constr, t2, t1, mode); + auto leftSubRight = isSubTypeUnderConstraint(gs, constr, t1, t2, mode, errorDetailsCollector); + if (!leftSubRight) { + return leftSubRight; + } + + auto subCollector = errorDetailsCollector.newCollector(); + auto rightSubLeft = isSubTypeUnderConstraint(gs, constr, t2, t1, mode, subCollector); + if constexpr (std::is_same::value) { + if (!rightSubLeft) { + auto message = ErrorColors::format( + "`{}` is a subtype of `{}` but not the reverse, so they are not equivalent", t1.show(gs), t2.show(gs)); + subCollector.message = message; + errorDetailsCollector.addErrorDetails(move(subCollector)); + } + } + + return rightSubLeft; } bool Types::equivNoUntyped(const GlobalState &gs, const TypePtr &t1, const TypePtr &t2) { return isAsSpecificAs(gs, t1, t2) && isAsSpecificAs(gs, t2, t1); } +template bool Types::equivNoUntypedUnderConstraint(const GlobalState &gs, TypeConstraint &constr, const TypePtr &t1, - const TypePtr &t2) { + const TypePtr &t2, T &errorDetailsCollector) { auto mode = UntypedMode::AlwaysIncompatible; - return isSubTypeUnderConstraint(gs, constr, t1, t2, mode) && isSubTypeUnderConstraint(gs, constr, t2, t1, mode); + return isSubTypeUnderConstraint(gs, constr, t1, t2, mode, errorDetailsCollector) && + isSubTypeUnderConstraint(gs, constr, t2, t1, mode, errorDetailsCollector); } +template bool isSubTypeUnderConstraintSingle(const GlobalState &gs, TypeConstraint &constr, UntypedMode mode, + const TypePtr &t1, const TypePtr &t2, + core::ErrorSection::Collector &errorDetailsCollector); +template bool isSubTypeUnderConstraintSingle(const GlobalState &gs, TypeConstraint &constr, UntypedMode mode, + const TypePtr &t1, const TypePtr &t2, + core::ErrorSection::NoOpCollector const &errorDetailsCollector); + +template bool Types::isSubTypeUnderConstraint(const GlobalState &gs, TypeConstraint &constr, const TypePtr &t1, + const TypePtr &t2, UntypedMode mode, + core::ErrorSection::Collector &errorDetailsCollector); +template bool Types::isSubTypeUnderConstraint(const GlobalState &gs, TypeConstraint &constr, const TypePtr &t1, + const TypePtr &t2, UntypedMode mode, + core::ErrorSection::NoOpCollector const &errorDetailsCollector); + +template bool Types::isSubType(const GlobalState &gs, const TypePtr &t1, const TypePtr &t2, + core::ErrorSection::Collector &errorDetailsCollector); +template bool Types::isSubType(const GlobalState &gs, const TypePtr &t1, const TypePtr &t2, + core::ErrorSection::NoOpCollector const &errorDetailsCollector); + +template bool Types::equivUnderConstraint(const GlobalState &gs, TypeConstraint &constr, const TypePtr &t1, + const TypePtr &t2, core::ErrorSection::Collector &errorDetailsCollector); +template bool Types::equivUnderConstraint(const GlobalState &gs, TypeConstraint &constr, const TypePtr &t1, + const TypePtr &t2, + core::ErrorSection::NoOpCollector const &errorDetailsCollector); + +template bool Types::equivNoUntypedUnderConstraint(const GlobalState &gs, TypeConstraint &constr, const TypePtr &t1, + const TypePtr &t2, + core::ErrorSection::Collector &errorDetailsCollector); +template bool Types::equivNoUntypedUnderConstraint(const GlobalState &gs, TypeConstraint &constr, const TypePtr &t1, + const TypePtr &t2, + core::ErrorSection::NoOpCollector const &errorDetailsCollector); + } // namespace sorbet::core diff --git a/core/types/typemaps.cc b/core/types/typemaps.cc index fac1a56320..1dbfc4ee67 100644 --- a/core/types/typemaps.cc +++ b/core/types/typemaps.cc @@ -54,14 +54,22 @@ TypePtr TypeVar::_approximate(const GlobalState &gs, const TypeConstraint &tc, c if (bound.isFullyDefined()) { return bound; } + } else if (tc.hasLowerBound(sym)) { + auto bound = tc.findLowerBound(sym); + if (bound.isFullyDefined() && !bound.isBottom()) { + return bound; + } } break; } case core::Polarity::Negative: { if (tc.hasLowerBound(sym)) { auto bound = tc.findLowerBound(sym); - // TODO(jez) Need to check for isBottom? - // if (bound.isFullyDefined() && !bound.isBottom()) { + if (bound.isFullyDefined()) { + return bound; + } + } else if (tc.hasUpperBound(sym)) { + auto bound = tc.findUpperBound(sym); if (bound.isFullyDefined()) { return bound; } diff --git a/core/types/types.cc b/core/types/types.cc index b12dfdecbf..156168ca6b 100644 --- a/core/types/types.cc +++ b/core/types/types.cc @@ -2,12 +2,14 @@ #include "absl/base/casts.h" #include "absl/strings/match.h" #include "common/common.h" +#include "common/strings/formatting.h" #include "common/typecase.h" #include "core/Context.h" #include "core/GlobalState.h" #include "core/Names.h" #include "core/Symbols.h" #include "core/TypeConstraint.h" +#include "core/TypeErrorDiagnostics.h" #include "core/errors/infer.h" #include "core/errors/resolver.h" #include @@ -77,15 +79,27 @@ TypePtr Types::Float() { } TypePtr Types::arrayOfUntyped(sorbet::core::SymbolRef blame) { - static vector targs{Types::untyped(blame)}; - static auto res = make_type(Symbols::Array(), move(targs)); - return res; + if constexpr (!sorbet::track_untyped_blame_mode && !sorbet::debug_mode) { + static vector targs{Types::untypedUntracked()}; + static auto res = make_type(Symbols::Array(), move(targs)); + return res; + } else { + vector targs{Types::untyped(blame)}; + auto res = make_type(Symbols::Array(), move(targs)); + return res; + } } TypePtr Types::rangeOfUntyped(sorbet::core::SymbolRef blame) { - static vector targs{Types::untyped(blame)}; - static auto res = make_type(Symbols::Range(), move(targs)); - return res; + if constexpr (!sorbet::track_untyped_blame_mode && !sorbet::debug_mode) { + static vector targs{Types::untypedUntracked()}; + static auto res = make_type(Symbols::Range(), move(targs)); + return res; + } else { + vector targs{Types::untyped(blame)}; + auto res = make_type(Symbols::Range(), move(targs)); + return res; + } } TypePtr Types::hashOfUntyped() { @@ -95,10 +109,14 @@ TypePtr Types::hashOfUntyped() { } TypePtr Types::hashOfUntyped(sorbet::core::SymbolRef blame) { - auto untypedWithBlame = Types::untyped(blame); - static vector targs{untypedWithBlame, untypedWithBlame, untypedWithBlame}; - static auto res = make_type(Symbols::Hash(), move(targs)); - return res; + if constexpr (!sorbet::track_untyped_blame_mode && !sorbet::debug_mode) { + return Types::hashOfUntyped(); + } else { + auto untypedWithBlame = Types::untyped(blame); + vector targs{untypedWithBlame, untypedWithBlame, untypedWithBlame}; + auto res = make_type(Symbols::Hash(), move(targs)); + return res; + } } TypePtr Types::procClass() { @@ -131,11 +149,16 @@ TypePtr Types::falsyTypes() { return res; } +absl::Span Types::falsySymbols() { + static InlinedVector res{Symbols::NilClass(), Symbols::FalseClass()}; + return res; +} + TypePtr Types::todo() { return make_type(Symbols::todo()); } -TypePtr Types::dropSubtypesOf(const GlobalState &gs, const TypePtr &from, ClassOrModuleRef klass) { +TypePtr Types::dropSubtypesOf(const GlobalState &gs, const TypePtr &from, absl::Span klasses) { TypePtr result; if (from.isUntyped()) { @@ -145,8 +168,8 @@ TypePtr Types::dropSubtypesOf(const GlobalState &gs, const TypePtr &from, ClassO typecase( from, [&](const OrType &o) { - auto lhs = dropSubtypesOf(gs, o.left, klass); - auto rhs = dropSubtypesOf(gs, o.right, klass); + auto lhs = dropSubtypesOf(gs, o.left, klasses); + auto rhs = dropSubtypesOf(gs, o.right, klasses); if (lhs == o.left && rhs == o.right) { result = from; } else if (lhs.isBottom()) { @@ -158,8 +181,8 @@ TypePtr Types::dropSubtypesOf(const GlobalState &gs, const TypePtr &from, ClassO } }, [&](const AndType &a) { - auto lhs = dropSubtypesOf(gs, a.left, klass); - auto rhs = dropSubtypesOf(gs, a.right, klass); + auto lhs = dropSubtypesOf(gs, a.left, klasses); + auto rhs = dropSubtypesOf(gs, a.right, klasses); if (lhs != a.left || rhs != a.right) { result = Types::all(gs, lhs, rhs); } else { @@ -170,51 +193,52 @@ TypePtr Types::dropSubtypesOf(const GlobalState &gs, const TypePtr &from, ClassO auto cdata = c.symbol.data(gs); if (c.symbol == core::Symbols::untyped()) { result = from; - } else if (c.symbol == klass || c.derivesFrom(gs, klass)) { + } else if (absl::c_any_of(klasses, + [&](auto klass) { return c.symbol == klass || c.derivesFrom(gs, klass); })) { result = Types::bottom(); - } else if (c.symbol.data(gs)->isClass() && klass.data(gs)->isClass() && - !klass.data(gs)->derivesFrom(gs, c.symbol)) { - // We have two classes (not modules), and if the class we're - // removing doesn't derive from `c`, there's nothing to do, - // because of ruby having single inheretance. + } else if (c.symbol.data(gs)->isClass() && absl::c_all_of(klasses, [&](auto klass) { + return klass.data(gs)->isClass() && !klass.data(gs)->derivesFrom(gs, c.symbol); + })) { + // We have two classes (not modules), and if all of the the classes we're removing + // don't derive from `c`, there's nothing to do because Ruby has single inheritance. result = from; } else if (cdata->flags.isSealed && (cdata->flags.isAbstract || cdata->isModule())) { auto subclasses = cdata->sealedSubclassesToUnion(gs); - ENFORCE(!Types::equiv(gs, subclasses, from), "sealedSubclassesToUnion about to cause infinte loop"); - result = dropSubtypesOf(gs, subclasses, klass); + ENFORCE(!Types::equiv(gs, subclasses, from), "sealedSubclassesToUnion about to cause infinite loop"); + result = dropSubtypesOf(gs, subclasses, klasses); } else { result = from; } }, [&](const AppliedType &a) { auto adata = a.klass.data(gs); - if (a.klass == klass || a.derivesFrom(gs, klass)) { + if (absl::c_any_of(klasses, [&](auto klass) { return a.klass == klass || a.derivesFrom(gs, klass); })) { result = Types::bottom(); - } else if (a.klass.data(gs)->isClass() && klass.data(gs)->isClass() && - !klass.data(gs)->derivesFrom(gs, a.klass)) { - // We have two classes (not modules), and if the class we're - // removing doesn't derive from `a`, there's nothing to do, - // because of ruby having single inheretance. + } else if (a.klass.data(gs)->isClass() && absl::c_all_of(klasses, [&](auto klass) { + return klass.data(gs)->isClass() && !klass.data(gs)->derivesFrom(gs, a.klass); + })) { + // We have two classes (not modules), and if all of the the classes we're removing + // don't derive from `c`, there's nothing to do because Ruby has single inheritance. result = from; } else if (adata->flags.isSealed && (adata->flags.isAbstract || adata->isModule())) { auto subclasses = adata->sealedSubclassesToUnion(gs); - ENFORCE(!Types::equiv(gs, subclasses, from), "sealedSubclassesToUnion about to cause infinte loop"); - result = dropSubtypesOf(gs, subclasses, klass); + ENFORCE(!Types::equiv(gs, subclasses, from), "sealedSubclassesToUnion about to cause infinite loop"); + result = dropSubtypesOf(gs, subclasses, klasses); result = Types::all(gs, from, result); } else { result = from; } }, [&](const TypePtr &) { - if (is_proxy_type(from) && dropSubtypesOf(gs, from.underlying(gs), klass).isBottom()) { + if (is_proxy_type(from) && dropSubtypesOf(gs, from.underlying(gs), klasses).isBottom()) { result = Types::bottom(); } else { result = from; } }); SLOW_ENFORCE(Types::isSubType(gs, result, from), - "dropSubtypesOf({}, {}) returned {}, which is not a subtype of the input", from.toString(gs), - klass.showFullName(gs), result.toString(gs)); + "dropSubtypesOf({}, [{}]) returned {}, which is not a subtype of the input", from.toString(gs), + fmt::map_join(klasses, ", ", [&](auto klass) { return klass.showFullName(gs); }), result.toString(gs)); return result; } @@ -249,15 +273,14 @@ bool Types::canBeFalsy(const GlobalState &gs, const TypePtr &what) { return true; } return Types::isSubType(gs, Types::falseClass(), what) || - Types::isSubType(gs, Types::nilClass(), - what); // check if inhabited by falsy values + Types::isSubType(gs, Types::nilClass(), what); // check if inhabited by falsy values } TypePtr Types::approximateSubtract(const GlobalState &gs, const TypePtr &from, const TypePtr &what) { TypePtr result; typecase( - what, [&](const ClassType &c) { result = Types::dropSubtypesOf(gs, from, c.symbol); }, - [&](const AppliedType &c) { result = Types::dropSubtypesOf(gs, from, c.klass); }, + what, [&](const ClassType &c) { result = Types::dropSubtypesOf(gs, from, absl::MakeSpan(&c.symbol, 1)); }, + [&](const AppliedType &c) { result = Types::dropSubtypesOf(gs, from, absl::MakeSpan(&c.klass, 1)); }, [&](const OrType &o) { result = Types::approximateSubtract(gs, Types::approximateSubtract(gs, from, o.left), o.right); }, @@ -317,7 +340,9 @@ TypePtr Types::tClass(const TypePtr &attachedClass) { } TypePtr Types::dropNil(const GlobalState &gs, const TypePtr &from) { - return Types::dropSubtypesOf(gs, from, Symbols::NilClass()); + static auto nilClass = core::Symbols::NilClass(); + static auto toDrop = absl::MakeSpan(&nilClass, 1); + return Types::dropSubtypesOf(gs, from, toDrop); } std::optional Types::getProcArity(const AppliedType &type) { @@ -604,14 +629,12 @@ InlinedVector Types::alignBaseTypeArgs(const GlobalState &gs, for (auto originalTp : asIf.data(gs)->typeMembers()) { auto name = originalTp.data(gs)->name; SymbolRef align; - int i = 0; for (auto x : what.data(gs)->typeMembers()) { if (x.data(gs)->name == name) { align = x; currentAlignment.emplace_back(x); break; } - i++; } if (!align.exists()) { currentAlignment.emplace_back(Symbols::noTypeMember()); @@ -957,13 +980,12 @@ TypePtr Types::unwrapType(const GlobalState &gs, Loc loc, const TypePtr &tp) { unwrappedElems.emplace_back(unwrapType(gs, loc, elem)); } return make_type(move(unwrappedElems)); - } else if (isa_type(tp) || isa_type(tp) || isa_type(tp)) { - if (auto e = gs.beginError(loc, errors::Infer::BareTypeUsage)) { - e.setHeader("Unexpected bare `{}` value found in type position", tp.show(gs)); - } - return Types::untypedUntracked(); } - return tp; + + if (auto e = gs.beginError(loc, errors::Infer::BareTypeUsage)) { + e.setHeader("Unexpected bare `{}` value found in type position", tp.show(gs)); + } + return Types::untypedUntracked(); } // This method is actually special: not only is it called from dispatchCall in calls.cc, it's @@ -1057,7 +1079,8 @@ TypePtr Types::applyTypeArguments(const GlobalState &gs, const CallLocs &locs, u bool validBounds = true; // Validate type parameter bounds. - if (!Types::isSubType(gs, argType, memType->upperBound)) { + ErrorSection::Collector errorDetailsCollector; + if (!Types::isSubType(gs, argType, memType->upperBound, errorDetailsCollector)) { validBounds = false; if (auto e = gs.beginError(loc, errors::Resolver::GenericTypeParamBoundMismatch)) { auto argStr = argType.show(gs); @@ -1065,10 +1088,13 @@ TypePtr Types::applyTypeArguments(const GlobalState &gs, const CallLocs &locs, u mem.showFullName(gs)); e.addErrorLine(memData->loc(), "`{}` is `{}` bounded by `{}` here", mem.showFullName(gs), "upper", memType->upperBound.show(gs)); + TypeErrorDiagnostics::explainTypeMismatch(gs, e, errorDetailsCollector, memType->upperBound, + argType); } } - if (!Types::isSubType(gs, memType->lowerBound, argType)) { + ErrorSection::Collector errorDetailsCollector2; + if (!Types::isSubType(gs, memType->lowerBound, argType, errorDetailsCollector2)) { validBounds = false; if (auto e = gs.beginError(loc, errors::Resolver::GenericTypeParamBoundMismatch)) { @@ -1077,6 +1103,8 @@ TypePtr Types::applyTypeArguments(const GlobalState &gs, const CallLocs &locs, u mem.showFullName(gs)); e.addErrorLine(memData->loc(), "`{}` is `{}` bounded by `{}` here", mem.showFullName(gs), "lower", memType->lowerBound.show(gs)); + TypeErrorDiagnostics::explainTypeMismatch(gs, e, errorDetailsCollector2, memType->lowerBound, + argType); } } @@ -1116,20 +1144,24 @@ Loc DispatchArgs::blockLoc(const GlobalState &gs) const { } DispatchArgs DispatchArgs::withSelfAndThisRef(const TypePtr &newSelfRef) const { - return DispatchArgs{ - name, locs, numPosArgs, args, newSelfRef, fullType, newSelfRef, block, originForUninitialized, - isPrivateOk, suppressErrors}; + return DispatchArgs{name, locs, numPosArgs, + args, newSelfRef, fullType, + newSelfRef, block, originForUninitialized, + isPrivateOk, suppressErrors, enclosingMethodForSuper}; } DispatchArgs DispatchArgs::withThisRef(const TypePtr &newThisRef) const { - return DispatchArgs{ - name, locs, numPosArgs, args, selfType, fullType, newThisRef, block, originForUninitialized, - isPrivateOk, suppressErrors}; + return DispatchArgs{name, locs, numPosArgs, + args, selfType, fullType, + newThisRef, block, originForUninitialized, + isPrivateOk, suppressErrors, enclosingMethodForSuper}; } DispatchArgs DispatchArgs::withErrorsSuppressed() const { - return DispatchArgs{ - name, locs, numPosArgs, args, selfType, fullType, thisType, block, originForUninitialized, isPrivateOk, true}; + return DispatchArgs{name, locs, numPosArgs, + args, selfType, fullType, + thisType, block, originForUninitialized, + isPrivateOk, true, enclosingMethodForSuper}; } DispatchResult DispatchResult::merge(const GlobalState &gs, DispatchResult::Combinator kind, DispatchResult &&left, diff --git a/definition_validator/BUILD b/definition_validator/BUILD index b36edd33f4..eb5128e8f1 100644 --- a/definition_validator/BUILD +++ b/definition_validator/BUILD @@ -24,6 +24,7 @@ cc_library( "//ast/treemap", "//core", "//core/sig_finder", + "//core/source_generator", "@com_google_absl//absl/strings", ], ) diff --git a/definition_validator/validator.cc b/definition_validator/validator.cc index e6810a462c..777845ec61 100644 --- a/definition_validator/validator.cc +++ b/definition_validator/validator.cc @@ -1,15 +1,21 @@ #include "absl/strings/ascii.h" #include "absl/strings/match.h" +#include "absl/strings/str_join.h" +#include "absl/strings/str_split.h" #include "ast/ast.h" #include "ast/treemap/treemap.h" +#include "common/sort/sort.h" #include "common/timers/Timer.h" +#include "core/TypeErrorDiagnostics.h" #include "core/core.h" #include "core/errors/resolver.h" +#include "core/source_generator/source_generator.h" #include "absl/algorithm/container.h" #include "core/sig_finder/sig_finder.h" #include "definition_validator/variance.h" +#include using namespace std; @@ -47,11 +53,11 @@ Signature decomposeSignature(const core::GlobalState &gs, core::MethodRef method // This is really just a useful helper function for this module: do not use it elsewhere. // -// It makes certain assuptions that it is running for the sake of computing overrides that are not +// It makes certain assumptions that it is running for the sake of computing overrides that are not // going to be true in other situations. bool checkSubtype(const core::Context ctx, core::TypeConstraint &constr, const core::TypePtr &sub, core::MethodRef subMethod, const core::TypePtr &super, core::MethodRef superMethod, - core::Polarity polarity) { + core::Polarity polarity, core::ErrorSection::Collector &errorDetailsCollector) { if (sub == nullptr || super == nullptr) { // nullptr is just "unannotated" which is T.untyped return true; @@ -91,16 +97,16 @@ bool checkSubtype(const core::Context ctx, core::TypeConstraint &constr, const c switch (polarity) { case core::Polarity::Negative: - return core::Types::isSubType(ctx, superType, subType); + return core::Types::isSubType(ctx, superType, subType, errorDetailsCollector); case core::Polarity::Positive: - return core::Types::isSubType(ctx, subType, superType); + return core::Types::isSubType(ctx, subType, superType, errorDetailsCollector); case core::Polarity::Neutral: Exception::raise("{}: unexpected neutral polarity, did you mean to pass Positive?", ctx.file.data(ctx).path()); } } -string supermethodKind(const core::Context ctx, core::MethodRef method) { +string superMethodKind(const core::Context ctx, core::MethodRef method) { auto methodData = method.data(ctx); ENFORCE(methodData->flags.isAbstract || methodData->flags.isOverridable || methodData->hasSig()); if (methodData->flags.isAbstract) { @@ -138,16 +144,20 @@ void matchPositional(const core::Context ctx, core::TypeConstraint &constr, auto &superArgType = superArgs[idx].get().type; auto &methodArgType = methodArgs[idx].get().type; - if (!checkSubtype(ctx, constr, methodArgType, method, superArgType, superMethod, core::Polarity::Negative)) { + core::ErrorSection::Collector errorDetailsCollector; + if (!checkSubtype(ctx, constr, methodArgType, method, superArgType, superMethod, core::Polarity::Negative, + errorDetailsCollector)) { if (auto e = ctx.state.beginError(method.data(ctx)->loc(), core::errors::Resolver::BadMethodOverride)) { e.setHeader("Parameter `{}` of type `{}` not compatible with type of {} method `{}`", - methodArgs[idx].get().show(ctx), methodArgType.show(ctx), supermethodKind(ctx, superMethod), + methodArgs[idx].get().show(ctx), methodArgType.show(ctx), superMethodKind(ctx, superMethod), superMethod.show(ctx)); e.addErrorLine(superMethod.data(ctx)->loc(), "The super method parameter `{}` was declared here with type `{}`", superArgs[idx].get().show(ctx), superArgType.show(ctx)); e.addErrorNote( "A parameter's type must be a supertype of the same parameter's type on the super method."); + core::TypeErrorDiagnostics::explainTypeMismatch(ctx, e, errorDetailsCollector, superArgType, + methodArgType); } } idx++; @@ -200,7 +210,7 @@ void validateCompatibleOverride(const core::Context ctx, core::MethodRef superMe // allow them to be declared with different names. // // The tradeoff is that this provides a cheap way to produce error messages at the - // individual arg that is imcompatible (versus only at the end once all constraints have + // individual arg that is incompatible (versus only at the end once all constraints have // been collected) at the cost of rejecting compatible overrides. // // (An alternative might be to collect a constraint and then after validating all arguments, @@ -276,18 +286,21 @@ void validateCompatibleOverride(const core::Context ctx, core::MethodRef superMe // if there is a corresponding parameter, make sure it has the right type if (hasCorrespondingRequired || hasCorrespondingOptional) { + core::ErrorSection::Collector errorDetailsCollector; if (!checkSubtype(ctx, *constr, corresponding->get().type, method, req.get().type, superMethod, - core::Polarity::Negative)) { + core::Polarity::Negative, errorDetailsCollector)) { if (auto e = ctx.state.beginError(method.data(ctx)->loc(), core::errors::Resolver::BadMethodOverride)) { e.setHeader("Keyword parameter `{}` of type `{}` not compatible with type of {} method `{}`", corresponding->get().show(ctx), corresponding->get().type.show(ctx), - supermethodKind(ctx, superMethod), superMethod.show(ctx)); + superMethodKind(ctx, superMethod), superMethod.show(ctx)); e.addErrorLine(superMethod.data(ctx)->loc(), "The corresponding parameter `{}` was declared here with type `{}`", req.get().show(ctx), req.get().type.show(ctx)); e.addErrorNote( "A parameter's type must be a supertype of the same parameter's type on the super method."); + core::TypeErrorDiagnostics::explainTypeMismatch(ctx, e, errorDetailsCollector, req.get().type, + corresponding->get().type); } } } else { @@ -306,18 +319,21 @@ void validateCompatibleOverride(const core::Context ctx, core::MethodRef superMe // if there is a corresponding parameter, make sure it has the right type if (corresponding != right.kw.optional.end()) { + core::ErrorSection::Collector errorDetailsCollector; if (!checkSubtype(ctx, *constr, corresponding->get().type, method, opt.get().type, superMethod, - core::Polarity::Negative)) { + core::Polarity::Negative, errorDetailsCollector)) { if (auto e = ctx.state.beginError(method.data(ctx)->loc(), core::errors::Resolver::BadMethodOverride)) { e.setHeader("Keyword parameter `{}` of type `{}` not compatible with type of {} method `{}`", corresponding->get().show(ctx), corresponding->get().type.show(ctx), - supermethodKind(ctx, superMethod), superMethod.show(ctx)); + superMethodKind(ctx, superMethod), superMethod.show(ctx)); e.addErrorLine(superMethod.data(ctx)->loc(), "The super method parameter `{}` was declared here with type `{}`", opt.get().show(ctx), opt.get().type.show(ctx)); e.addErrorNote( "A parameter's type must be a supertype of the same parameter's type on the super method."); + core::TypeErrorDiagnostics::explainTypeMismatch(ctx, e, errorDetailsCollector, opt.get().type, + corresponding->get().type); } } } else if (absl::c_any_of(right.kw.required, @@ -344,6 +360,7 @@ void validateCompatibleOverride(const core::Context ctx, core::MethodRef superMe } if (auto leftRest = left.kw.rest) { + core::ErrorSection::Collector errorDetailsCollector; if (!right.kw.rest) { if (auto e = ctx.state.beginError(method.data(ctx)->loc(), core::errors::Resolver::BadMethodOverride)) { e.setHeader("{} method `{}` must accept **`{}`", implementationOf(ctx, superMethod), @@ -351,16 +368,18 @@ void validateCompatibleOverride(const core::Context ctx, core::MethodRef superMe e.addErrorLine(superMethod.data(ctx)->loc(), "Base method defined here"); } } else if (!checkSubtype(ctx, *constr, right.kw.rest->get().type, method, leftRest->get().type, superMethod, - core::Polarity::Negative)) { + core::Polarity::Negative, errorDetailsCollector)) { if (auto e = ctx.state.beginError(method.data(ctx)->loc(), core::errors::Resolver::BadMethodOverride)) { e.setHeader("Parameter **`{}` of type `{}` not compatible with type of {} method `{}`", right.kw.rest->get().show(ctx), right.kw.rest->get().type.show(ctx), - supermethodKind(ctx, superMethod), superMethod.show(ctx)); + superMethodKind(ctx, superMethod), superMethod.show(ctx)); e.addErrorLine(superMethod.data(ctx)->loc(), "The super method parameter **`{}` was declared here with type `{}`", left.kw.rest->get().show(ctx), left.kw.rest->get().type.show(ctx)); e.addErrorNote( "A parameter's type must be a supertype of the same parameter's type on the super method."); + core::TypeErrorDiagnostics::explainTypeMismatch(ctx, e, errorDetailsCollector, left.kw.rest->get().type, + right.kw.rest->get().type); } } } @@ -390,43 +409,87 @@ void validateCompatibleOverride(const core::Context ctx, core::MethodRef superMe const auto &methodBlkArg = method.data(ctx)->arguments.back(); const auto &superMethodBlkArg = superMethod.data(ctx)->arguments.back(); + core::ErrorSection::Collector errorDetailsCollector; if (!checkSubtype(ctx, *constr, methodBlkArg.type, method, superMethodBlkArg.type, superMethod, - core::Polarity::Negative)) { + core::Polarity::Negative, errorDetailsCollector)) { if (auto e = ctx.state.beginError(method.data(ctx)->loc(), core::errors::Resolver::BadMethodOverride)) { e.setHeader("Block parameter `{}` of type `{}` not compatible with type of {} method `{}`", methodBlkArg.argumentName(ctx), methodBlkArg.type.show(ctx), - supermethodKind(ctx, superMethod), superMethod.show(ctx)); + superMethodKind(ctx, superMethod), superMethod.show(ctx)); e.addErrorLine(superMethod.data(ctx)->loc(), "The super method parameter `{}` was declared here with type `{}`", superMethodBlkArg.show(ctx), superMethodBlkArg.type.show(ctx)); e.addErrorNote( "A parameter's type must be a supertype of the same parameter's type on the super method."); + core::TypeErrorDiagnostics::explainTypeMismatch(ctx, e, errorDetailsCollector, superMethodBlkArg.type, + methodBlkArg.type); } } } { // make sure the return types are compatible - auto &superReturn = superMethod.data(ctx)->resultType; - auto &methodReturn = method.data(ctx)->resultType; - if (!checkSubtype(ctx, *constr, methodReturn, method, superReturn, superMethod, core::Polarity::Positive)) { + auto superReturn = superMethod.data(ctx)->resultType; + if (superReturn == core::Types::void_()) { + // Mimics how `.void` methods are handled in cfg::Return case of environment.cc + superReturn = core::Types::top(); + } + + auto &methodReturn = method.data(ctx)->resultType; + // Don't have to do the void -> top trick, because if parent is top, the child method return + // type can be whatever. And if parent is not top, then neither void nor T.anything in the + // child return will be compatible with the parent, so we may as well keep it as `void` for + // the sake of showing an error message in the terms that the user wrote ("where did this + // T.anything come from? I wrote .void"). + + core::ErrorSection::Collector errorDetailsCollector; + if (!checkSubtype(ctx, *constr, methodReturn, method, superReturn, superMethod, core::Polarity::Positive, + errorDetailsCollector)) { if (auto e = ctx.state.beginError(method.data(ctx)->loc(), core::errors::Resolver::BadMethodOverride)) { - e.setHeader("Return type `{}` does not match return type of {} method `{}`", methodReturn.show(ctx), - supermethodKind(ctx, superMethod), superMethod.show(ctx)); + auto methodReturnShow = methodReturn == core::Types::void_() ? "void" : methodReturn.show(ctx); + e.setHeader("Return type `{}` does not match return type of {} method `{}`", methodReturnShow, + superMethodKind(ctx, superMethod), superMethod.show(ctx)); e.addErrorLine(superMethod.data(ctx)->loc(), "Super method defined here with return type `{}`", superReturn.show(ctx)); e.addErrorNote("A method's return type must be a subtype of the return type on the super method."); + core::TypeErrorDiagnostics::explainTypeMismatch(ctx, e, errorDetailsCollector, superReturn, + methodReturn); } } } } -void validateOverriding(const core::Context ctx, core::MethodRef method) { +optional +constructOverrideAutocorrect(const core::Context ctx, const ast::ExpressionPtr &tree, core::MethodRef method) { + auto methodLoc = method.data(ctx)->loc(); + auto parsedSig = sig_finder::SigFinder::findSignature(ctx, tree, methodLoc.copyWithZeroLength()); + if (!parsedSig.has_value()) { + return nullopt; + } + + ast::Block *block = parsedSig->origSend->block(); + if (!block) { + return nullopt; + } + + auto *blockBody = ast::cast_tree(block->body); + ENFORCE(blockBody != nullptr); + auto insertLoc = ctx.locAt(blockBody->loc.copyWithZeroLength()); + + vector edits; + edits.emplace_back(core::AutocorrectSuggestion::Edit{insertLoc, "override."}); + return core::AutocorrectSuggestion{ + fmt::format("Add `{}` to `{}` sig", "override", method.data(ctx)->name.show(ctx)), + std::move(edits), + }; +} + +void validateOverriding(const core::Context ctx, const ast::ExpressionPtr &tree, core::MethodRef method) { auto klass = method.data(ctx)->owner; auto name = method.data(ctx)->name; auto klassData = klass.data(ctx); - InlinedVector overridenMethods; + InlinedVector overriddenMethods; // Matches the behavior of the runtime checks // NOTE(jez): I don't think this check makes all that much sense, but I haven't thought about it. @@ -450,17 +513,17 @@ void validateOverriding(const core::Context ctx, core::MethodRef method) { if (klassData->superClass().exists()) { auto superMethod = klassData->superClass().data(ctx)->findMethodTransitive(ctx, name); if (superMethod.exists()) { - overridenMethods.emplace_back(superMethod); + overriddenMethods.emplace_back(superMethod); } } for (const auto &mixin : klassData->mixins()) { auto superMethod = mixin.data(ctx)->findMethod(ctx, name); if (superMethod.exists()) { - overridenMethods.emplace_back(superMethod); + overriddenMethods.emplace_back(superMethod); } } - if (overridenMethods.size() == 0 && method.data(ctx)->flags.isOverride && + if (overriddenMethods.size() == 0 && method.data(ctx)->flags.isOverride && !method.data(ctx)->flags.isIncompatibleOverride) { if (auto e = ctx.state.beginError(method.data(ctx)->loc(), core::errors::Resolver::BadMethodOverride)) { e.setHeader("Method `{}` is marked `{}` but does not override anything", method.show(ctx), "override"); @@ -469,40 +532,57 @@ void validateOverriding(const core::Context ctx, core::MethodRef method) { // we don't raise override errors if the method implements an abstract method, which means we need to know ahead of // time whether any parent methods are abstract - auto anyIsInterface = absl::c_any_of(overridenMethods, [&](auto &m) { return m.data(ctx)->flags.isAbstract; }); - for (const auto &overridenMethod : overridenMethods) { - if (overridenMethod.data(ctx)->flags.isFinal) { + auto anyIsInterface = absl::c_any_of(overriddenMethods, [&](auto &m) { return m.data(ctx)->flags.isAbstract; }); + for (const auto &overriddenMethod : overriddenMethods) { + if (overriddenMethod.data(ctx)->flags.isFinal) { if (auto e = ctx.state.beginError(method.data(ctx)->loc(), core::errors::Resolver::OverridesFinal)) { - e.setHeader("`{}` was declared as final and cannot be overridden by `{}`", overridenMethod.show(ctx), + e.setHeader("`{}` was declared as final and cannot be overridden by `{}`", overriddenMethod.show(ctx), method.show(ctx)); - e.addErrorLine(overridenMethod.data(ctx)->loc(), "original method defined here"); + e.addErrorLine(overriddenMethod.data(ctx)->loc(), "original method defined here"); } } auto isRBI = absl::c_any_of(method.data(ctx)->locs(), [&](auto &loc) { return loc.file().data(ctx).isRBI(); }); if (!method.data(ctx)->flags.isOverride && method.data(ctx)->hasSig() && - (overridenMethod.data(ctx)->flags.isOverridable || overridenMethod.data(ctx)->flags.isOverride) && - !anyIsInterface && overridenMethod.data(ctx)->hasSig() && !method.data(ctx)->flags.isRewriterSynthesized && + (overriddenMethod.data(ctx)->flags.isOverridable || overriddenMethod.data(ctx)->flags.isOverride) && + !anyIsInterface && overriddenMethod.data(ctx)->hasSig() && !method.data(ctx)->flags.isRewriterSynthesized && !isRBI) { - if (auto e = ctx.state.beginError(method.data(ctx)->loc(), core::errors::Resolver::UndeclaredOverride)) { + auto methodLoc = method.data(ctx)->loc(); + + if (auto e = ctx.state.beginError(methodLoc, core::errors::Resolver::UndeclaredOverride)) { e.setHeader("Method `{}` overrides an overridable method `{}` but is not declared with `{}`", - method.show(ctx), overridenMethod.show(ctx), "override."); - e.addErrorLine(overridenMethod.data(ctx)->loc(), "defined here"); + method.show(ctx), overriddenMethod.show(ctx), "override."); + e.addErrorLine(overriddenMethod.data(ctx)->loc(), "defined here"); + + auto potentialAutocorrect = constructOverrideAutocorrect(ctx, tree, method); + if (potentialAutocorrect.has_value()) { + e.addAutocorrect(std::move(*potentialAutocorrect)); + } } } if (!method.data(ctx)->flags.isOverride && !method.data(ctx)->flags.isAbstract && method.data(ctx)->hasSig() && - overridenMethod.data(ctx)->flags.isAbstract && overridenMethod.data(ctx)->hasSig() && + overriddenMethod.data(ctx)->flags.isAbstract && overriddenMethod.data(ctx)->hasSig() && !method.data(ctx)->flags.isRewriterSynthesized && !isRBI) { if (auto e = ctx.state.beginError(method.data(ctx)->loc(), core::errors::Resolver::UndeclaredOverride)) { e.setHeader("Method `{}` implements an abstract method `{}` but is not declared with `{}`", - method.show(ctx), overridenMethod.show(ctx), "override."); - e.addErrorLine(overridenMethod.data(ctx)->loc(), "defined here"); + method.show(ctx), overriddenMethod.show(ctx), "override."); + e.addErrorLine(overriddenMethod.data(ctx)->loc(), "defined here"); + + auto potentialAutocorrect = constructOverrideAutocorrect(ctx, tree, method); + if (potentialAutocorrect.has_value()) { + e.addAutocorrect(std::move(*potentialAutocorrect)); + } } } - if ((overridenMethod.data(ctx)->flags.isAbstract || overridenMethod.data(ctx)->flags.isOverridable || - (overridenMethod.data(ctx)->hasSig() && method.data(ctx)->flags.isOverride)) && + if ((overriddenMethod.data(ctx)->flags.isAbstract || overriddenMethod.data(ctx)->flags.isOverridable || + (overriddenMethod.data(ctx)->hasSig() && method.data(ctx)->flags.isOverride)) && !method.data(ctx)->flags.isIncompatibleOverride && !isRBI && - !method.data(ctx)->flags.isRewriterSynthesized) { - validateCompatibleOverride(ctx, overridenMethod, method); + !method.data(ctx)->flags.isRewriterSynthesized && + overriddenMethod != core::Symbols::BasicObject_initialize()) { + // We only ignore BasicObject#initialize for backwards compatibility. + // One day, we may want to build something like overridable(allow_incompatible: true) + // and mark certain methods in the standard library as possible to be overridden incompatibly, + // without needing to write `override(allow_incompatible: true)`. + validateCompatibleOverride(ctx, overriddenMethod, method); } } } @@ -539,7 +619,7 @@ void validateFinalAncestorHelper(core::Context ctx, const core::ClassOrModuleRef } } -void validateFinalMethodHelper(core::Context ctx, const core::ClassOrModuleRef klass, ast::ExpressionPtr &tree, +void validateFinalMethodHelper(core::Context ctx, const core::ClassOrModuleRef klass, const ast::ExpressionPtr &tree, const core::ClassOrModuleRef errMsgClass) { if (!klass.data(ctx)->flags.isFinal) { return; @@ -571,7 +651,7 @@ void validateFinalMethodHelper(core::Context ctx, const core::ClassOrModuleRef k } } -void validateFinal(core::Context ctx, const core::ClassOrModuleRef klass, ast::ExpressionPtr &tree) { +void validateFinal(core::Context ctx, const core::ClassOrModuleRef klass, const ast::ExpressionPtr &tree) { const ast::ClassDef &classDef = ast::cast_tree_nonnull(tree); const auto superClass = klass.data(ctx)->superClass(); if (superClass.exists()) { @@ -730,8 +810,7 @@ void validateSuperClass(core::Context ctx, const core::ClassOrModuleRef sym, con return; } - if (auto e = ctx.state.beginError(core::Loc(sym.data(ctx)->loc().file(), classDef.declLoc), - core::errors::Resolver::NonClassSuperclass)) { + if (auto e = ctx.state.beginError(ctx.locAt(classDef.declLoc), core::errors::Resolver::NonClassSuperclass)) { auto superClassFqn = superClass.show(ctx); e.setHeader("The super class `{}` of `{}` does not derive from `{}`", superClassFqn, sym.show(ctx), core::Symbols::Class().show(ctx)); @@ -837,7 +916,11 @@ void validateRequiredAncestors(core::Context ctx, const core::ClassOrModuleRef s } class ValidateWalk { +public: + ValidateWalk(const ast::ExpressionPtr &tree) : tree(tree) {} + private: + const ast::ExpressionPtr &tree; UnorderedMap> abstractCache; const vector &getAbstractMethods(const core::GlobalState &gs, core::ClassOrModuleRef klass) { @@ -897,42 +980,155 @@ class ValidateWalk { if (auto e = gs.beginError(sym.data(gs)->loc(), core::errors::Resolver::SubclassingNotAllowed)) { auto parentName = parent.show(gs); e.setHeader("Subclassing `{}` is not allowed", parentName); - e.addErrorLine(parent.data(gs)->loc(), "`{}` is a subclass of `T::Struct`", parentName); + auto parentDeclLoc = parent.data(gs)->loc(); + e.addErrorLine(parentDeclLoc, "`{}` is a subclass of `{}`", parentName, "T::Struct"); + if (gs.suggestUnsafe && parentDeclLoc.exists()) { + auto declSource = parentDeclLoc.source(gs).value(); + auto ltTStruct = "< T::Struct"sv; + if (absl::EndsWith(declSource, ltTStruct)) { + e.replaceWith("Replace with `T::InexactStruct`", parentDeclLoc, "{}< {}", + declSource.substr(0, declSource.size() - ltTStruct.size()), "T::InexactStruct"); + } + } } } - void validateAbstract(const core::GlobalState &gs, core::ClassOrModuleRef sym) { - if (sym.data(gs)->flags.isAbstract) { + core::AutocorrectSuggestion::Edit defineInheritedAbstractMethod(const core::GlobalState &gs, + const core::ClassOrModuleRef sym, + const core::MethodRef abstractMethodRef, + const core::Loc insertAt, const string &format, + const string &classOrModuleIndent) { + auto showOptions = core::ShowOptions().withUseValidSyntax().withConcretizeIfAbstract(); + if (sym.data(gs)->attachedClass(gs).exists()) { + showOptions = showOptions.withForceSelfPrefix(); + } + auto resultType = abstractMethodRef.data(gs)->resultType; + auto methodDefinition = core::source_generator::prettyTypeForMethod(gs, abstractMethodRef, nullptr, resultType, + nullptr, showOptions); + + vector indentedLines; + absl::c_transform( + absl::StrSplit(methodDefinition, "\n"), std::back_inserter(indentedLines), + [classOrModuleIndent](auto &line) -> string { return fmt::format("{} {}", classOrModuleIndent, line); }); + auto indentedMethodDefinition = absl::StrJoin(indentedLines, "\n"); + + return core::AutocorrectSuggestion::Edit{insertAt, fmt::format(format, indentedMethodDefinition)}; + } + + void validateAbstract(const core::Context ctx, core::ClassOrModuleRef sym, const ast::ClassDef &classDef) { + if (sym.data(ctx)->flags.isAbstract) { return; } - auto loc = sym.data(gs)->loc(); - if (loc.exists() && loc.file().data(gs).isRBI()) { + + if (ctx.file.data(ctx).isRBI()) { return; } - auto &abstract = getAbstractMethods(gs, sym); + auto missingAbstractMethods = findMissingAbstractMethods(ctx, sym); + if (missingAbstractMethods.empty()) { + return; + } - if (abstract.empty()) { + auto errorBuilder = ctx.beginError(classDef.declLoc, core::errors::Resolver::BadAbstractMethod); + if (!errorBuilder) { return; } - for (auto proto : abstract) { - if (proto.data(gs)->owner == sym) { + if (missingAbstractMethods.size() > 1) { + errorBuilder.setHeader("Missing definitions for abstract methods in `{}`", sym.show(ctx)); + } else { + errorBuilder.setHeader("Missing definition for abstract method `{}` in `{}`", + missingAbstractMethods.front().show(ctx), sym.show(ctx)); + } + + auto classOrModuleDeclaredAt = ctx.locAt(classDef.declLoc); + auto classOrModuleEndsAt = ctx.locAt(classDef.loc.copyEndWithZeroLength()); + auto hasSingleLineDefinition = + classOrModuleDeclaredAt.position(ctx).first.line == classOrModuleEndsAt.position(ctx).second.line; + + auto hasEmptyBody = classDef.rhs.empty(); + if (classDef.rhs.size() == 1) { + if (auto *mdef = ast::cast_tree(classDef.rhs[0])) { + hasEmptyBody = ast::isa_tree(mdef->rhs); + } + } + + auto [endLoc, indentLength] = classOrModuleEndsAt.findStartOfLine(ctx); + string classOrModuleIndent(indentLength, ' '); + auto insertAt = endLoc.adjust(ctx, -indentLength, 0); + auto format = "{}\n" + classOrModuleIndent; + + vector edits; + if (hasSingleLineDefinition && hasEmptyBody) { + // First, break the class/module definition up onto multiple lines. + auto endRange = classOrModuleDeclaredAt.copyEndWithZeroLength().join(classOrModuleEndsAt); + edits.emplace_back( + core::AutocorrectSuggestion::Edit{endRange, fmt::format("\n{}end", classOrModuleIndent)}); + + // Then, modify our insertion strategy such that we add new methods to the top of the class/module + // body rather than the bottom. This is a trick to ensure that we put the new methods within the new + // class/module body that we just created. + insertAt = classOrModuleDeclaredAt.copyEndWithZeroLength(); + format = "\n{}"; + } + + for (auto proto : missingAbstractMethods) { + errorBuilder.addErrorLine(proto.data(ctx)->loc(), "`{}` defined here", proto.data(ctx)->name.show(ctx)); + + if (hasSingleLineDefinition && !hasEmptyBody) { + // We don't suggest autocorrects for this case because we cannot say for sure how the + // class/module has been declared, and so we also can't determine how to modify it. continue; } - auto mem = sym.data(gs)->findConcreteMethodTransitive(gs, proto.data(gs)->name); - if (!mem.exists()) { - if (auto e = gs.beginError(loc, core::errors::Resolver::BadAbstractMethod)) { - e.setHeader("Missing definition for abstract method `{}`", proto.show(gs)); - e.addErrorLine(proto.data(gs)->loc(), "defined here"); - } + edits.emplace_back(defineInheritedAbstractMethod(ctx, sym, proto, insertAt, format, classOrModuleIndent)); + } + + if (edits.empty()) { + return; + } + + errorBuilder.addAutocorrect(core::AutocorrectSuggestion{ + fmt::format("Define inherited abstract method{}", missingAbstractMethods.size() > 1 ? "s" : ""), + edits, + }); + } + + vector findMissingAbstractMethods(const core::Context ctx, core::ClassOrModuleRef sym) { + vector result; + + auto &abstractMethods = getAbstractMethods(ctx, sym); + if (abstractMethods.empty()) { + return result; + } + + for (auto proto : abstractMethods) { + if (proto.data(ctx)->owner == sym) { + continue; } + + auto concreteMethodRef = sym.data(ctx)->findConcreteMethodTransitive(ctx, proto.data(ctx)->name); + if (concreteMethodRef.exists()) { + continue; + } + + result.emplace_back(proto); } + + // Sort missing methods to ensure consistent ordering in both the error message and the autocorrect output. + fast_sort(result, [ctx](const auto &l, const auto &r) -> bool { + return l.data(ctx)->name.show(ctx) < r.data(ctx)->name.show(ctx); + }); + + // Deduplicate methods to prevent suggesting duplicate corrections for methods that are missing from several + // ancestors in the same inheritance chain. + result.resize(std::distance(result.begin(), std::unique(result.begin(), result.end()))); + + return result; } public: - void preTransformClassDef(core::Context ctx, ast::ExpressionPtr &tree) { + void preTransformClassDef(core::Context ctx, const ast::ExpressionPtr &tree) { auto &classDef = ast::cast_tree_nonnull(tree); auto sym = classDef.symbol; auto singleton = sym.data(ctx)->lookupSingletonClass(ctx); @@ -940,13 +1136,13 @@ class ValidateWalk { if (!sym.data(ctx)->isSingletonClass(ctx)) { // Only validateAbstract for this class if we haven't already (we already have if this // is a `class << self` ClassDef) - validateAbstract(ctx, sym); + validateAbstract(ctx, sym, classDef); if (ctx.state.requiresAncestorEnabled) { validateRequiredAncestors(ctx, sym); } } - validateAbstract(ctx, singleton); + validateAbstract(ctx, singleton, classDef); validateFinal(ctx, sym, tree); validateSealed(ctx, sym, classDef); validateSuperClass(ctx, sym, classDef); @@ -956,7 +1152,7 @@ class ValidateWalk { } } - void preTransformMethodDef(core::Context ctx, ast::ExpressionPtr &tree) { + void preTransformMethodDef(core::Context ctx, const ast::ExpressionPtr &tree) { auto &methodDef = ast::cast_tree_nonnull(tree); auto methodData = methodDef.symbol.data(ctx); auto ownerData = methodData->owner.data(ctx); @@ -982,10 +1178,10 @@ class ValidateWalk { // See the comment in `VarianceValidator::validateMethod` for an explanation of why we don't // need to check types on instance variables. - validateOverriding(ctx, methodDef.symbol); + validateOverriding(ctx, this->tree, methodDef.symbol); } - void postTransformSend(core::Context ctx, ast::ExpressionPtr &tree) { + void postTransformSend(core::Context ctx, const ast::ExpressionPtr &tree) { auto &send = ast::cast_tree_nonnull(tree); if (send.fun != core::Names::new_()) { return; @@ -1023,7 +1219,7 @@ class ValidateWalk { ast::ParsedFile runOne(core::Context ctx, ast::ParsedFile tree) { Timer timeit(ctx.state.tracer(), "validateSymbols"); - ValidateWalk validate; + ValidateWalk validate(tree.tree); ast::TreeWalk::apply(ctx, validate, tree.tree); return tree; } diff --git a/docs/build-llvm.md b/docs/build-llvm.md new file mode 100644 index 0000000000..946de517a1 --- /dev/null +++ b/docs/build-llvm.md @@ -0,0 +1,170 @@ +# Building an LLVM release + +Useful links: + +- +- +- + +Commands I've run in the past: + +```bash +pay ssh +sudo apt-get install -y autoconf build-essential cmake parallel mc chrpath ninja + +# This builds LLVM with the GCC toolchain that's on the box +# (It seems like it actually bootstraps it.) +llvm/utils/release/test-release.sh \ + -no-test-suite \ + -no-compare-files \ + -release 15.0.7 \ + -final \ + -triple x86_64-linux-gnu-ubuntu-20.04 \ + -j 16 \ + -use-ninja \ + -lldb + +# The same, but for Linux on aarch64 +llvm/utils/release/test-release.sh \ + -no-test-suite \ + -no-compare-files \ + -release 15.0.7 \ + -final \ + -triple aarch64-linux-gnu \ + -j 16 \ + -use-ninja \ + -lldb + +# +# I've never had to run this on macOS, but if/when we do that it would be good +# to update this with the command used. +# +``` + +The release tarball is at `final/*.tar.xz`. + +The build took over 2 hours to create on a devbox (I just know that the first +attempt failed at the 2 hour mark--I didn't get the full runtime for the time it +passed). + +It also ran the tests at the end (I thought that the `-no-test-suite` flag would +have turned that off), which added another half hour to the runtime. The test +results looked like this: + +``` +Testing Time: 1936.77s + Skipped : 46 + Unsupported : 4791 + Passed : 99647 + Expectedly Failed: 303 + Timed Out : 18 + Failed : 28 +FAILED: CMakeFiles/check-all +``` + +Having a 99.95% pass rate was good enough for me. + +## Tinkering with these releases + +The easiest way to trick bazel into using this tarball is to put the tarball in +a folder, serve that folder with `python -m http.server`, and then update the +`llvm_mirror_prefixes` variable in the `WORKSPACE` so that it hits the localhost +mirror first. + +We try to test with localhost servers like this before publishing the release +somewhere, because it's way more annoying to yank a bad release from a trusted +source later than it is to spin up a localhost testing server like this. + +You will also have to put the release archive name and its checksum in the +`bazel-toolchain` (`toolchains_llvm`) repo, in the file called +`toolchain/internal/llvm_distributions.bzl`. At the moment, there is no way to +provide extra, per-project LLVM releases tarballs—they must all be declared +upstream. We don't submit our LLVM release builds' checksums upstream because we +don't want to be on the hook for maintaining these releases for the whole world. + +## Publishing these releases + +We have a [GitHub fork of LLVM](https://github.com/sorbet/llvm-project) that we +use only for publishing extra release tarballs. At this time there are no custom +changes to LLVM in this fork in support of building Sorbet. + +To publish a new release: + +1. Clone the LLVM project, and add the Sorbet fork as a remote: + + ``` + mkdir -p ~/stripe/github + cd !$ + git clone git@github.com:llvm/llvm-project.git + cd llvm-project + git remote add sorbet git@github.com:sorbet/llvm-project.git + git fetch sorbet + ``` + + This repo is huge and will take over 4 minutes to clone even on a fast + connection. + + If you already have this repo locally (maybe this is not your first time + upgrading LLVM), make sure to pull: + + ``` + cd ~/stripe/github/llvm-project + git checkout main + git pull + git push sorbet main + ``` + + This should update the `main` branch and also fetch all new `release/*` + branches and all new release tags. + +1. Push the LLVM release branch and tag to the Sorbet fork. (The Sorbet fork by + default has no branches, only those that we have explicitly pushed to it.) + + ``` + git checkout --track origin/release/15.x + git push sorbet release/15.x # release branch + git push sorbet llvm-org-15.0.7 # release tag + ``` + +1. Create a release in Sorbet's fork with the upstream tag. + You can use this command to open the release page in a browser. You'll want + to edit the `LLVM` variable so it populates the right version in all the + places. + + ``` + (LLVM=15.0.7; open "https://github.com/sorbet/llvm-project/releases/new?tag=llvmorg-$LLVM&title=LLVM%20$LLVM&body=LLVM%20$LLVM%20release%20builds%20created%20by%20the%20Sorbet%20team,%20for%20use%20when%20building%20Sorbet") + ``` + + Make sure that the release notes look good, and attach any binaries that you + need to to this release. + + When everything looks good, click the green button to create the release. + +## [OUTDATED] Publishing releases to S3 + +> [!NOTE] +> +> We used to use a special S3 bucket owned by Stripe to host custom LLVM builds. +> At some point, Stripe's build infrastructure changed, and these buckets are no +> longer reachable from Stripe's CI environment. These notes are still here for +> posterity's sake. + +To make these archives available to Sorbet when building in Buildkite CI and in +Stripe's CI environment, we store the archives in S3. + +I always use the AWS console for this. There are instructions for how to log in +at . From there, you'll have to + +- Make a new folder with the LLVM version number +- Upload any archives you built +- Set the "Everyone (public access)" + (http://acs.amazonaws.com/groups/global/AllUsers) ACL to **Read**, so that + Buildkite can access it. + - To do this, you'll have to temporarily change the [Block Public Access + settings for this + account](https://us-west-2.console.aws.amazon.com/s3/settings?region=us-west-2&bucketType=general) + for the Sorbet S3 account. + - Be sure to flip this back to off when you're done. + +It may be useful to use an `aws s3 cp` invocation for this. If you do that, feel +free to update this doc with what command you used. diff --git a/docs/compiler-caveats.md b/docs/compiler-caveats.md deleted file mode 100644 index 07b4a8162f..0000000000 --- a/docs/compiler-caveats.md +++ /dev/null @@ -1,128 +0,0 @@ -# Compiler Caveats - -## Typechecking - -The compiler will generate code that unconditionally checks signatures for -compiled methods and type annotations in the body of a compiled method. This is -true even of `sig` blocks that specify `checked(:never)`. Sorbet -unconditionally checks types because those same checks are used to enable fast -execution paths within methods that the compiler is aware of. - - -## Runtime - -Loading shared objects produced by the compiler requires a little bit of setup. -As it's not possible for us to bake-in the path to the ruby source file at -compile time, we expect that this is passed through to the shared object via the -`$__sorbet_ruby_realpath` global. The effect of this is is that you either need -to patch `require` to supply this value at runtime, or provide it directly -before loading the shared object. - -In our tests, we patch require and provide this value before loading the shared -object: https://github.com/sorbet/sorbet/blob/aa8434150/test/patch_require.rb#L31. - -The compiler assumes that you have a monkey patch for `BasicObject#nil?` that -behaves identically to `Kernel#nil?`. If you do not have this, calling `.nil?` -on an object that does respond to `nil?` or defines it in some other way will -not throw an exception (as the interpreter would do) but will instead evaluate -to whether the receiver is `nil` or not. - -## Thread safety - -The compiler currently assumes that the artifacts it produces are used in a -single-threaded context. The most significant place that this assumption is -leveraged is during shared object initialization, where both global values are -shared as weak symbols and static values linked into the vm are initialized on -first access. - -This can be mitigated by ensuring that only one shared object is loaded at a -time, which Stripe enforces with a lock around `require` statements in the -autoloader. As this assumption is present in Stripe's environment, we don't -currently have plans to make initialization of shared objects work in the -context of multiple threads. - -## The ruby stack - -There are a few different features that cause compiled code to interact directly -with the ruby stack: - -* Accurate line numbers in stack traces -* Closure variable storage -* Method arguments for sends that will go through the vm - -In order to to support these features, we edit the top of the ruby stack when a -compiled function is entered, making space for locals and populating the iseq -pointer. The iseq pointer that is written into the stack it is allocated when -the compiled module is first loaded, and has enough fields filled out to inform -the vm of the stack layout required, and how to reconstruct a line number at a -given point in time. - -One problem with this approach is that all of the functions emitted by the -compiler now assume that it's fine to edit the top of the ruby stack. In the -case where the function has been called through the vm this assumption is valid, -but if the function is called directly from some other context a stack frame -must be pushed to avoid corrupting the caller. This comes up in a few different -cases: - -1. Class and method `` functions, which don't correspond to real - functions that the vm will ever call, but that get called during compiled - module initialization: - - https://github.com/sorbet/sorbet/blob/aa84341504/compiler/IREmitter/NameBasedIntrinsics.cc#L109-L112 - - https://github.com/sorbet/sorbet/blob/aa84341504/compiler/IREmitter/Payload/codegen-payload.c#L1213-L1219 -2. Final methods that are called directly would edit the frame of the calling - context and corrupt any locals of that context if a frame isn't pushed: - - https://github.com/sorbet/sorbet/blob/aa84341504/compiler/IREmitter/sends.cc#L134-L135 - - https://github.com/sorbet/sorbet/blob/aa84341504/compiler/IREmitter/Payload/codegen-payload.c#L1895-L1907 -3. Blocks that are inlined into the body of intrinsics that support direct - block calls will also need their own frames, as raising exceptions from that - context without them would put the vm into a strange state. - - https://github.com/sorbet/sorbet/blob/aa84341504/compiler/IREmitter/Payload/codegen-payload.c#L785-L803 -4. Block functions extracted from `rescue` blocks need to have a special frame - pushed that contains enough space for one local that holds `$!`: - - https://github.com/sorbet/sorbet/blob/aa84341504/compiler/IREmitter/Payload/patches/vm_insnhelper.c#L21-L22 - -## Patches to the Ruby VM - -We maintain a set of patches to the Ruby VM, which are necessary to support the -compiler, in Stripe's Ruby repo: - -http://go/git/stripe-private-oss-forks/ruby - -However, in order to keep the the Sorbet Compiler build self-contained and -runnable outside of Stripe infrastructure, we do not reference the internal git -repository directly from our build. Instead, we generate diffs against vanilla -Ruby 2.7.2, and place them in `third-party/ruby`. The patches are applied in -`third_party/externals.bzl`. - -### Standards for review of Ruby VM patches - -Patching the Ruby VM is a somewhat risky business, for the following reasons: - -1. It is large, complex, and sometimes sparsely documented. -2. Bad VM changes can sometimes have very subtle effects, and may only manifest - in obscure corner cases that, no matter how much testing we do up front, we - may not catch until things are in production. - -Thus we apply the following general standards for pull requests that modify the -VM. - -**If a VM patch very obviously does not affect the behavior of the VM except in -the presence of compiled Ruby code**, then it does not require any special -scrutiny beyond what we would normally apply in PR review. The terms "obviously" -and "behavior" are not precisely defined here; when in doubt, make sure to -discuss the patch with the team. - -Here is an example of a good argument that something "obviously does not affect -behavior" when the compiler is not in use: the patch that introduces -`VM_METHOD_TYPE_SORBET` only extends an existing internal enum for method types, -and adds cases to various functions for that enum value. Nowhere in the VM is a -method of type `VM_METHOD_TYPE_SORBET` ever actually constructed; this only -happens in code generated by the compiler. Therefore, the VM's behavior should -be totally unchanged. - -**If a VM patch _does_ affect the behavior of the VM even in the absence of -compiled Ruby code**, then the PR must clearly explain why we believe the patch -is safe, _and_ the PR author should verify that Ruby's own tests -(`make test-all`) still pass with the patch applied. (Note, however, that -"`make test-all` passes" is not a sufficient argument in itself; in the past, -buggy VM patches have managed to slip by this.) diff --git a/docs/compiler-internals.md b/docs/compiler-internals.md deleted file mode 100644 index 64cc286cea..0000000000 --- a/docs/compiler-internals.md +++ /dev/null @@ -1,24 +0,0 @@ -# SorbetLLVM Internals - -> 🚧 This doc is WIP. Add or change it as you see fit. 🚧 - -Plugin injector: - -1. DSL passes -2. IREmission (typed cfg to LLVM IR) - IREmission (typed cfg to LLVM IR) - IREmission (typed cfg to LLVM IR) - (accumulate multiple methods' CFGs into one llvm::Module) -3. ObjectFileEmission (LLVM IR to shared object `*.so`) - -Ruby methods and Ruby blocks are similar in many ways from the compiler's -perspective (they both end up as llvm::Functions that can be called directly). - -Key data structures: - -TypecheckThreadState - llvm::Module -CompilerState -IREmitterContext -Aliases - diff --git a/docs/debugging-crashes.md b/docs/debugging-crashes.md new file mode 100644 index 0000000000..f8cc017e4d --- /dev/null +++ b/docs/debugging-crashes.md @@ -0,0 +1,97 @@ +# Debugging Crashes in Sorbet + +From time to time, Sorbet crashes. Every crash in Sorbet is a bug, no matter +what. We'd be more than happy to fix the cause of the crash. Most of the time, +fixing a crash in Sorbet is quite easy, while the hard part is diagnosing the +cause of the crash itself. + +This doc contains some tips for debugging Sorbet crashes. + +It's going to be specific to the Sorbet type checker (not sorbet-runtime). + +## Find a reproducer + +This doc is not going to cover tips for how to reproduce the crash. Hopefully, +most crashes will be fairly straightforward to reproduce. If you can reproduce +the crash in a codebase that you're comfortable sharing with the Sorbet team, +feel free to share that reproducer directly with the team, either in a [new +issue], or if absolutely necessary, in a DM with a member of the Sorbet team. + +[new issue]: https://github.com/sorbet/sorbet/issues/new/choose + +Once we have a reproducer for a crash, we are happy to take over from there. + +## Get it running in a C++ debugger + +If for whatever reason, you can't share a reproducer, the other option is to +share more information about the crash itself, so that a member of the Sorbet +team can try to use a combination of looking at the code and trial-and-error to +make a suitable reproducer. + +A backtrace is the most useful piece of information to share, and it's easiest +to share this by running Sorbet under a C/C++ debugger like `gdb` or `lldb`. + +To do this: + +1. Find the path to the Sorbet binary that you're running. If you're using + Bundler, you can print it by running this inside your project. + + ```bash + echo $(bundle info sorbet-static --path)/libexec/sorbet + ``` + + This will print the full path to the `sorbet` binary. Note that the `srb` + executable is a shell wrapper script around the `sorbet` binary executable. + Attempting to debug the `srb` executable will not work. + + If you are using the `SRB_SORBET_EXE` environment variable, that will + already be the path to the `sorbet` binary. + +1. Run Sorbet on your project, under GDB or LLDB. + + ```bash + # gdb: + gdb --args $(bundle info sorbet-static --path)/libexec/sorbet + + # lldb: + lldb -- $(bundle info sorbet-static --path)/libexec/sorbet + ``` + + Then press `r` ENTER to launch the process. + + > Note: + > that when invoking Sorbet via the `srb tc` command, Sorbet will also + > add an extra argument like `@/path/to/.cache/sorbet/...` which Sorbet uses + > to discover RBI files provided via gems' `rbi/` folders. You may or may + > not need to list this extra `@...` argument when running the debugger. + > + > An easy way to list these extra arguments is to run: + > + > ```bash + > SRB_SORBET_EXE=echo srb tc + > ``` + > + > (This works by asking `srb` to pretend that the `sorbet` binary is + > actually the `echo` command, which has the effect of simply printing the + > arguments then exiting without type checking.) + +1. When the crash happens, the debugger will pause execution. You can type + + ``` + bt + ``` + + Ask it to print a pretty backtrace. Please include this information in your + [new issue] or share it with a member of the Sorbet team. + +Depending on the contents of the backtrace, a member of the Sorbet team may ask +you to try running other commands in the debugger to attempt to diagnose the +problem. + +If you want to try to debug it on your own but are unfamiliar with GDB/LLDB, we +recommend this quick reference: + +→ + +It shows a number of commonly-used debugger commands in both GDB and LLDB +command syntax. diff --git a/docs/internals.md b/docs/internals.md index 2ac0b9daab..cfc473eaa6 100644 --- a/docs/internals.md +++ b/docs/internals.md @@ -214,7 +214,7 @@ of plugin system. This will allow for a wider audience of Rubyists to teach Sorbet about DSLs they've written. This is why we've intentionally limited the power of Rewriter passes. -Specfically, we artificially limit what code we call from Rewriter passes. +Specifically, we artificially limit what code we call from Rewriter passes. Sometimes it would be convenient to call into other phases of Sorbet (like resolver or infer), but instead we've reimplemented functionality in the Rewriter pass. This keeps the surface area of the API we'd have to present to diff --git a/docs/lsp-dev-guide.md b/docs/lsp-dev-guide.md new file mode 100644 index 0000000000..af7fda5933 --- /dev/null +++ b/docs/lsp-dev-guide.md @@ -0,0 +1,184 @@ +# Developing Sorbet's Language Server + +Developing on Sorbet's language server is slightly more involved (as compared +with working on the guts of Sorbet's type checker, or the sorbet-runtime gem). + +There are two main things you might want to do: + +1. Work on the server component. + + The best way to develop on the server is to write a small test and use + our test suite. But _is_ possible to test this by driving the editor. + +2. Work on the client component. + + While LSP is technically client agnostic, in practice, VS Code needs a lot + of boilerplate to be able to support most LSP features. As a result, we + maintain first-party support for a VS Code extension. + +## Working on the server + +### By way of tests + +There are two main kinds of LSP tests you're likely to interact with: + +- The LSP tests in `test/testdata/lsp/`. + + Each file in `test/testdata/lsp/` is both a test of Sorbet via LSP and + Sorbet's standalone pipeline. + + These tests are documented in [Sorbet's README]. In particular, when running + as LSP tests, there are a bunch more annotations you can use to test LSP + features. For example `# ^ hover: ...` is an annotation that will send a hover + request at that location. + + **Tips:** + + - [Configure fzf to drive Bazel](https://blog.jez.io/fzf-bazel/). This is the + easiest way to go from "name of file on disk" to "Bazel test target I can + run" + + - Run the test with the `--test_output=errors` flag or `--test_output=all` + flag. If the test fails, you'll be see a line that looks like + + ``` + + exec test/lsp_test_runner --single_test=test/testdata/lsp/completion/alias_method.rb + ``` + + somewhere in the output. This is the actual binary that Bazel built and ran + to run the test, which you can then debug. To debug this test, delete the `+ + exec` part and add `lldb -- bazel-bin/` in front: + + ``` + lldb -- bazel-bin/test/lsp_test_runner --single_test=test/testdata/lsp/completion/alias_method.rb + ``` + + This is the easiest way to debug LSP things, because you can set breakpoints + inside the code that implements various LSP methods without having to click + in the UI to initiate those requests. + +- The protocol tests. + + These files live in `test/lsp/*protocol_test_corpus.cc` (there are only a + handful of them). + + These tests are written in C++ and test low-level protocol that can't be + tested with plain Ruby files. That means things like "what if you get two + `initialize` requests" or "what if you get a very oddly specific sequence of + fast and slow path edits." + + These test are more work to write but sometimes your only option. + + **Tips:** + + - These tests are slow (some of them _really_ slow). You're almost never going + to want to run all of them. Run just the one you want with `--test_arg`: + + ``` + bazel test --config=dbg --test_output=all \ + //test/lsp:multithreaded_protocol_test_corpus \ + --test_arg=--test-case=CanPreemptSlowPathWithCodeAction + ``` + + - Only edit multithreaded_protocol_test_corpus in particular as a last resort. + This is one of the only places in Sorbet's test suite that uses threads, and + that means that unless you know what you're doing, it can be very flaky. + (An example of when to use this: when it's specifically the concurrency / + sequencing that's going wrong in LSP.) + +### By trying it in a client + +There are two quick-and-dirty sandbox configurations that you can use for +testing a custom build of the language server in a real client: + +- `test/sandbox/vscode/` +- `test/sandbox/nvim/` + +Go into either of those directories, read the respective `README.md` in the +folder, and it will tell you how to launch the editor. All the configurations +will use `../../../bazel-bin/main/sorbet` to launch the language server, so +whatever Sorbet you most recently built will be used. + +**Tips**: + +- By default, both of those are configured to drop a Sorbet debug log to the + current folder. In the log you will find the complete trace of the JSON RPC + methods sent between the client and server, as well as other debug logs. + +- You can edit the language server launch settings to include `-vvv`, which will + raise the log level to trace, which will show you literally every log (can be + noisy, sometimes useful). + +- You can attach a debugger to a running LSP server launched this way. Find its + PID and then use `lldb -p `. + + I use [this zsh config][fzf-lldb] so that I can simply do `lldb -p **` + and then use fzf to find the relevant process ID, rather than having to find + it in `ps` or `htop` and copy it over. + +- If the thing you're trying to test happens at startup, you can pass the + `--wait-for-dbg` flag to the LSP command line args. Sorbet will hang + indefinitely until it detects that a debugger has attached (like with `lldb + -p`). It will then break, where you can set breakpoints and then `continue` to + resume normal execution. + +[fzf-lldb]: https://github.com/jez/dotfiles/blob/master/util/lldb.zsh + +## Working on the VS Code client + +**Tips:** + +- Both mechanisms below set the log level to trace, so you will be able to see + all logs. + +- You can work on both the VS Code extension and the Sorbet language server at + the same time: simply close whatever workspace might be open in the VS Code + window and then "Open Folder" to the `test/sandbox/vscode/` folder discussed + above. (Be sure that the "Sorbet (path)" configuration is selected.) + + +### If you want to use VS Code to edit the extension + +``` +cd vscode_extension +code --new-window . +``` + +This will open a new workspace for the VS Code extension sources. + +Edit the code as you see fit, then to test your changes: + +- Find the "Run and Debug" window +- Ensure that "Launch Extension" is selected in the dropdown +- Click the green triangle, which will launch a new VS Code instance with the + local copy of the extension loaded +- Navigate to the project you want to test Sorbet in + +Or in one command: ⇧⌘P > `Debug: Select and Start Debugging` > `Launch Extension` + +This will launch a separate VS Code window, with the newly-compiled extension +sources. The nice thing is that you can use the first VS Code window to set +breakpoints, and they will get hit when you interact with the second VS Code +window. + +If you want to use print debugging, the log level will be set to `trace` so all +logs should show up. + +### If you want to use another editor + +The "Launch Extension" workflow described in the previous section can be +achieved with the command line as well: + +```bash +cd vscode_extension + +# Start watching TypeScript files for changes, recompiling as necessary. +yarn watch + +# ... alternatively, to run once without watching: +yarn compile + +# Launch a new VS Code window with the newly-compiled extension sources +yarn launch ../test/sandbox/vscode +``` + diff --git a/docs/pipeline.md b/docs/pipeline.md index 7ddd55e0c7..b182375993 100644 --- a/docs/pipeline.md +++ b/docs/pipeline.md @@ -49,7 +49,7 @@ where what the phases of Sorbet actually do are documented. ▼ ┌─────────────────────────────────────────────────────────┐ │ ┌─────────────┐ │ - │ │ GlobalState │ resolve │ + │ │ GlobalState │ nameAndResolve │ │ └─────────────┘ │ ├─────────────────────────────────────────────────────────┤ │ ┌─────────────────┐ │ @@ -124,7 +124,7 @@ Some notes: about it amongst ourselves) into three big chunks: 1. index - 2. resolve + 2. nameAndResolve 3. typecheck - The "index" chunk works at the file level and involves largely syntactic @@ -163,12 +163,13 @@ Some notes: For more information on what's parallelized, see [Namer & Resolver Pipeline](namer-resolver-pipeline.md). - The sequential parts of resolve were originally designed to be whole-program, - which makes it tricky to make incremental. Our current trick is to separate - changes into "local changes" (fast path) and "non-local changes" (slow path). - It's easy to run resolve in a mode where it assumes it's on the fast path but - checks to see if it needs to take the slow path. The details are a bit hairy, - but you can see them if you look for `incrementalResolve` in the code. + The sequential parts of nameAndResolve were originally designed to be + whole-program, which makes it tricky to make incremental. Our current trick is + to separate changes into "local changes" (fast path) and "non-local changes" + (slow path). It's easy to run nameAndResolve in a mode where it assumes it's + on the fast path but checks to see if it needs to take the slow path. The + details are a bit hairy, but you can see them if you look for + `incrementalResolve` in the code. - The "typecheck" chunk operates on an immutable GlobalState. Because we don't do global type inference, type checking one method can't affect the result of diff --git a/docs/running-compiled-code.md b/docs/running-compiled-code.md deleted file mode 100644 index e68e0b57c6..0000000000 --- a/docs/running-compiled-code.md +++ /dev/null @@ -1,153 +0,0 @@ -# Running compiled code - -The instructions will help you compile and run ruby using the Sorbet Compiler on -Linux. - -## Building `sorbet_ruby` - -The Sorbet Compiler relies on additional runtime support that we provide by -linking it into the Ruby VM. As a result, it is not possible to run Sorbet -Compiler-generated artifacts in a stock Ruby VM. To build our custom VM, run the -following command: - -```shell -$ bazel build @sorbet_ruby_2_7_for_compiler//:ruby.tar.gz \ - --config=release-linux \ - --crosstool_top=@bazel_tools//tools/cpp:toolchain -``` - -The flags that we give are: - -- `--config=release-linux` - Build a release build. - - The resulting Ruby VM will include runtime assertions that prevent loading - compiled artifacts emitted by a different revision of Sorbet (based on Git - SHA). - -- `--crosstool_top=@bazel_tools//tools/cpp:toolchain` - Build using the native - C++ toolchain instead of the one that we manage in the Bazel sandbox. Without - this flag the Ruby build will be configured to use the C++ compiler that lives - in the bazel sandbox to build extensions with native code components, which - doesn't relocate well. - -Once this build has finished, the archive -`bazel-bin/external/sorbet_ruby_2_7_for_compiler/ruby.tar.gz` will contain the -build of Ruby 2.7 with our patches applied. Copy that TAR file out of the Bazel -tree and extract it in a scratchpad directory: - -```shell -$ mkdir scratchpad -$ cp bazel-bin/external/sorbet_ruby_2_7_for_compiler/ruby.tar.gz scratchpad -$ tar -xf scratchpad/ruby.tar.gz -C scratchpad -``` - -You can verify that it's doing the right thing by running: - -```shell -$ scratchpad/bin/ruby --version -ruby 2.7.2p137 (2020-10-01 revision 5445e04352) [x86_64-linux] -``` - - -## Building the Sorbet Compiler - -Now that you have a VM archive available, you can build a version of Sorbet that -is able to generate native code. - -```shell -$ bazel build //compiler:sorbet --config=release-linux -``` - -> NOTE: We're explicitly not passing the `--crosstool_top` flag for this build -> (like we did when building `sorbet_ruby`), as we want sorbet to us the version -> of Clang that we pin in the sandbox. - -This will take quite a while, as it will build Sorbet and LLVM 12. If you've -ever been looking for an excuse to upgrade your development machine, this is it. -Once it's complete, you will find a binary in `bazel-bin/compiler/sorbet` that -behaves exactly like Sorbet the type checker, but has a few additional -command-line flags available. Copy it into the scratchpad as well: - -```shell -$ mkdir -p scratchpad/bin -$ cp bazel-bin/compiler/sorbet scratchpad/bin -$ scratchpad/bin/sorbet --help -``` - -## Compiling some Ruby - -Let's compile a simple program to start with: - -```ruby -# typed: true -# frozen_string_literal: true -# compiled: true - -xs = [1, 2, 3, 4] - -xs.each do |x| - puts "Element: #{x}" -end - -puts xs -``` - -All of the sigils at the top of the file are required: - -- The Sorbet Compiler requires that files are at least `typed: true` because - otherwise Sorbet will not produce a typed CFG -- `frozen_string_literal: true` is required because of how we translate Ruby - String literals (for simplicity, we have only implemented frozen string - literals) -- `compiled: true` opts in to having the compiler emit a compiled artifact. (The - compiler will not compile all files in a project by default.) - -Place the Ruby program above in `test.rb`, and compile it with the following -commands: - -```shell -$ mkdir compiled_output -$ mkdir ir_output -$ scratchpad/bin/sorbet --compiled-out-dir=compiled_output --llvm-ir-dir=ir_output test.rb -No errors! Great job. -$ ls compiled_output -test.rb.so -$ ls ir_output -test.rb.ll test.rb.lowered.ll test.rb.opt.ll -``` - -The files produced have suffixes that correspond to different phases of -compilation: `.ll` is the initial LLVM IR before any optimizations are applied; -`.lowered.ll` is that tree with some fast optimizations applied; `.opt.ll` is -the final version of the LLVM IR with all optimizations applied; `.rb.so` is the -native artifact that can be run by the Ruby VM. - -## Running the compiled Ruby artifacts - -You will need to patch `Kernel#require` in order to run the ruby code compiled -in the previous step. First, copy the `test/patch_require.rb` into the -`scratchpad` directory: - -``` -$ cp test/patch_require.rb scratchpad/ -``` - -> If you'd like to read more about why `patch_require.rb` is necessary, the -> [compiler-caveats.md](compiler-caveats.md#runtime) document contains more of -> an explanation in the `Runtime` section. - -Now, assuming that your generated code is in the `output` directory from the -previous step, you can use the following command to run the compiled code: - -``` -$ llvmir="./output" force_compile=true scratchpad/bin/ruby -r ./scratchpad/patch_require.rb -e 'require "test.rb"' -SorbetLLVM using compiled: ./output/test.rb.so for test.rb -Element: 1 -Element: 2 -Element: 3 -Element: 4 -1 -2 -3 -4 -``` diff --git a/docs/scip-ruby/CONTRIBUTING.md b/docs/scip-ruby/CONTRIBUTING.md index 528e3e38f2..9e5af13b4e 100644 --- a/docs/scip-ruby/CONTRIBUTING.md +++ b/docs/scip-ruby/CONTRIBUTING.md @@ -262,7 +262,7 @@ Typically, I'll copy over the minimized code to the root and run: ```bash -./bazel build //main:scip-ruby --config=dbg && ./bazel-out/darwin-dbg/bin/main/scip-ruby tmp.rb -p cfg-text-loc --index-file /dev/null +./bazel build //main:scip-ruby --config=dbg && ./bazel-out/darwin_arm64-dbg/bin/main/scip-ruby tmp.rb -p cfg-text-loc --index-file /dev/null ``` Alternately, it may be useful to create a `tmp.rb` @@ -286,7 +286,7 @@ but the following works: ```bash export TEST_DIR="$PWD/test/scip/testdata" -pushd bazel-out/darwin-dbg/bin +pushd bazel-out/darwin_arm64-dbg/bin lldb -- ./test/scip_test_runner "$TEST_DIR/my_test.rb" --output="$TEST_DIR/my_test.snapshot.rb" popd unset TEST_DIR diff --git a/docs/suggest-sig.md b/docs/suggest-sig.md index 6b5b2b71a5..20143e038c 100644 --- a/docs/suggest-sig.md +++ b/docs/suggest-sig.md @@ -1,4 +1,17 @@ -# Suggesting Sigs +# [archived] Suggesting Sigs + +> **Note** The information in this doc is no longer relevant, as the option has +> been removed in current versions of Sorbet. It remains here as a reference for +> those using older Sorbet versions. +> +> In new Sorbet versions, this functionality has been replaced with [Untyped +> Blame](untyped-blame.md) tracking, which is a more powerful and precise +> mechanism to achieve a similar goal. +> +> The first version to remove the `--suggest-sig` flag was +> [0.5.10841](https://github.com/sorbet/sorbet/commit/f7b1eafdbb0cd4526c906b4c68d1063933964bba). + +- - - - - It's possible to build Sorbet in a way that it can suggest the "most impactful" methods to add a signature to, something like this: diff --git a/docs/untyped-blame.md b/docs/untyped-blame.md new file mode 100644 index 0000000000..fe3e6ecfbb --- /dev/null +++ b/docs/untyped-blame.md @@ -0,0 +1,219 @@ +# Blaming usages of untyped to definitions + +It's possible to build Sorbet in a way that it can "blame" usages of untyped +back to a definition (like a method or instance variable) which introduced the +usage of untyped in the first place. With this information, it becomes easier to +evaluate the cost vs benefit of improving the type annotations on any given +method. + +It requires building Sorbet with a special build configuration, because simply +adding the capability to blame untyped usages to definitions (regardless of +whether the user actually cares to use that capability) causes Sorbet to use +substantially more memory for certain internal data structures. + +Once built, it works something like this. Given an input file like this: + +```ruby +# typed: true + +def method_without_a_sig_1 = 0 +def method_without_a_sig_2 = 0 + +x = method_without_a_sig_1 +x.even? +x.even? + +y = method_without_a_sig_2 +y.even? +``` + +Sorbet can produce untyped blame output like this: + +``` +❯ sorbet --track-untyped --print=untyped-blame foo.rb +No errors! Great job. +[ + { + "path": "foo.rb", + "package": "", + "owner": "Object", + "name": "method_without_a_sig_2", + "count": 1 + }, + { + "path": "foo.rb", + "package": "", + "owner": "Object", + "name": "method_without_a_sig_1", + "count": 2 + } +] +``` + +## Building Sorbet with untyped blame support + +1. Follow the [Quickstart] instructions in Sorbet's README. + + These instructions will ensure that have a working build environment. If you + encounter problems attempting to build Sorbet, feel free to ask for help in + the #internals channel on the [Sorbet Slack](https://sorbet.org/slack). + +1. Build a release version of Sorbet with untyped blame support. The + instructions differ by platform. + + For Linux: + + ``` + ./bazel build //main:sorbet --config=release-linux --config=untyped-blame + ``` + + For macOS: + + ``` + ./bazel build //main:sorbet --config=release-mac --config=untyped-blame + ``` + +1. Test that the new build works: + + ``` + ❯ bazel-bin/main/sorbet --track-untyped --print=untyped-blame -e 'T.unsafe(nil).foo' + No errors! Great job. + [{"path":"https://github.com/sorbet/sorbet/tree/master/rbi/sorbet/t.rbi","package":"","owner":"T.class_of(T)","name":"unsafe","count":1}] + ``` + +[Quickstart]: https://github.com/sorbet/sorbet/#quickstart + +## Collecting untyped blame information + +Sorbet collects untyped blame information by type checking a project, and +dumping JSON information about untyped usages at the end. To use this new build +of Sorbet to type check a project, change into your project's directory and run +this command: + +```bash +SRB_SORBET_EXE=path/to/bazel-bin/main/sorbet bundle exec srb tc \ + --track-untyped --print=untyped-blame +``` + +**Note**: Replace `path/to/...` with the real path to the newly-built sorbet +binary. + +The untyped blame information will output to stdout. To redirect it to a file, +either use a Unix pipe, or use this form of the `--print` option, which will put +the print output directly into a file (in this case, it will create the +`/tmp/untyped-blame.json` file): + +```bash +SRB_SORBET_EXE=path/to/bazel-bin/main/sorbet bundle exec srb tc \ + --track-untyped --print=untyped-blame:/tmp/untyped-blame.json +``` + +## Interpreting the output + +The output will be a JSON array of JSON objects, where each object represents a +definition, alongside how many usages of untyped blame to that definition. + +Each entry has these keys: + +- `owner` + + The fully qualified name of the owner of the definition. For a method like + `A::B#foo`, the owner will be `A::B`. For a method like `A::B.foo`, the owner + will be `T.class_of(A::B)`. + +- `name` + + The name of the definition. For a method like `A::B#foo`, the name will be + `foo`. The name will also be `foo` for a method like `A::B.foo` (though the + `owner` will be different, see above). + +- `path` + + The path to the primary location of the definition. The primary location of + the definition is a Sorbet-internal heuristic that takes into account whether + the location is in an RBI file or not as well as the strictness level of that + file. + +- `count` + + The number of usages of untyped that blame to this definition. + +- `package` + + Will always be ``, unless using Stripe's internal Ruby packaging + system. In that case, `package` will be the name of the package that owns this + definition. + + +### Some special definitions + +There are some special definitions in the output that don't map to definitions +defined anywhere in code. + +- `owner = ::` + + Some usages of untyped arise because of `T.untyped` that arises internal to + Sorbet. For example, most existing limitation with [shape + types](https://sorbet.org/docs/shapes) arise because Sorbet will silently + introduce a usage of untyped. + + In these cases, there is not a specific definition Sorbet can blame the + untyped to, so Sorbet invents some synthetic definitions to attribute the + untyped. + + All these symbols live under the special owner `::`. + Some examples: + + - `name = ` + + Sorbet is not always able to infer a type for a call to `super`. When that + happens, Sorbet implicitly treats the `super` as a method that returns + untyped, and blames the untyped to this synthetic definition. + + Read more: [Why is `super` untyped?](https://sorbet.org/docs/faq#why-is-super-untyped-even-when-the-parent-method-has-a-sig) + + - `name = ` + + Sorbet does not infer types for `proc` and `lambda` literals the same way it + does for usages of blocks attached to method calls. When creating a `proc` + or `lambda` value, the inferred type will be a `T.proc` type where all the + params and return type of the proc are `T.untyped`. + +- `name = ` + + This represents usages of untyped in the program arising from undeclared + instance variables. + + In files below `# typed: strict`, Sorbet does not require instance variables + to be declared. As such, it is not always able to correlate a usage of an + instance variable with a certain name to a definition. When this happens, + Sorbet assumes that the instance variable corresponds to some hypothetical + instance variable whose type is `T.untyped`. It then blames the usage of + untyped to this synthetic `::` definition. + +- `name = ` + + Not all usages of untyped blame to a definition. Ideally, every use + of untyped would blame to either an internal, `::` + definition or to a definition in the codebase itself. + + But we have not audited all usages of untyped in Sorbet itself, so some usages + of untyped have not yet been annotated with a suitable + `::` blame. + + If you're interested in helping out, feel free to search Sorbet's codebase for + `Types::untypedUntracked` and convert them to usages of + `Types::untyped(definition)`. + + +## Automatically suggesting method signatures + +Having discovered which methods are the most important to typecheck, you may +want to attempt to add signatures to methods in bulk to drive down usage of +untyped. + +Sorbet has multiple tools for this, documented on +[sorbet.org](https://sorbet.org): + +→ [Automatically suggesting method signatures](https://sorbet.org/docs/sig-suggestion) + diff --git a/docs/variance-defaulting.md b/docs/variance-defaulting.md index 2265612aa0..a4308c5cd2 100644 --- a/docs/variance-defaulting.md +++ b/docs/variance-defaulting.md @@ -60,7 +60,7 @@ Having defaults for bare generic classes is _mostly_ a hack. In almost all cases, we would probably prefer the user to give us arguments. We don't want to see `Array`, we want `T::Array[SomeType]`. But there are some -complications we have to accomodate. +complications we have to accommodate. - Pre-Sorbet type annotations. diff --git a/emscripten/BUILD b/emscripten/BUILD index bc6621be1a..beb96196e2 100644 --- a/emscripten/BUILD +++ b/emscripten/BUILD @@ -1,51 +1,28 @@ -cc_binary( - name = "sorbet-wasm.tar", - linkopts = select({ - "//tools/config:webasm": [ - "-s", - "EXPORT_NAME=\"Sorbet\"", - "-s", - "MODULARIZE=1", - "-s", - "EXPORTED_FUNCTIONS=[\"_typecheck\",\"_lsp\"]", - "-s", - # - `addFunction` converts a JavaScript function to a C++ function - # pointer. It is used in the Sourcegraph extension - # https://sourcegraph.com/extensions/sourcegraph/lang-ruby - # to enable Sorbet to push JSON-RPC responses to JavaScript and - # avoid polling. This requires 1 reserved function pointer - # (see RESERVED_FUNCTION_POINTERS below). - # - `Pointer_stringify` converts a C++ char* to a JavaScript string. - "EXTRA_EXPORTED_RUNTIME_METHODS=[\"ccall\",\"cwrap\",\"addFunction\",\"Pointer_stringify\"]", - "-s", - "TOTAL_MEMORY=67108864", - "-s", - # This let's us allocate 1 JavaScript function (the callback for - # JSON-RPC responses) as a function pointer for use in C++ - # (see addFunction above). - "RESERVED_FUNCTION_POINTERS=1", - "-s", - "DISABLE_EXCEPTION_CATCHING=2", - ], - "//conditions:default": [], - }), - linkstatic = select({ - "//tools/config:linkshared": 0, - "//conditions:default": 1, - }), - visibility = ["//visibility:public"], - deps = [ - ":main", - ], -) +load("@emsdk//emscripten_toolchain:wasm_rules.bzl", "wasm_cc_binary") -cc_library( - name = "main", +BASE_LINKOPTS = [ + "-s", + "MODULARIZE=1", + "-s", + "EXPORT_NAME=Sorbet", + "-s", + "EXPORTED_RUNTIME_METHODS=ccall,cwrap,addFunction,UTF8ToString", + "-s", + "INITIAL_MEMORY=67108864", + # This let's us allocate 1 JavaScript function (the callback for + # JSON-RPC responses) as a function pointer for use in C++ + # (see addFunction above). + "-s", + "ALLOW_TABLE_GROWTH=1", +] + +cc_binary( + name = "sorbet-wasm", srcs = ["main.cc"], - linkstatic = select({ - "//tools/config:linkshared": 0, - "//conditions:default": 1, - }), + linkopts = BASE_LINKOPTS, + # This target won't build successfully on its own because of missing + # emscripten headers, etc. Therefore, we hide it from wildcards. + tags = ["manual"], visibility = ["//visibility:public"], deps = [ "//main:realmain", @@ -54,3 +31,9 @@ cc_library( "//payload/text:empty", ], ) + +wasm_cc_binary( + name = "sorbet-wasm.d", + cc_target = ":sorbet-wasm", + visibility = ["//visibility:public"], +) diff --git a/gems/sorbet-runtime/.rubocop.yml b/gems/sorbet-runtime/.rubocop.yml index 85b1848b0d..97bdec37a6 100644 --- a/gems/sorbet-runtime/.rubocop.yml +++ b/gems/sorbet-runtime/.rubocop.yml @@ -3,6 +3,8 @@ require: rubocop-performance AllCops: NewCops: disable + SuggestExtensions: false + TargetRubyVersion: 2.7 # Stripe in-house styles that are prevalent in the codebase already. @@ -84,6 +86,11 @@ Performance/CollectionLiteralInLoop: Exclude: # Ok to prioritize brevity over perf in tests - 'test/**/*' +Performance/BindCall: + Exclude: + # Only runs when `bind_call` is not built into the stdlib + - 'lib/types/private/methods/call_validation_2_6.rb' + - 'lib/types/private/methods/call_validation.rb' # We frequently care about the exact type of values, not just truthiness Style/DoubleNegation: @@ -109,3 +116,29 @@ Layout/ArgumentAlignment: # matters more than the readability of the result - 'lib/types/private/methods/call_validation_2_6.rb' - 'lib/types/private/methods/call_validation_2_7.rb' + +Layout/EmptyLinesAroundAttributeAccessor: + Enabled: false +Layout/EmptyLineBetweenDefs: + Enabled: false +Layout/RescueEnsureAlignment: + Enabled: false +Lint/ConstantDefinitionInBlock: + Enabled: false +Style/YodaCondition: + Enabled: false +Style/OptionalBooleanParameter: + Enabled: false +Style/MixinUsage: + Enabled: false +Style/CommentAnnotation: + Enabled: false +Naming/VariableNumber: + Enabled: false +Style/ClassEqualityComparison: + Enabled: false +Style/AccessorGrouping: + Enabled: false +Style/BisectedAttrAccessor: + Exclude: + - 'lib/types/private/methods/signature.rb' diff --git a/gems/sorbet-runtime/.rubocop_todo.yml b/gems/sorbet-runtime/.rubocop_todo.yml index d2c8b1d537..78c69c4dfc 100644 --- a/gems/sorbet-runtime/.rubocop_todo.yml +++ b/gems/sorbet-runtime/.rubocop_todo.yml @@ -126,7 +126,6 @@ Layout/MultilineMethodCallBraceLayout: # SupportedStyles: aligned, indented, indented_relative_to_receiver Layout/MultilineMethodCallIndentation: Exclude: - - 'lib/types/interface_wrapper.rb' - 'lib/types/props/custom_type.rb' - 'lib/types/props/decorator.rb' - 'lib/types/props/pretty_printable.rb' @@ -365,7 +364,6 @@ Style/RedundantSelf: Exclude: - 'lib/types/compatibility_patches.rb' - 'lib/types/enum.rb' - - 'lib/types/interface_wrapper.rb' - 'lib/types/private/abstract/data.rb' - 'lib/types/private/decl_state.rb' - 'lib/types/private/methods/modes.rb' diff --git a/gems/sorbet-runtime/Rakefile b/gems/sorbet-runtime/Rakefile index debc45199d..2830cce09f 100644 --- a/gems/sorbet-runtime/Rakefile +++ b/gems/sorbet-runtime/Rakefile @@ -25,9 +25,13 @@ task :test_vm_serde do require_tests end -require 'rubocop/rake_task' -RuboCop::RakeTask.new(:rubocop) do |t| - t.requires << 'rubocop-performance' +task :rubocop do + # For some reason, using RuboCop::RakeTask causes our test suite to run a + # bazillion times. I'm guessing that something about RuboCop::RakeTask is + # causing a file to be required that shouldn't be getting required? I have + # lost patience trying to debug it and am simply going to use a Subprocess. + require 'subprocess' + Subprocess.check_call(['rubocop']) end begin diff --git a/gems/sorbet-runtime/bench/enum.rb b/gems/sorbet-runtime/bench/enum.rb new file mode 100644 index 0000000000..5d49cfb698 --- /dev/null +++ b/gems/sorbet-runtime/bench/enum.rb @@ -0,0 +1,90 @@ +# frozen_string_literal: true +# typed: true + +require 'benchmark' + +require_relative '../lib/sorbet-runtime' + +module SorbetBenchmarks + module Enum + extend T::Sig + + class Example; end + + class MyEnum < T::Enum + enums do + X = new + Y = new + Z = new + end + end + + def self.time_block(name, iterations_of_block: 1_000_000, iterations_in_block: 2, &blk) + 1_000.times(&blk) # warmup + + GC.start + GC.disable + + t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC) + iterations_of_block.times(&blk) + duration_s = Process.clock_gettime(Process::CLOCK_MONOTONIC) - t0 + + GC.enable + + ns_per_iter = duration_s * 1_000_000_000 / (iterations_of_block * iterations_in_block) + duration_str = ns_per_iter >= 1000 ? "#{(ns_per_iter / 1000).round(3)} μs" : "#{ns_per_iter.round(3)} ns" + puts "#{name}: #{duration_str}" + end + + def self.all_comparisons + time_block("T::Enum == T::Enum", iterations_of_block: 100_000, iterations_in_block: 9) do + # rubocop:disable Lint/BinaryOperatorWithIdenticalOperands + # rubocop:disable Lint/Void + MyEnum::X == MyEnum::X + MyEnum::X == MyEnum::X + MyEnum::X == MyEnum::X + + MyEnum::Y == MyEnum::Y + MyEnum::Y == MyEnum::Y + MyEnum::Y == MyEnum::Y + + MyEnum::Z == MyEnum::Z + MyEnum::Z == MyEnum::Z + MyEnum::Z == MyEnum::Z + # rubocop:enable Lint/Void + # rubocop:enable Lint/BinaryOperatorWithIdenticalOperands + end + + time_block("T::Enum == String", iterations_of_block: 100_000, iterations_in_block: 9) do + # rubocop:disable Lint/Void + MyEnum::X == 'x' + MyEnum::X == 'x' + MyEnum::X == 'x' + + MyEnum::Y == 'y' + MyEnum::Y == 'y' + MyEnum::Y == 'y' + + MyEnum::Z == 'z' + MyEnum::Z == 'z' + MyEnum::Z == 'z' + # rubocop:enable Lint/Void + end + end + + def self.run + puts("before T::Configuration.enable_legacy_t_enum_migration_mode") + + all_comparisons + + T::Configuration.enable_legacy_t_enum_migration_mode + T::Configuration.soft_assert_handler = lambda do |str, extra| + # Empty + # (Not trying to benchmark the performance of the soft_assert_handler) + end + puts("\nafter T::Configuration.enable_legacy_t_enum_migration_mode") + + all_comparisons + end + end +end diff --git a/gems/sorbet-runtime/bench/tasks.rb b/gems/sorbet-runtime/bench/tasks.rb index 0d26fc44ba..131d6c1ffb 100644 --- a/gems/sorbet-runtime/bench/tasks.rb +++ b/gems/sorbet-runtime/bench/tasks.rb @@ -12,6 +12,7 @@ require_relative 'tutils' require_relative 'typecheck' require_relative 'typecheck_kwargs_splat' +require_relative 'enum' namespace :bench do task :getters do @@ -58,5 +59,9 @@ SorbetBenchmarks::TUtils.run end + task :enum do + SorbetBenchmarks::Enum.run + end + task all: %i[getters setters constructor deserialize prop_definition serialize_custom_type sigs tutils typecheck] end diff --git a/gems/sorbet-runtime/bench/typecheck.rb b/gems/sorbet-runtime/bench/typecheck.rb index 178e0219b9..2cf88cccda 100644 --- a/gems/sorbet-runtime/bench/typecheck.rb +++ b/gems/sorbet-runtime/bench/typecheck.rb @@ -166,6 +166,11 @@ def self.run application_class_param(example) end + time_block("sig {params(x: Example).returns(T.anything)}") do + application_class_param_returns_anything(example) + application_class_param_returns_anything(example) + end + time_block("T.let(..., T.nilable(Example))") do T.let(nil, T.nilable(Example)) T.let(example, T.nilable(Example)) @@ -176,6 +181,11 @@ def self.run nilable_application_class_param(example) end + time_block("sig {params(x: T.nilable(Example)).returns(T.anything)}") do + nilable_application_class_param_returns_anything(nil) + nilable_application_class_param_returns_anything(example) + end + time_block("sig {params(s: Symbol, x: Integer, y: Integer).void} (with kwargs)") do arg_plus_kwargs(:foo, x: 1, y: 2) arg_plus_kwargs(:bar, x: 1) @@ -188,8 +198,10 @@ def self.run class_method = Object.instance_method(:class) time_block(".bind(example).call Object#class") do + # rubocop:disable Performance/BindCall class_method.bind(example).call class_method.bind(example).call + # rubocop:enable Performance/BindCall end if T::Configuration::AT_LEAST_RUBY_2_7 @@ -220,9 +232,15 @@ def self.nilable_integer_param(x); end sig {params(x: Example).void} def self.application_class_param(x); end + sig {params(x: Example).returns(T.anything)} + def self.application_class_param_returns_anything(x); end + sig {params(x: T.nilable(Example)).void} def self.nilable_application_class_param(x); end + sig {params(x: T.nilable(Example)).returns(T.anything)} + def self.nilable_application_class_param_returns_anything(x); end + sig {params(s: Symbol, x: Integer, y: Integer).void} def self.arg_plus_kwargs(s, x:, y: 0); end end diff --git a/gems/sorbet-runtime/lib/sorbet-runtime.rb b/gems/sorbet-runtime/lib/sorbet-runtime.rb index e997da431f..3c8cad6009 100644 --- a/gems/sorbet-runtime/lib/sorbet-runtime.rb +++ b/gems/sorbet-runtime/lib/sorbet-runtime.rb @@ -19,6 +19,7 @@ module T::Private::Types; end require_relative 'types/configuration' require_relative 'types/_types' require_relative 'types/private/decl_state' +require_relative 'types/private/caller_utils' require_relative 'types/private/class_utils' require_relative 'types/private/runtime_levels' require_relative 'types/private/methods/_methods' @@ -73,7 +74,6 @@ module T::Private::Types; end # Catch all. Sort of built by `cd extn; find types -type f | grep -v test | sort` require_relative 'types/generic' -require_relative 'types/interface_wrapper' require_relative 'types/private/abstract/declare' require_relative 'types/private/abstract/hooks' require_relative 'types/private/casts' @@ -117,6 +117,3 @@ module T::Private::Types; end require_relative 'types/non_forcing_constants' require_relative 'types/compatibility_patches' - -# Sorbet Compiler support module -require_relative 'types/private/compiler' diff --git a/gems/sorbet-runtime/lib/types/compatibility_patches.rb b/gems/sorbet-runtime/lib/types/compatibility_patches.rb index 028b292995..17e46ea3ae 100644 --- a/gems/sorbet-runtime/lib/types/compatibility_patches.rb +++ b/gems/sorbet-runtime/lib/types/compatibility_patches.rb @@ -38,7 +38,7 @@ def observe!(method_name) module MethodDoubleExtensions def initialize(object, method_name, proxy) - if ::Kernel.instance_method(:respond_to?).bind(object).call(method_name, true) + if ::Kernel.instance_method(:respond_to?).bind(object).call(method_name, true) # rubocop:disable Performance/BindCall method = ::RSpec::Support.method_handle_for(object, method_name) T::Private::Methods.maybe_run_sig_block_for_method(method) end diff --git a/gems/sorbet-runtime/lib/types/configuration.rb b/gems/sorbet-runtime/lib/types/configuration.rb index c012b6e8b6..d598fa4e7e 100644 --- a/gems/sorbet-runtime/lib/types/configuration.rb +++ b/gems/sorbet-runtime/lib/types/configuration.rb @@ -113,7 +113,7 @@ def self.disable_vm_prop_serde # statically, so that methods don't have to guard themselves from being # called incorrectly by untyped code. # - # @param [:never, :compiled, :tests, :always] default_checked_level + # @param [:never, :tests, :always] default_checked_level def self.default_checked_level=(default_checked_level) T::Private::RuntimeLevels.default_checked_level = default_checked_level end @@ -451,7 +451,7 @@ def self.scalar_types @default_module_name_mangler = if T::Configuration::AT_LEAST_RUBY_2_7 ->(type) {MODULE_NAME.bind_call(type)} else - ->(type) {MODULE_NAME.bind(type).call} + ->(type) {MODULE_NAME.bind(type).call} # rubocop:disable Performance/BindCall end @module_name_mangler = nil @@ -536,6 +536,7 @@ def self.without_ruby_warnings @legacy_t_enum_migration_mode = false def self.enable_legacy_t_enum_migration_mode + T::Enum.include(T::Enum::LegacyMigrationMode) @legacy_t_enum_migration_mode = true end def self.disable_legacy_t_enum_migration_mode diff --git a/gems/sorbet-runtime/lib/types/enum.rb b/gems/sorbet-runtime/lib/types/enum.rb index 381152636a..b575cc2a86 100644 --- a/gems/sorbet-runtime/lib/types/enum.rb +++ b/gems/sorbet-runtime/lib/types/enum.rb @@ -192,62 +192,83 @@ def <=>(other) # responds to the `to_str` method. It does not actually call `to_str` however. # # See https://ruby-doc.org/core-2.4.0/String.html#method-i-3D-3D - sig {returns(String)} + T::Sig::WithoutRuntime.sig {returns(String)} def to_str msg = 'Implicit conversion of Enum instances to strings is not allowed. Call #serialize instead.' if T::Configuration.legacy_t_enum_migration_mode? T::Configuration.soft_assert_handler( msg, - storytime: {class: self.class.name}, + storytime: { + class: self.class.name, + caller_location: Kernel.caller_locations(1..1)&.[](0)&.then {"#{_1.path}:#{_1.lineno}"}, + }, ) serialize.to_s else - raise NoMethodError.new(msg) + Kernel.raise NoMethodError.new(msg) end end - sig {params(other: BasicObject).returns(T::Boolean).checked(:never)} - def ==(other) - case other - when String - if T::Configuration.legacy_t_enum_migration_mode? - comparison_assertion_failed(:==, other) - self.serialize == other + module LegacyMigrationMode + include Kernel + extend T::Helpers + abstract! + + if T.unsafe(false) + # Declare to the type system that the `serialize` method for sure exists + # on whatever we mix this into. + T::Sig::WithoutRuntime.sig {abstract.returns(T.untyped)} + def serialize; end + end + + # WithoutRuntime so that comparison_assertion_failed can assume a constant stack depth + T::Sig::WithoutRuntime.sig {params(other: BasicObject).returns(T::Boolean)} + def ==(other) + case other + when String + if T::Configuration.legacy_t_enum_migration_mode? + comparison_assertion_failed(:==, other) + self.serialize == other + else + false + end else - false + super(other) end - else - super(other) end - end - sig {params(other: BasicObject).returns(T::Boolean).checked(:never)} - def ===(other) - case other - when String - if T::Configuration.legacy_t_enum_migration_mode? - comparison_assertion_failed(:===, other) - self.serialize == other + # WithoutRuntime so that comparison_assertion_failed can assume a constant stack depth + T::Sig::WithoutRuntime.sig {params(other: BasicObject).returns(T::Boolean)} + def ===(other) + case other + when String + if T::Configuration.legacy_t_enum_migration_mode? + comparison_assertion_failed(:===, other) + self.serialize == other + else + false + end else - false + super(other) end - else - super(other) end - end - sig {params(method: Symbol, other: T.untyped).void} - private def comparison_assertion_failed(method, other) - T::Configuration.soft_assert_handler( - 'Enum to string comparison not allowed. Compare to the Enum instance directly instead. See go/enum-migration', - storytime: { - class: self.class.name, - self: self.inspect, - other: other, - other_class: other.class.name, - method: method, - } - ) + # WithoutRuntime so that caller_locations can assume a constant stack depth + # (Otherwise, the first call would be the method with the wrapping, which would have a different stack depth.) + T::Sig::WithoutRuntime.sig {params(method: Symbol, other: T.untyped).void} + private def comparison_assertion_failed(method, other) + T::Configuration.soft_assert_handler( + 'Enum to string comparison not allowed. Compare to the Enum instance directly instead. See go/enum-migration', + storytime: { + class: self.class.name, + self: self.inspect, + other: other, + other_class: other.class.name, + method: method, + caller_location: Kernel.caller_locations(2..2)&.[](0)&.then {"#{_1.path}:#{_1.lineno}"}, + } + ) + end end ### Private implementation ### @@ -357,7 +378,7 @@ def self.enums(&blk) @fully_initialized = true end - sig {params(child_class: Module).void} + sig {params(child_class: T::Class[T.anything]).void} def self.inherited(child_class) super diff --git a/gems/sorbet-runtime/lib/types/interface_wrapper.rb b/gems/sorbet-runtime/lib/types/interface_wrapper.rb deleted file mode 100644 index 580d4b0df5..0000000000 --- a/gems/sorbet-runtime/lib/types/interface_wrapper.rb +++ /dev/null @@ -1,162 +0,0 @@ -# frozen_string_literal: true -# typed: false - -# Wraps an object, exposing only the methods defined on a given class/module. The idea is that, in -# the absence of a static type checker that would prevent you from calling non-Bar methods on a -# variable of type Bar, we can use these wrappers as a way of enforcing it at runtime. -# -# Once we ship static type checking, we should get rid of this entirely. -class T::InterfaceWrapper - extend T::Sig - - module Helpers - def wrap_instance(obj) - T::InterfaceWrapper.wrap_instance(obj, self) - end - - def wrap_instances(arr) - T::InterfaceWrapper.wrap_instances(arr, self) - end - end - - private_class_method :new # use `wrap_instance` - - def self.wrap_instance(obj, interface_mod) - wrapper = wrapped_dynamic_cast(obj, interface_mod) - if wrapper.nil? - raise "#{obj.class} cannot be cast to #{interface_mod}" - end - wrapper - end - - sig do - params( - arr: Array, - interface_mod: T.untyped - ) - .returns(Array) - end - def self.wrap_instances(arr, interface_mod) - arr.map {|instance| self.wrap_instance(instance, interface_mod)} - end - - def initialize(target_obj, interface_mod) - if target_obj.is_a?(T::InterfaceWrapper) - # wrapped_dynamic_cast should guarantee this never happens. - raise "Unexpected: wrapping a wrapper. Please report this bug at https://github.com/sorbet/sorbet/issues" - end - - if !target_obj.is_a?(interface_mod) - # wrapped_dynamic_cast should guarantee this never happens. - raise "Unexpected: `is_a?` failed. Please report this bug at https://github.com/sorbet/sorbet/issues" - end - - if target_obj.class == interface_mod - # wrapped_dynamic_cast should guarantee this never happens. - raise "Unexpected: exact class match. Please report this bug at https://github.com/sorbet/sorbet/issues" - end - - @target_obj = target_obj - @interface_mod = interface_mod - self_methods = self.class.self_methods - - # If perf becomes an issue, we can define these on an anonymous subclass, and keep a cache - # so we only need to do it once per unique `interface_mod` - T::Utils.methods_excluding_object(interface_mod).each do |method_name| - if self_methods.include?(method_name) - raise "interface_mod has a method that conflicts with #{self.class}: #{method_name}" - end - - define_singleton_method(method_name) do |*args, &blk| - target_obj.send(method_name, *args, &blk) - end - - if singleton_class.respond_to?(:ruby2_keywords, true) - singleton_class.send(:ruby2_keywords, method_name) - end - - if target_obj.singleton_class.public_method_defined?(method_name) - # no-op, it's already public - elsif target_obj.singleton_class.protected_method_defined?(method_name) - singleton_class.send(:protected, method_name) - elsif target_obj.singleton_class.private_method_defined?(method_name) - singleton_class.send(:private, method_name) - else - raise "This should never happen. Report this bug at https://github.com/sorbet/sorbet/issues" - end - end - end - - def kind_of?(other) - is_a?(other) - end - - def is_a?(other) - if !other.is_a?(Module) - raise TypeError.new("class or module required") - end - - # This makes is_a? return true for T::InterfaceWrapper (and its ancestors), - # as well as for @interface_mod and its ancestors. - self.class <= other || @interface_mod <= other - end - - # Prefixed because we're polluting the namespace of the interface we're wrapping, and we don't - # want anyone else (besides dynamic_cast) calling it. - def __target_obj_DO_NOT_USE # rubocop:disable Naming/MethodName - @target_obj - end - - # Prefixed because we're polluting the namespace of the interface we're wrapping, and we don't - # want anyone else (besides wrapped_dynamic_cast) calling it. - def __interface_mod_DO_NOT_USE # rubocop:disable Naming/MethodName - @interface_mod - end - - # "Cast" an object to another type. If `obj` is an InterfaceWrapper, returns the the wrapped - # object if that matches `type`. Otherwise, returns `obj` if it matches `type`. Otherwise, - # returns nil. - # - # @param obj [Object] object to cast - # @param mod [Module] type to cast `obj` to - # - # @example - # if (impl = T::InterfaceWrapper.dynamic_cast(iface, MyImplementation)) - # impl.do_things - # end - def self.dynamic_cast(obj, mod) - if obj.is_a?(T::InterfaceWrapper) - target_obj = obj.__target_obj_DO_NOT_USE - target_obj.is_a?(mod) ? target_obj : nil - elsif obj.is_a?(mod) - obj - else - nil - end - end - - # Like dynamic_cast, but puts the result in its own wrapper if necessary. - # - # @param obj [Object] object to cast - # @param mod [Module] type to cast `obj` to - def self.wrapped_dynamic_cast(obj, mod) - # Avoid unwrapping and creating an equivalent wrapper. - if obj.is_a?(T::InterfaceWrapper) && obj.__interface_mod_DO_NOT_USE == mod - return obj - end - - cast_obj = dynamic_cast(obj, mod) - if cast_obj.nil? - nil - elsif cast_obj.class == mod - # Nothing to wrap, they want the full class - cast_obj - else - new(cast_obj, mod) - end - end - - def self.self_methods - @self_methods ||= self.instance_methods(false).to_set - end -end diff --git a/gems/sorbet-runtime/lib/types/non_forcing_constants.rb b/gems/sorbet-runtime/lib/types/non_forcing_constants.rb index 96445357cb..0459b92610 100644 --- a/gems/sorbet-runtime/lib/types/non_forcing_constants.rb +++ b/gems/sorbet-runtime/lib/types/non_forcing_constants.rb @@ -4,20 +4,13 @@ module T::NonForcingConstants # NOTE: This method is documented on the RBI in Sorbet's payload, so that it # shows up in the hover/completion documentation via LSP. - T::Sig::WithoutRuntime.sig {params(val: BasicObject, klass: String, package: T.nilable(String)).returns(T::Boolean)} - def self.non_forcing_is_a?(val, klass, package: nil) + T::Sig::WithoutRuntime.sig {params(val: BasicObject, klass: String).returns(T::Boolean)} + def self.non_forcing_is_a?(val, klass) method_name = "T::NonForcingConstants.non_forcing_is_a?" if klass.empty? raise ArgumentError.new("The string given to `#{method_name}` must not be empty") end - # We don't treat packages differently at runtime, but the static - # type-checker still needs to have the package and constant - # separated out. This just re-assembles the string as needed - if !package.nil? - klass = "::#{package}::#{klass}" - end - current_klass = T.let(nil, T.nilable(Module)) current_prefix = T.let(nil, T.nilable(String)) @@ -25,10 +18,7 @@ def self.non_forcing_is_a?(val, klass, package: nil) parts.each do |part| if current_klass.nil? # First iteration - if part != "" && package.nil? - # if we've supplied a package, we're probably running in - # package mode, which means absolute references are - # meaningless + if part != "" raise ArgumentError.new("The string given to `#{method_name}` must be an absolute constant reference that starts with `::`") end @@ -37,7 +27,7 @@ def self.non_forcing_is_a?(val, klass, package: nil) # if this had a :: prefix, then there's no more loading to # do---skip to the next one - next if part == "" + next end if current_klass.autoload?(part) diff --git a/gems/sorbet-runtime/lib/types/private/abstract/declare.rb b/gems/sorbet-runtime/lib/types/private/abstract/declare.rb index b1159ca47f..4ebee4a11f 100644 --- a/gems/sorbet-runtime/lib/types/private/abstract/declare.rb +++ b/gems/sorbet-runtime/lib/types/private/abstract/declare.rb @@ -18,7 +18,6 @@ def self.declare_abstract(mod, type:) Abstract::Data.set(mod, :abstract_type, type) mod.extend(Abstract::Hooks) - mod.extend(T::InterfaceWrapper::Helpers) if mod.is_a?(Class) if type == :interface @@ -35,11 +34,11 @@ def self.declare_abstract(mod, type:) # define_method because of the guard above mod.send(:define_singleton_method, :new) do |*args, &blk| - super(*args, &blk).tap do |result| - if result.instance_of?(mod) - raise "#{mod} is declared as abstract; it cannot be instantiated" - end + result = super(*args, &blk) + if result.instance_of?(mod) + raise "#{mod} is declared as abstract; it cannot be instantiated" end + result end # Ruby doesn not emit "method redefined" warnings for aliased methods diff --git a/gems/sorbet-runtime/lib/types/private/caller_utils.rb b/gems/sorbet-runtime/lib/types/private/caller_utils.rb new file mode 100644 index 0000000000..a84c3ab37d --- /dev/null +++ b/gems/sorbet-runtime/lib/types/private/caller_utils.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true +# typed: false + +module T::Private::CallerUtils + if Thread.respond_to?(:each_caller_location) # RUBY_VERSION >= "3.2" + def self.find_caller + skipped_first = false + Thread.each_caller_location do |loc| + unless skipped_first + skipped_first = true + next + end + + next if loc.path&.start_with?("" diff --git a/gems/sorbet-runtime/lib/types/private/types/string_holder.rb b/gems/sorbet-runtime/lib/types/private/types/string_holder.rb index b2295f7730..a3bcbe63c9 100644 --- a/gems/sorbet-runtime/lib/types/private/types/string_holder.rb +++ b/gems/sorbet-runtime/lib/types/private/types/string_holder.rb @@ -9,6 +9,10 @@ def initialize(string) @string = string end + def build_type + nil + end + # overrides Base def name string diff --git a/gems/sorbet-runtime/lib/types/private/types/type_alias.rb b/gems/sorbet-runtime/lib/types/private/types/type_alias.rb index de805c9659..dcb03b8de2 100644 --- a/gems/sorbet-runtime/lib/types/private/types/type_alias.rb +++ b/gems/sorbet-runtime/lib/types/private/types/type_alias.rb @@ -9,6 +9,10 @@ def initialize(callable) @callable = callable end + def build_type + nil + end + def aliased_type @aliased_type ||= T::Utils.coerce(@callable.call) end diff --git a/gems/sorbet-runtime/lib/types/private/types/void.rb b/gems/sorbet-runtime/lib/types/private/types/void.rb index f1a65e0b6d..3208a68ead 100644 --- a/gems/sorbet-runtime/lib/types/private/types/void.rb +++ b/gems/sorbet-runtime/lib/types/private/types/void.rb @@ -18,6 +18,10 @@ module VOID freeze end + def build_type + nil + end + # overrides Base def name "" diff --git a/gems/sorbet-runtime/lib/types/props/_props.rb b/gems/sorbet-runtime/lib/types/props/_props.rb index 8d81d30822..420e1d2651 100644 --- a/gems/sorbet-runtime/lib/types/props/_props.rb +++ b/gems/sorbet-runtime/lib/types/props/_props.rb @@ -110,7 +110,7 @@ def reload_decorator! # # @return [void] sig {params(name: Symbol, cls: T.untyped, rules: T.untyped).void} - def prop(name, cls, rules={}) + def prop(name, cls, **rules) cls = T::Utils.coerce(cls) if !cls.is_a?(Module) decorator.prop_defined(name, cls, rules) end @@ -132,8 +132,8 @@ def plugin(mod) end # Shorthand helper to define a `prop` with `immutable => true` - sig {params(name: Symbol, cls_or_args: T.untyped, args: T::Hash[Symbol, T.untyped]).void} - def const(name, cls_or_args, args={}) + sig {params(name: Symbol, cls_or_args: T.untyped, args: T.untyped).void} + def const(name, cls_or_args, **args) if (cls_or_args.is_a?(Hash) && cls_or_args.key?(:immutable)) || args.key?(:immutable) Kernel.raise ArgumentError.new("Cannot pass 'immutable' argument when using 'const' keyword to define a prop") end diff --git a/gems/sorbet-runtime/lib/types/props/decorator.rb b/gems/sorbet-runtime/lib/types/props/decorator.rb index c55b8a6f94..d22facff74 100644 --- a/gems/sorbet-runtime/lib/types/props/decorator.rb +++ b/gems/sorbet-runtime/lib/types/props/decorator.rb @@ -100,10 +100,7 @@ def decorated_class # checked(:never) - potentially O(prop accesses) depending on usage pattern sig {params(prop: Symbol, val: T.untyped).void.checked(:never)} def validate_prop_value(prop, val) - # We call `setter_proc` here without binding to an instance, so it'll run - # `instance_variable_set` if validation passes, but nothing will care. - # We only care about the validation. - prop_rules(prop).fetch(:setter_proc).call(val) + prop_rules(prop).fetch(:value_validate_proc).call(val) end # For performance, don't use named params here. @@ -296,7 +293,7 @@ def prop_validate_definition!(name, cls, rules, type) cls: PropTypeOrClass, rules: Rules, ) - .void + .returns(T.anything) .checked(:never) end private def prop_nilable?(cls, rules) @@ -335,7 +332,7 @@ def prop_defined(name, cls, rules={}) prop_validate_definition!(name, cls, rules, type_object) - # Retrive the possible underlying object with T.nilable. + # Retrieve the possible underlying object with T.nilable. type = T::Utils::Nilable.get_underlying_type(type) rules_sensitivity = rules[:sensitivity] @@ -378,7 +375,11 @@ def prop_defined(name, cls, rules={}) end end - rules[:setter_proc] = T::Props::Private::SetterFactory.build_setter_proc(@class, name, rules).freeze + setter_proc, value_validate_proc = T::Props::Private::SetterFactory.build_setter_proc(@class, name, rules) + setter_proc.freeze + value_validate_proc.freeze + rules[:setter_proc] = setter_proc + rules[:value_validate_proc] = value_validate_proc add_prop_definition(name, rules) diff --git a/gems/sorbet-runtime/lib/types/props/has_lazily_specialized_methods.rb b/gems/sorbet-runtime/lib/types/props/has_lazily_specialized_methods.rb index 46138def3e..a165fc61f7 100644 --- a/gems/sorbet-runtime/lib/types/props/has_lazily_specialized_methods.rb +++ b/gems/sorbet-runtime/lib/types/props/has_lazily_specialized_methods.rb @@ -117,7 +117,9 @@ module DecoratorMethods def eagerly_define_lazy_methods! return if lazily_defined_methods.empty? + # rubocop:disable Style/StringConcatenation source = "# frozen_string_literal: true\n" + lazily_defined_methods.values.map(&:call).map(&:to_s).join("\n\n") + # rubocop:enable Style/StringConcatenation cls = decorated_class cls.class_eval(source) diff --git a/gems/sorbet-runtime/lib/types/props/pretty_printable.rb b/gems/sorbet-runtime/lib/types/props/pretty_printable.rb index 496fca8d1d..8a472ff4be 100644 --- a/gems/sorbet-runtime/lib/types/props/pretty_printable.rb +++ b/gems/sorbet-runtime/lib/types/props/pretty_printable.rb @@ -7,13 +7,13 @@ module T::Props::PrettyPrintable # Override the PP gem with something that's similar, but gives us a hook to do redaction and customization def pretty_print(pp) - clazz = T.unsafe(T.cast(self, Object).class).decorator + klass = T.unsafe(T.cast(self, Object).class).decorator multiline = pp.is_a?(PP) - pp.group(1, "<#{clazz.inspect_class_with_decoration(self)}", ">") do - clazz.all_props.sort.each do |prop| + pp.group(1, "<#{klass.inspect_class_with_decoration(self)}", ">") do + klass.all_props.sort.each do |prop| pp.breakable - val = clazz.get(self, prop) - rules = clazz.prop_rules(prop) + val = klass.get(self, prop) + rules = klass.prop_rules(prop) pp.text("#{prop}=") if (custom_inspect = rules[:inspect]) inspected = if T::Utils.arity(custom_inspect) == 1 @@ -28,7 +28,7 @@ def pretty_print(pp) val.pretty_print(pp) end end - clazz.pretty_print_extra(self, pp) + klass.pretty_print_extra(self, pp) end end @@ -55,7 +55,7 @@ def valid_rule_key?(key) end # Overridable method to specify how the first part of a `pretty_print`d object's class should look like - # NOTE: This is just to support Stripe's `PrettyPrintableModel` case, and not recommended to be overriden + # NOTE: This is just to support Stripe's `PrettyPrintableModel` case, and not recommended to be overridden sig {params(instance: T::Props::PrettyPrintable).returns(String)} def inspect_class_with_decoration(instance) T.unsafe(instance).class.to_s diff --git a/gems/sorbet-runtime/lib/types/props/private/deserializer_generator.rb b/gems/sorbet-runtime/lib/types/props/private/deserializer_generator.rb index 2393e1a284..8a2f6858be 100644 --- a/gems/sorbet-runtime/lib/types/props/private/deserializer_generator.rb +++ b/gems/sorbet-runtime/lib/types/props/private/deserializer_generator.rb @@ -137,7 +137,10 @@ def __t_props_generated_deserialize(hash) when ApplyPrimitiveDefault literal = default.default case literal - when String, Integer, Symbol, Float, TrueClass, FalseClass, NilClass + # `Float` is intentionally left out here because `.inspect` does not produce the correct code + # representation for non-finite values like `Float::INFINITY` and `Float::NAN` and it's not totally + # clear that it won't cause issues with floating point precision. + when String, Integer, Symbol, TrueClass, FalseClass, NilClass literal.inspect else "self.class.decorator.props_with_defaults.fetch(#{prop.inspect}).default" diff --git a/gems/sorbet-runtime/lib/types/props/private/setter_factory.rb b/gems/sorbet-runtime/lib/types/props/private/setter_factory.rb index 00c76ed1e7..95ff986300 100644 --- a/gems/sorbet-runtime/lib/types/props/private/setter_factory.rb +++ b/gems/sorbet-runtime/lib/types/props/private/setter_factory.rb @@ -7,6 +7,7 @@ module SetterFactory extend T::Sig SetterProc = T.type_alias {T.proc.params(val: T.untyped).void} + ValueValidationProc = T.type_alias {T.proc.params(val: T.untyped).void} ValidateProc = T.type_alias {T.proc.params(prop: Symbol, value: T.untyped).void} sig do @@ -15,7 +16,7 @@ module SetterFactory prop: Symbol, rules: T::Hash[Symbol, T.untyped] ) - .returns(SetterProc) + .returns([SetterProc, ValueValidationProc]) .checked(:never) end def self.build_setter_proc(klass, prop, rules) @@ -55,20 +56,32 @@ def self.build_setter_proc(klass, prop, rules) non_nil_type: Module, klass: T.all(Module, T::Props::ClassMethods), ) - .returns(SetterProc) + .returns([SetterProc, ValueValidationProc]) end private_class_method def self.simple_non_nil_proc(prop, accessor_key, non_nil_type, klass) - proc do |val| - unless val.is_a?(non_nil_type) - T::Props::Private::SetterFactory.raise_pretty_error( - klass, - prop, - T::Utils.coerce(non_nil_type), - val, - ) - end - instance_variable_set(accessor_key, val) - end + [ + proc do |val| + unless val.is_a?(non_nil_type) + T::Props::Private::SetterFactory.raise_pretty_error( + klass, + prop, + T::Utils.coerce(non_nil_type), + val, + ) + end + instance_variable_set(accessor_key, val) + end, + proc do |val| + unless val.is_a?(non_nil_type) + T::Props::Private::SetterFactory.raise_pretty_error( + klass, + prop, + T::Utils.coerce(non_nil_type), + val, + ) + end + end, + ] end sig do @@ -79,27 +92,46 @@ def self.build_setter_proc(klass, prop, rules) klass: T.all(Module, T::Props::ClassMethods), validate: T.nilable(ValidateProc) ) - .returns(SetterProc) + .returns([SetterProc, ValueValidationProc]) end private_class_method def self.non_nil_proc(prop, accessor_key, non_nil_type, klass, validate) - proc do |val| - # this use of recursively_valid? is intentional: unlike for - # methods, we want to make sure data at the 'edge' - # (e.g. models that go into databases or structs serialized - # from disk) are correct, so we use more thorough runtime - # checks there - if non_nil_type.recursively_valid?(val) - validate&.call(prop, val) - else - T::Props::Private::SetterFactory.raise_pretty_error( - klass, - prop, - non_nil_type, - val, - ) - end - instance_variable_set(accessor_key, val) - end + [ + proc do |val| + # this use of recursively_valid? is intentional: unlike for + # methods, we want to make sure data at the 'edge' + # (e.g. models that go into databases or structs serialized + # from disk) are correct, so we use more thorough runtime + # checks there + if non_nil_type.recursively_valid?(val) + validate&.call(prop, val) + else + T::Props::Private::SetterFactory.raise_pretty_error( + klass, + prop, + non_nil_type, + val, + ) + end + instance_variable_set(accessor_key, val) + end, + proc do |val| + # this use of recursively_valid? is intentional: unlike for + # methods, we want to make sure data at the 'edge' + # (e.g. models that go into databases or structs serialized + # from disk) are correct, so we use more thorough runtime + # checks there + if non_nil_type.recursively_valid?(val) + validate&.call(prop, val) + else + T::Props::Private::SetterFactory.raise_pretty_error( + klass, + prop, + non_nil_type, + val, + ) + end + end, + ] end sig do @@ -109,24 +141,32 @@ def self.build_setter_proc(klass, prop, rules) non_nil_type: Module, klass: T.all(Module, T::Props::ClassMethods), ) - .returns(SetterProc) + .returns([SetterProc, ValueValidationProc]) end private_class_method def self.simple_nilable_proc(prop, accessor_key, non_nil_type, klass) - proc do |val| - if val.nil? - instance_variable_set(accessor_key, nil) - elsif val.is_a?(non_nil_type) - instance_variable_set(accessor_key, val) - else - T::Props::Private::SetterFactory.raise_pretty_error( - klass, - prop, - T::Utils.coerce(non_nil_type), - val, - ) + [ + proc do |val| + unless val.nil? || val.is_a?(non_nil_type) + T::Props::Private::SetterFactory.raise_pretty_error( + klass, + prop, + T::Utils.coerce(non_nil_type), + val, + ) + end instance_variable_set(accessor_key, val) - end - end + end, + proc do |val| + unless val.nil? || val.is_a?(non_nil_type) + T::Props::Private::SetterFactory.raise_pretty_error( + klass, + prop, + T::Utils.coerce(non_nil_type), + val, + ) + end + end, + ] end sig do @@ -137,30 +177,50 @@ def self.build_setter_proc(klass, prop, rules) klass: T.all(Module, T::Props::ClassMethods), validate: T.nilable(ValidateProc), ) - .returns(SetterProc) + .returns([SetterProc, ValueValidationProc]) end private_class_method def self.nilable_proc(prop, accessor_key, non_nil_type, klass, validate) - proc do |val| - if val.nil? - instance_variable_set(accessor_key, nil) - # this use of recursively_valid? is intentional: unlike for - # methods, we want to make sure data at the 'edge' - # (e.g. models that go into databases or structs serialized - # from disk) are correct, so we use more thorough runtime - # checks there - elsif non_nil_type.recursively_valid?(val) - validate&.call(prop, val) - instance_variable_set(accessor_key, val) - else - T::Props::Private::SetterFactory.raise_pretty_error( - klass, - prop, - non_nil_type, - val, - ) - instance_variable_set(accessor_key, val) - end - end + [ + proc do |val| + if val.nil? + instance_variable_set(accessor_key, nil) + # this use of recursively_valid? is intentional: unlike for + # methods, we want to make sure data at the 'edge' + # (e.g. models that go into databases or structs serialized + # from disk) are correct, so we use more thorough runtime + # checks there + elsif non_nil_type.recursively_valid?(val) + validate&.call(prop, val) + instance_variable_set(accessor_key, val) + else + T::Props::Private::SetterFactory.raise_pretty_error( + klass, + prop, + non_nil_type, + val, + ) + instance_variable_set(accessor_key, val) + end + end, + proc do |val| + if val.nil? + # this use of recursively_valid? is intentional: unlike for + # methods, we want to make sure data at the 'edge' + # (e.g. models that go into databases or structs serialized + # from disk) are correct, so we use more thorough runtime + # checks there + elsif non_nil_type.recursively_valid?(val) + validate&.call(prop, val) + else + T::Props::Private::SetterFactory.raise_pretty_error( + klass, + prop, + non_nil_type, + val, + ) + end + end, + ] end sig do @@ -176,7 +236,7 @@ def self.raise_pretty_error(klass, prop, type, val) base_message = "Can't set #{klass.name}.#{prop} to #{val.inspect} (instance of #{val.class}) - need a #{type}" pretty_message = "Parameter '#{prop}': #{base_message}\n" - caller_loc = caller_locations&.find {|l| !l.to_s.include?('sorbet-runtime/lib/types/props')} + caller_loc = caller_locations.find {|l| !l.to_s.include?('sorbet-runtime/lib/types/props')} if caller_loc pretty_message += "Caller: #{caller_loc.path}:#{caller_loc.lineno}\n" end diff --git a/gems/sorbet-runtime/lib/types/props/serializable.rb b/gems/sorbet-runtime/lib/types/props/serializable.rb index 4f886d59d8..b621785809 100644 --- a/gems/sorbet-runtime/lib/types/props/serializable.rb +++ b/gems/sorbet-runtime/lib/types/props/serializable.rb @@ -264,7 +264,13 @@ def message_with_generated_source_context(error, generated_method, generate_sour line_label = error.backtrace.find {|l| l.end_with?("in `#{generated_method}'")} return unless line_label - line_num = line_label.split(':')[1]&.to_i + line_num = if line_label.start_with?("(eval)") + # (eval):13:in `__t_props_generated_serialize' + line_label.split(':')[1]&.to_i + else + # (eval at /Users/jez/stripe/sorbet/gems/sorbet-runtime/lib/types/props/has_lazily_specialized_methods.rb:65):13:in `__t_props_generated_serialize' + line_label.split(':')[2]&.to_i + end return unless line_num source_lines = self.send(generate_source_method).split("\n") diff --git a/gems/sorbet-runtime/lib/types/struct.rb b/gems/sorbet-runtime/lib/types/struct.rb index 49d61ec4a1..3ca17fe459 100644 --- a/gems/sorbet-runtime/lib/types/struct.rb +++ b/gems/sorbet-runtime/lib/types/struct.rb @@ -39,7 +39,7 @@ def initialize(hash={}) # Matches the signature in Props, but raises since this is an immutable struct and only const is allowed sig {params(name: Symbol, cls: T.untyped, rules: T.untyped).void} - def self.prop(name, cls, rules={}) + def self.prop(name, cls, **rules) return super if (cls.is_a?(Hash) && cls[:immutable]) || rules[:immutable] raise "Cannot use `prop` in #{self.name} because it is an immutable struct. Use `const` instead" diff --git a/gems/sorbet-runtime/lib/types/types/anything.rb b/gems/sorbet-runtime/lib/types/types/anything.rb index 8374daba1f..65f33ad49d 100644 --- a/gems/sorbet-runtime/lib/types/types/anything.rb +++ b/gems/sorbet-runtime/lib/types/types/anything.rb @@ -6,6 +6,10 @@ module T::Types class Anything < Base def initialize; end + def build_type + nil + end + # overrides Base def name "T.anything" diff --git a/gems/sorbet-runtime/lib/types/types/attached_class.rb b/gems/sorbet-runtime/lib/types/types/attached_class.rb index 059956a68d..dbfc3a6a3f 100644 --- a/gems/sorbet-runtime/lib/types/types/attached_class.rb +++ b/gems/sorbet-runtime/lib/types/types/attached_class.rb @@ -10,6 +10,10 @@ class AttachedClassType < Base def initialize(); end + def build_type + nil + end + # overrides Base def name "T.attached_class" diff --git a/gems/sorbet-runtime/lib/types/types/base.rb b/gems/sorbet-runtime/lib/types/types/base.rb index 2096bbc99a..b75fdaacae 100644 --- a/gems/sorbet-runtime/lib/types/types/base.rb +++ b/gems/sorbet-runtime/lib/types/types/base.rb @@ -33,6 +33,12 @@ def valid?(obj) raise NotImplementedError end + # Force any lazy initialization that this type might need to do + # It's unusual to call this directly; you probably want to call it indirectly via `T::Utils.run_all_sig_blocks`. + def build_type + raise NotImplementedError + end + # Equality is based on name, so be sure the name reflects all relevant state when implementing. def name raise NotImplementedError diff --git a/gems/sorbet-runtime/lib/types/types/class_of.rb b/gems/sorbet-runtime/lib/types/types/class_of.rb index 6874394b8c..89fa94de7c 100644 --- a/gems/sorbet-runtime/lib/types/types/class_of.rb +++ b/gems/sorbet-runtime/lib/types/types/class_of.rb @@ -10,6 +10,10 @@ def initialize(type) @type = type end + def build_type + nil + end + # overrides Base def name "T.class_of(#{@type})" diff --git a/gems/sorbet-runtime/lib/types/types/enum.rb b/gems/sorbet-runtime/lib/types/types/enum.rb index 29cea0c898..34eb26f51d 100644 --- a/gems/sorbet-runtime/lib/types/types/enum.rb +++ b/gems/sorbet-runtime/lib/types/types/enum.rb @@ -12,6 +12,10 @@ def initialize(values) @values = values end + def build_type + nil + end + # overrides Base def valid?(obj) @values.member?(obj) @@ -29,7 +33,7 @@ def valid?(obj) # overrides Base def name - @name ||= "T.deprecated_enum([#{@values.map(&:inspect).join(', ')}])" + @name ||= "T.deprecated_enum([#{@values.map(&:inspect).sort.join(', ')}])" end # overrides Base diff --git a/gems/sorbet-runtime/lib/types/types/fixed_array.rb b/gems/sorbet-runtime/lib/types/types/fixed_array.rb index 645a617f1a..967273d99f 100644 --- a/gems/sorbet-runtime/lib/types/types/fixed_array.rb +++ b/gems/sorbet-runtime/lib/types/types/fixed_array.rb @@ -6,23 +6,30 @@ module T::Types # Takes a list of types. Validates each item in an array using the type in the same position # in the list. class FixedArray < Base - attr_reader :types - def initialize(types) - @types = types.map {|type| T::Utils.coerce(type)} + @inner_types = types + end + + def types + @types ||= @inner_types.map {|type| T::Utils.coerce(type)} + end + + def build_type + types + nil end # overrides Base def name - "[#{@types.join(', ')}]" + "[#{types.join(', ')}]" end # overrides Base def recursively_valid?(obj) - if obj.is_a?(Array) && obj.length == @types.length + if obj.is_a?(Array) && obj.length == types.length i = 0 - while i < @types.length - if !@types[i].recursively_valid?(obj[i]) + while i < types.length + if !types[i].recursively_valid?(obj[i]) return false end i += 1 @@ -35,10 +42,10 @@ def recursively_valid?(obj) # overrides Base def valid?(obj) - if obj.is_a?(Array) && obj.length == @types.length + if obj.is_a?(Array) && obj.length == types.length i = 0 - while i < @types.length - if !@types[i].valid?(obj[i]) + while i < types.length + if !types[i].valid?(obj[i]) return false end i += 1 @@ -56,7 +63,7 @@ def valid?(obj) # Properly speaking, covariance here is unsound since arrays # can be mutated, but sorbet implements covariant tuples for # ease of adoption. - @types.size == other.types.size && @types.zip(other.types).all? do |t1, t2| + types.size == other.types.size && types.zip(other.types).all? do |t1, t2| t1.subtype_of?(t2) end when TypedArray @@ -85,7 +92,7 @@ def valid?(obj) # overrides Base def describe_obj(obj) if obj.is_a?(Array) - if obj.length == @types.length + if obj.length == types.length item_classes = obj.map(&:class).join(', ') "type [#{item_classes}]" else diff --git a/gems/sorbet-runtime/lib/types/types/fixed_hash.rb b/gems/sorbet-runtime/lib/types/types/fixed_hash.rb index 4c2929586c..2bb805be5a 100644 --- a/gems/sorbet-runtime/lib/types/types/fixed_hash.rb +++ b/gems/sorbet-runtime/lib/types/types/fixed_hash.rb @@ -5,30 +5,37 @@ module T::Types # Takes a hash of types. Validates each item in a hash using the type in the same position # in the list. class FixedHash < Base - attr_reader :types - def initialize(types) - @types = types.transform_values {|v| T::Utils.coerce(v)} + @inner_types = types + end + + def types + @types ||= @inner_types.transform_values {|v| T::Utils.coerce(v)} + end + + def build_type + types + nil end # overrides Base def name - serialize_hash(@types) + serialize_hash(types) end # overrides Base def recursively_valid?(obj) return false unless obj.is_a?(Hash) - return false if @types.any? {|key, type| !type.recursively_valid?(obj[key])} - return false if obj.any? {|key, _| !@types[key]} + return false if types.any? {|key, type| !type.recursively_valid?(obj[key])} + return false if obj.any? {|key, _| !types[key]} true end # overrides Base def valid?(obj) return false unless obj.is_a?(Hash) - return false if @types.any? {|key, type| !type.valid?(obj[key])} - return false if obj.any? {|key, _| !@types[key]} + return false if types.any? {|key, type| !type.valid?(obj[key])} + return false if obj.any? {|key, _| !types[key]} true end @@ -37,7 +44,7 @@ def valid?(obj) case other when FixedHash # Using `subtype_of?` here instead of == would be unsound - @types == other.types + types == other.types when TypedHash # warning: covariant hashes diff --git a/gems/sorbet-runtime/lib/types/types/intersection.rb b/gems/sorbet-runtime/lib/types/types/intersection.rb index e55295fe1c..bc17acbb79 100644 --- a/gems/sorbet-runtime/lib/types/types/intersection.rb +++ b/gems/sorbet-runtime/lib/types/types/intersection.rb @@ -4,10 +4,12 @@ module T::Types # Takes a list of types. Validates that an object matches all of the types. class Intersection < Base - attr_reader :types - def initialize(types) - @types = types.flat_map do |type| + @inner_types = types + end + + def types + @types ||= @inner_types.flat_map do |type| type = T::Utils.resolve_alias(type) if type.is_a?(Intersection) # Simplify nested intersections (mostly so `name` returns a nicer value) @@ -18,19 +20,24 @@ def initialize(types) end.uniq end + def build_type + types + nil + end + # overrides Base def name - "T.all(#{@types.map(&:name).compact.sort.join(', ')})" + "T.all(#{types.map(&:name).compact.sort.join(', ')})" end # overrides Base def recursively_valid?(obj) - @types.all? {|type| type.recursively_valid?(obj)} + types.all? {|type| type.recursively_valid?(obj)} end # overrides Base def valid?(obj) - @types.all? {|type| type.valid?(obj)} + types.all? {|type| type.valid?(obj)} end # overrides Base diff --git a/gems/sorbet-runtime/lib/types/types/noreturn.rb b/gems/sorbet-runtime/lib/types/types/noreturn.rb index f784450b48..6e0088db65 100644 --- a/gems/sorbet-runtime/lib/types/types/noreturn.rb +++ b/gems/sorbet-runtime/lib/types/types/noreturn.rb @@ -6,6 +6,10 @@ module T::Types class NoReturn < Base def initialize; end + def build_type + nil + end + # overrides Base def name "T.noreturn" diff --git a/gems/sorbet-runtime/lib/types/types/proc.rb b/gems/sorbet-runtime/lib/types/types/proc.rb index aece6a3a2f..f73a91f5cd 100644 --- a/gems/sorbet-runtime/lib/types/types/proc.rb +++ b/gems/sorbet-runtime/lib/types/types/proc.rb @@ -8,21 +8,31 @@ module T::Types # At present, we only support fixed-arity procs with no optional or # keyword arguments. class Proc < Base - attr_reader :arg_types - attr_reader :returns - def initialize(arg_types, returns) - @arg_types = {} - arg_types.each do |key, raw_type| - @arg_types[key] = T::Utils.coerce(raw_type) + @inner_arg_types = arg_types + @inner_returns = returns + end + + def arg_types + @arg_types ||= @inner_arg_types.transform_values do |raw_type| + T::Utils.coerce(raw_type) end - @returns = T::Utils.coerce(returns) + end + + def returns + @returns ||= T::Utils.coerce(@inner_returns) + end + + def build_type + arg_types + returns + nil end # overrides Base def name args = [] - @arg_types.each do |k, v| + arg_types.each do |k, v| args << "#{k}: #{v.name}" end "T.proc.params(#{args.join(', ')}).returns(#{returns})" diff --git a/gems/sorbet-runtime/lib/types/types/self_type.rb b/gems/sorbet-runtime/lib/types/types/self_type.rb index e431a3e0f9..f1eafc06c3 100644 --- a/gems/sorbet-runtime/lib/types/types/self_type.rb +++ b/gems/sorbet-runtime/lib/types/types/self_type.rb @@ -8,6 +8,10 @@ class SelfType < Base def initialize(); end + def build_type + nil + end + # overrides Base def name "T.self_type" diff --git a/gems/sorbet-runtime/lib/types/types/simple.rb b/gems/sorbet-runtime/lib/types/types/simple.rb index 45937673a1..5bacd1c6dd 100644 --- a/gems/sorbet-runtime/lib/types/types/simple.rb +++ b/gems/sorbet-runtime/lib/types/types/simple.rb @@ -13,13 +13,22 @@ def initialize(raw_type) @raw_type = raw_type end + def build_type + nil + end + # overrides Base def name # Memoize to mitigate pathological performance with anonymous modules (https://bugs.ruby-lang.org/issues/11119) # # `name` isn't normally a hot path for types, but it is used in initializing a T::Types::Union, # and so in `T.nilable`, and so in runtime constructions like `x = T.let(nil, T.nilable(Integer))`. + # + # Care more about back compat than we do about performance here. + # Once 2.6 is well in the rear view mirror, we can replace this. + # rubocop:disable Performance/BindCall @name ||= (NAME_METHOD.bind(@raw_type).call || @raw_type.name).freeze + # rubocop:enable Performance/BindCall end # overrides Base @@ -32,6 +41,11 @@ def valid?(obj) case other when Simple @raw_type <= other.raw_type + when TypedClass + # This case is a bit odd--we would have liked to solve this like we do + # for `T::Array` et al., but don't for backwards compatibility. + # See `type_for_module` below. + @raw_type <= other.underlying_class else false end @@ -89,6 +103,9 @@ def self.type_for_module(mod) elsif !Object.autoload?(:Set) && Object.const_defined?(:Set) && mod == ::Set T::Set[T.untyped] else + # ideally we would have a case mapping from ::Class -> T::Class here + # but for backwards compatibility we don't have that, and instead + # have a special case in subtype_of_single? Simple.new(mod) end diff --git a/gems/sorbet-runtime/lib/types/types/t_enum.rb b/gems/sorbet-runtime/lib/types/types/t_enum.rb index 121368c7b6..be5deb5b1b 100644 --- a/gems/sorbet-runtime/lib/types/types/t_enum.rb +++ b/gems/sorbet-runtime/lib/types/types/t_enum.rb @@ -10,6 +10,10 @@ def initialize(val) @val = val end + def build_type + nil + end + # overrides Base def name # Strips the #<...> off, just leaving the ... diff --git a/gems/sorbet-runtime/lib/types/types/type_parameter.rb b/gems/sorbet-runtime/lib/types/types/type_parameter.rb index 4edf5368c3..ed80363a9f 100644 --- a/gems/sorbet-runtime/lib/types/types/type_parameter.rb +++ b/gems/sorbet-runtime/lib/types/types/type_parameter.rb @@ -20,6 +20,10 @@ def initialize(name) @name = name end + def build_type + nil + end + def self.make(name) cached = Private.cached_entry(name) return cached if cached diff --git a/gems/sorbet-runtime/lib/types/types/type_variable.rb b/gems/sorbet-runtime/lib/types/types/type_variable.rb index 187119167d..60222bb6ec 100644 --- a/gems/sorbet-runtime/lib/types/types/type_variable.rb +++ b/gems/sorbet-runtime/lib/types/types/type_variable.rb @@ -19,6 +19,10 @@ def initialize(variance) @variance = variance end + def build_type + nil + end + def valid?(obj) true end diff --git a/gems/sorbet-runtime/lib/types/types/typed_array.rb b/gems/sorbet-runtime/lib/types/types/typed_array.rb index 9e4d4152c1..7d95fb109f 100644 --- a/gems/sorbet-runtime/lib/types/types/typed_array.rb +++ b/gems/sorbet-runtime/lib/types/types/typed_array.rb @@ -5,7 +5,7 @@ module T::Types class TypedArray < TypedEnumerable # overrides Base def name - "T::Array[#{@type.name}]" + "T::Array[#{type.name}]" end def underlying_class @@ -60,6 +60,11 @@ def valid?(obj) obj.is_a?(Array) end + def freeze + build_type # force lazy initialization before freezing the object + super + end + module Private INSTANCE = Untyped.new.freeze end diff --git a/gems/sorbet-runtime/lib/types/types/typed_class.rb b/gems/sorbet-runtime/lib/types/types/typed_class.rb index 0088300841..8a148470b3 100644 --- a/gems/sorbet-runtime/lib/types/types/typed_class.rb +++ b/gems/sorbet-runtime/lib/types/types/typed_class.rb @@ -3,15 +3,22 @@ module T::Types class TypedClass < T::Types::Base - attr_reader :type - def initialize(type) - @type = T::Utils.coerce(type) + @inner_type = type + end + + def type + @type ||= T::Utils.coerce(@inner_type) + end + + def build_type + type + nil end # overrides Base def name - "T::Class[#{@type.name}]" + "T::Class[#{type.name}]" end def underlying_class @@ -67,6 +74,11 @@ def initialize super(T.untyped) end + def freeze + build_type # force lazy initialization before freezing the object + super + end + module Private INSTANCE = Untyped.new.freeze end @@ -77,6 +89,11 @@ def initialize super(T.anything) end + def freeze + build_type # force lazy initialization before freezing the object + super + end + module Private INSTANCE = Anything.new.freeze end diff --git a/gems/sorbet-runtime/lib/types/types/typed_enumerable.rb b/gems/sorbet-runtime/lib/types/types/typed_enumerable.rb index 39fe5571b5..2a29dac9f3 100644 --- a/gems/sorbet-runtime/lib/types/types/typed_enumerable.rb +++ b/gems/sorbet-runtime/lib/types/types/typed_enumerable.rb @@ -6,10 +6,17 @@ module T::Types # `case` statement below in `describe_obj` in order to get better # error messages. class TypedEnumerable < Base - attr_reader :type - def initialize(type) - @type = T::Utils.coerce(type) + @inner_type = type + end + + def type + @type ||= T::Utils.coerce(@inner_type) + end + + def build_type + type + nil end def underlying_class @@ -18,7 +25,7 @@ def underlying_class # overrides Base def name - "T::Enumerable[#{@type.name}]" + "T::Enumerable[#{type.name}]" end # overrides Base @@ -34,20 +41,20 @@ def recursively_valid?(obj) begin it = 0 while it < obj.count - return false unless @type.recursively_valid?(obj[it]) + return false unless type.recursively_valid?(obj[it]) it += 1 end true end when Hash - return false unless @type.is_a?(FixedArray) - types = @type.types + return false unless type.is_a?(FixedArray) + types = type.types return false if types.count != 2 key_type = types[0] value_type = types[1] obj.each_pair do |key, val| # Some objects (I'm looking at you Rack::Utils::HeaderHash) don't - # iterate over a [key, value] array, so we can't juse use the @type.recursively_valid?(v) + # iterate over a [key, value] array, so we can't just use the type.recursively_valid?(v) return false if !key_type.recursively_valid?(key) || !value_type.recursively_valid?(val) end true @@ -65,10 +72,10 @@ def recursively_valid?(obj) # boundlessness, it does not express a type. For example `(nil...nil)` is not a T::Range[NilClass], its a range # of unknown types (T::Range[T.untyped]). # Similarly, `(nil...1)` is not a `T::Range[T.nilable(Integer)]`, it's a boundless range of Integer. - (obj.begin.nil? || @type.recursively_valid?(obj.begin)) && (obj.end.nil? || @type.recursively_valid?(obj.end)) + (obj.begin.nil? || type.recursively_valid?(obj.begin)) && (obj.end.nil? || type.recursively_valid?(obj.end)) when Set obj.each do |item| - return false unless @type.recursively_valid?(item) + return false unless type.recursively_valid?(item) end true @@ -90,7 +97,7 @@ def recursively_valid?(obj) # should be invariant because they are mutable and support # both reading and writing. However, Sorbet treats *all* # Enumerable subclasses as covariant for ease of adoption. - @type.subtype_of?(other.type) + type.subtype_of?(other.type) elsif other.class <= Simple underlying_class <= other.raw_type else @@ -165,7 +172,7 @@ def describe_obj(obj) if T::Configuration::AT_LEAST_RUBY_2_7 Object.instance_method(:class).bind_call(obj) else - Object.instance_method(:class).bind(obj).call + Object.instance_method(:class).bind(obj).call # rubocop:disable Performance/BindCall end end end diff --git a/gems/sorbet-runtime/lib/types/types/typed_enumerator.rb b/gems/sorbet-runtime/lib/types/types/typed_enumerator.rb index e79341eb94..05c490c244 100644 --- a/gems/sorbet-runtime/lib/types/types/typed_enumerator.rb +++ b/gems/sorbet-runtime/lib/types/types/typed_enumerator.rb @@ -3,15 +3,13 @@ module T::Types class TypedEnumerator < TypedEnumerable - attr_reader :type - def underlying_class Enumerator end # overrides Base def name - "T::Enumerator[#{@type.name}]" + "T::Enumerator[#{type.name}]" end # overrides Base diff --git a/gems/sorbet-runtime/lib/types/types/typed_enumerator_chain.rb b/gems/sorbet-runtime/lib/types/types/typed_enumerator_chain.rb index f85b6ea01f..bdeeeb6316 100644 --- a/gems/sorbet-runtime/lib/types/types/typed_enumerator_chain.rb +++ b/gems/sorbet-runtime/lib/types/types/typed_enumerator_chain.rb @@ -3,15 +3,13 @@ module T::Types class TypedEnumeratorChain < TypedEnumerable - attr_reader :type - def underlying_class Enumerator::Chain end # overrides Base def name - "T::Enumerator::Chain[#{@type.name}]" + "T::Enumerator::Chain[#{type.name}]" end # overrides Base diff --git a/gems/sorbet-runtime/lib/types/types/typed_enumerator_lazy.rb b/gems/sorbet-runtime/lib/types/types/typed_enumerator_lazy.rb index 3569be398a..f053c1394f 100644 --- a/gems/sorbet-runtime/lib/types/types/typed_enumerator_lazy.rb +++ b/gems/sorbet-runtime/lib/types/types/typed_enumerator_lazy.rb @@ -3,15 +3,13 @@ module T::Types class TypedEnumeratorLazy < TypedEnumerable - attr_reader :type - def underlying_class Enumerator::Lazy end # overrides Base def name - "T::Enumerator::Lazy[#{@type.name}]" + "T::Enumerator::Lazy[#{type.name}]" end # overrides Base diff --git a/gems/sorbet-runtime/lib/types/types/typed_hash.rb b/gems/sorbet-runtime/lib/types/types/typed_hash.rb index f48e0c1595..185813220a 100644 --- a/gems/sorbet-runtime/lib/types/types/typed_hash.rb +++ b/gems/sorbet-runtime/lib/types/types/typed_hash.rb @@ -3,22 +3,32 @@ module T::Types class TypedHash < TypedEnumerable - # Technically we don't need these, but they are a nice api - attr_reader :keys, :values - def underlying_class Hash end def initialize(keys:, values:) - @keys = T::Utils.coerce(keys) - @values = T::Utils.coerce(values) - @type = T::Utils.coerce([keys, values]) + @inner_keys = keys + @inner_values = values + end + + # Technically we don't need this, but it is a nice api + def keys + @keys ||= T::Utils.coerce(@inner_keys) + end + + # Technically we don't need this, but it is a nice api + def values + @values ||= T::Utils.coerce(@inner_values) + end + + def type + @type ||= T::Utils.coerce([keys, values]) end # overrides Base def name - "T::Hash[#{@keys.name}, #{@values.name}]" + "T::Hash[#{keys.name}, #{values.name}]" end # overrides Base diff --git a/gems/sorbet-runtime/lib/types/types/typed_range.rb b/gems/sorbet-runtime/lib/types/types/typed_range.rb index 50e2ef9d53..07c087696c 100644 --- a/gems/sorbet-runtime/lib/types/types/typed_range.rb +++ b/gems/sorbet-runtime/lib/types/types/typed_range.rb @@ -3,15 +3,13 @@ module T::Types class TypedRange < TypedEnumerable - attr_reader :type - def underlying_class Hash end # overrides Base def name - "T::Range[#{@type.name}]" + "T::Range[#{type.name}]" end # overrides Base diff --git a/gems/sorbet-runtime/lib/types/types/typed_set.rb b/gems/sorbet-runtime/lib/types/types/typed_set.rb index 8c1b2caee3..781c0212d4 100644 --- a/gems/sorbet-runtime/lib/types/types/typed_set.rb +++ b/gems/sorbet-runtime/lib/types/types/typed_set.rb @@ -3,15 +3,13 @@ module T::Types class TypedSet < TypedEnumerable - attr_reader :type - def underlying_class Hash end # overrides Base def name - "T::Set[#{@type.name}]" + "T::Set[#{type.name}]" end # overrides Base diff --git a/gems/sorbet-runtime/lib/types/types/union.rb b/gems/sorbet-runtime/lib/types/types/union.rb index a2f160a82b..28a2d5ea0e 100644 --- a/gems/sorbet-runtime/lib/types/types/union.rb +++ b/gems/sorbet-runtime/lib/types/types/union.rb @@ -4,12 +4,14 @@ module T::Types # Takes a list of types. Validates that an object matches at least one of the types. class Union < Base - attr_reader :types - # Don't use Union.new directly, use `Private::Pool.union_of_types` # inside sorbet-runtime and `T.any` elsewhere. def initialize(types) - @types = types.flat_map do |type| + @inner_types = types + end + + def types + @types ||= @inner_types.flat_map do |type| type = T::Utils.coerce(type) if type.is_a?(Union) # Simplify nested unions (mostly so `name` returns a nicer value) @@ -20,6 +22,11 @@ def initialize(types) end.uniq end + def build_type + types + nil + end + # overrides Base def name # Use the attr_reader here so we can override it in SimplePairUnion @@ -51,12 +58,12 @@ def name # overrides Base def recursively_valid?(obj) - @types.any? {|type| type.recursively_valid?(obj)} + types.any? {|type| type.recursively_valid?(obj)} end # overrides Base def valid?(obj) - @types.any? {|type| type.valid?(obj)} + types.any? {|type| type.valid?(obj)} end # overrides Base diff --git a/gems/sorbet-runtime/lib/types/types/untyped.rb b/gems/sorbet-runtime/lib/types/types/untyped.rb index a06a633d42..ee0bc62357 100644 --- a/gems/sorbet-runtime/lib/types/types/untyped.rb +++ b/gems/sorbet-runtime/lib/types/types/untyped.rb @@ -7,6 +7,10 @@ class Untyped < Base def initialize; end + def build_type + nil + end + # overrides Base def name "T.untyped" diff --git a/gems/sorbet-runtime/lib/types/utils.rb b/gems/sorbet-runtime/lib/types/utils.rb index 3c72e14915..482d58efb4 100644 --- a/gems/sorbet-runtime/lib/types/utils.rb +++ b/gems/sorbet-runtime/lib/types/utils.rb @@ -4,6 +4,7 @@ module T::Utils module Private def self.coerce_and_check_module_types(val, check_val, check_module_type) + # rubocop:disable Style/CaseLikeIf if val.is_a?(T::Types::Base) if val.is_a?(T::Private::Types::TypeAlias) val.aliased_type @@ -31,6 +32,7 @@ def self.coerce_and_check_module_types(val, check_val, check_module_type) raise "Invalid value for type constraint. Must be an #{T::Types::Base}, a " \ "class/module, or an array. Got a `#{val.class}`." end + # rubocop:enable Style/CaseLikeIf end end @@ -79,8 +81,8 @@ def self.wrap_method_with_call_validation_if_needed(mod, method_sig, original_me end # Unwraps all the sigs. - def self.run_all_sig_blocks - T::Private::Methods.run_all_sig_blocks + def self.run_all_sig_blocks(force_type_init: true) + T::Private::Methods.run_all_sig_blocks(force_type_init: force_type_init) end # Return the underlying type for a type alias. Otherwise returns type. @@ -173,7 +175,7 @@ module Nilable def self.get_type_info(prop_type) if prop_type.is_a?(T::Types::Union) non_nilable_type = prop_type.unwrap_nilable - if non_nilable_type&.is_a?(T::Types::Simple) + if non_nilable_type.is_a?(T::Types::Simple) non_nilable_type = non_nilable_type.raw_type end TypeInfo.new(true, non_nilable_type) @@ -188,7 +190,7 @@ def self.get_type_info(prop_type) def self.get_underlying_type(prop_type) if prop_type.is_a?(T::Types::Union) non_nilable_type = prop_type.unwrap_nilable - if non_nilable_type&.is_a?(T::Types::Simple) + if non_nilable_type.is_a?(T::Types::Simple) non_nilable_type = non_nilable_type.raw_type end non_nilable_type || prop_type diff --git a/gems/sorbet-runtime/sorbet-runtime.gemspec b/gems/sorbet-runtime/sorbet-runtime.gemspec index e7139dad49..7ed09923ff 100644 --- a/gems/sorbet-runtime/sorbet-runtime.gemspec +++ b/gems/sorbet-runtime/sorbet-runtime.gemspec @@ -14,16 +14,14 @@ Gem::Specification.new do |s| s.required_ruby_version = ['>= 2.7.0'] s.add_development_dependency 'minitest', '~> 5.11' - s.add_development_dependency 'mocha', '~> 1.7' + s.add_development_dependency 'mocha', '~> 2.1' s.add_development_dependency 'rake' - s.add_development_dependency 'rubocop', '~> 0.90.0' - s.add_development_dependency 'rubocop-performance', '~> 1.8.0' + s.add_development_dependency 'rubocop', '1.57.1' + s.add_development_dependency 'rubocop-performance', '1.13.2' # for reproducing race conditions in tests s.add_development_dependency 'concurrent-ruby', '~> 1.1.5' s.add_development_dependency 'pry' s.add_development_dependency 'pry-byebug' - # for validating generated code - s.add_development_dependency 'parser', '~> 2.7.1' # for running ruby subprocesses s.add_development_dependency 'subprocess', '~> 1.5.3' end diff --git a/gems/sorbet-runtime/test/concurrent-ruby-shims.rbi b/gems/sorbet-runtime/test/concurrent-ruby-shims.rbi new file mode 100644 index 0000000000..39f7465219 --- /dev/null +++ b/gems/sorbet-runtime/test/concurrent-ruby-shims.rbi @@ -0,0 +1,7 @@ +# typed: __STDLIB_INTERNAL + +class Concurrent::Hash < Hash + K = type_member(:out) + V = type_member(:out) + Elem = type_member(:out) +end diff --git a/gems/sorbet-runtime/test/test_helper.rb b/gems/sorbet-runtime/test/test_helper.rb index c8ea76e188..4fcf887735 100644 --- a/gems/sorbet-runtime/test/test_helper.rb +++ b/gems/sorbet-runtime/test/test_helper.rb @@ -17,9 +17,9 @@ module Critic; end module Critic::Unit; end -module MiniTest; end -class MiniTest::Spec; end -class Critic::Unit::UnitTest < MiniTest::Spec; end +module Minitest; end +class Minitest::Spec; end +class Critic::Unit::UnitTest < Minitest::Spec; end module Critic::Extensions; end module Critic::Extensions::TypeExt def self.unpatch_types; end diff --git a/gems/sorbet-runtime/test/types/abstract_validation.rb b/gems/sorbet-runtime/test/types/abstract_validation.rb index 51e2c7cd5a..a3bf4e8333 100644 --- a/gems/sorbet-runtime/test/types/abstract_validation.rb +++ b/gems/sorbet-runtime/test/types/abstract_validation.rb @@ -273,7 +273,7 @@ def foo assert_equal(:impl2, klass.new.foo) end - it "suceeds when the method is defined directly on the receiving class" do + it "succeeds when the method is defined directly on the receiving class" do mod = Module.new do extend T::Helpers abstract! diff --git a/gems/sorbet-runtime/test/types/builder_syntax.rb b/gems/sorbet-runtime/test/types/builder_syntax.rb index c9243f6779..354d06c855 100644 --- a/gems/sorbet-runtime/test/types/builder_syntax.rb +++ b/gems/sorbet-runtime/test/types/builder_syntax.rb @@ -257,20 +257,6 @@ def self.test_method(x); end mod.test_method(:llamas) # wrong, but ignored end - it '`compiled` is not checked' do - mod = Module.new do - extend T::Sig - sig do - params(x: Integer) - .returns(String) - .checked(:compiled) - end - def self.test_method(x); end - end - - mod.test_method(:llamas) # wrong, but ignored - end - def make_mod Module.new do extend T::Sig @@ -391,19 +377,6 @@ def self.foo; end; foo end assert_includes(ex.message, "To use .on_failure you must additionally call .checked(:tests) or .checked(:always), otherwise, the .on_failure has no effect") end - - it 'forbids .on_failure if default_checked_level is :compiled' do - T::Private::RuntimeLevels.instance_variable_set(:@default_checked_level, :compiled) - - ex = assert_raises do - Class.new do - extend T::Sig - sig {void.on_failure(:soft, notify: 'me')} - def self.foo; end; foo - end - end - assert_includes(ex.message, "To use .on_failure you must additionally call .checked(:tests) or .checked(:always), otherwise, the .on_failure has no effect") - end end end @@ -451,17 +424,6 @@ def self.foo; end; foo assert_includes(ex.message, "You can't use .checked(:never) with .on_failure") end - it 'forbids .on_failure and then .checked(:compiled)' do - ex = assert_raises do - Class.new do - extend T::Sig - sig {returns(NilClass).on_failure(:soft, notify: 'me').checked(:compiled)} - def self.foo; end; foo - end - end - assert_includes(ex.message, "You can't use .checked(:compiled) with .on_failure") - end - it 'allows .on_failure and then .checked(:tests)' do Class.new do extend T::Sig @@ -492,18 +454,6 @@ def self.foo; end; foo assert_includes(ex.message, "You can't use .on_failure with .checked(:never)") end - it 'forbids .checked(:compiled) and then .on_failure' do - ex = assert_raises do - Class.new do - extend T::Sig - # We explicitly need to test that this ordering raises a certain error - sig {returns(NilClass).checked(:compiled).on_failure(:soft, notify: 'me')} - def self.foo; end; foo - end - end - assert_includes(ex.message, "You can't use .on_failure with .checked(:compiled)") - end - it 'allows .checked(:tests) and then .on_failure' do Class.new do extend T::Sig diff --git a/gems/sorbet-runtime/test/types/compiler.rb b/gems/sorbet-runtime/test/types/compiler.rb deleted file mode 100644 index 03491ed436..0000000000 --- a/gems/sorbet-runtime/test/types/compiler.rb +++ /dev/null @@ -1,18 +0,0 @@ -# frozen_string_literal: true -require_relative '../test_helper' - -module Opus::Types::Test - class CompilerTest < Critic::Unit::UnitTest - describe 'T::Private::Compiler.running_compiled?' do - it 'returns false always, because this is the interpreter' do - refute(T::Private::Compiler.running_compiled?) - end - end - - describe 'T::Private::Compiler.compiler_version' do - it 'returns nil always, because this is the interpreter' do - assert_nil(T::Private::Compiler.compiler_version) - end - end - end -end diff --git a/gems/sorbet-runtime/test/types/configuration.rb b/gems/sorbet-runtime/test/types/configuration.rb index c6dcfb36e4..a105806c9b 100644 --- a/gems/sorbet-runtime/test/types/configuration.rb +++ b/gems/sorbet-runtime/test/types/configuration.rb @@ -408,7 +408,7 @@ def @mod.foo(a) ex = assert_raises do T::Configuration.default_checked_level = :foo end - assert_includes(ex.message, "Invalid `checked` level 'foo'. Use one of: [:always, :tests, :never, :compiled].") + assert_includes(ex.message, "Invalid `checked` level 'foo'. Use one of: [:always, :tests, :never].") end it 'fails when default_checked_level has already been read' do diff --git a/gems/sorbet-runtime/test/types/edge_cases.rb b/gems/sorbet-runtime/test/types/edge_cases.rb index 5678a9d07e..d7bccab285 100644 --- a/gems/sorbet-runtime/test/types/edge_cases.rb +++ b/gems/sorbet-runtime/test/types/edge_cases.rb @@ -637,6 +637,19 @@ def new(type: self) assert_instance_of(String, klass.new(type: String)) end + it 'allows abstract classes to redclare tap' do + superclass = Class.new do + extend T::Helpers + abstract! + + attr_reader :tap + end + + klass = Class.new(superclass) + + assert_instance_of(klass, klass.new) + end + it 'handles class scope change when already hooked' do klass = Class.new do extend T::Sig @@ -868,7 +881,7 @@ def foo; end mutex = Mutex.new replaced = T::Private::ClassUtils.replace_method(T::Private::Methods.singleton_class, :run_sig_block_for_method) do |*args| barrier.wait - mutex.synchronize {replaced.bind(T::Private::Methods).call(*args)} + mutex.synchronize {replaced.bind_call(T::Private::Methods, *args)} end klass = Class.new do diff --git a/gems/sorbet-runtime/test/types/enum.rb b/gems/sorbet-runtime/test/types/enum.rb index 33a41baf0e..a3dda4474b 100644 --- a/gems/sorbet-runtime/test/types/enum.rb +++ b/gems/sorbet-runtime/test/types/enum.rb @@ -370,6 +370,7 @@ class CustomSerializationEnum < T::Enum describe 'string value conversion assertions' do ENUM_CONVERSION_MSG = /Implicit conversion of Enum instances to strings is not allowed. Call #serialize instead./.freeze + before do T::Configuration.expects(:soft_assert_handler).never end @@ -383,7 +384,9 @@ class CustomSerializationEnum < T::Enum it 'raises an assertion if to_str is called (implicitly) and also returns the serialized value' do ex = assert_raises(NoMethodError) do - 'foo ' + CardSuit::HEART + # rubocop:disable Style/StringConcatenation + "foo " + CardSuit::HEART + # rubocop:enable Style/StringConcatenation end assert_match(ENUM_CONVERSION_MSG, ex.message) end @@ -393,8 +396,9 @@ class CustomSerializationEnum < T::Enum ENUM_CONVERSION_MSG_LEGACY = 'Implicit conversion of Enum instances to strings is not allowed. Call #serialize instead.' before do T::Configuration.enable_legacy_t_enum_migration_mode - T::Configuration.expects(:soft_assert_handler).at_least_once.with do |message| + T::Configuration.expects(:soft_assert_handler).at_least_once.with do |message, storytime:| assert_equal(ENUM_CONVERSION_MSG_LEGACY, message) + assert_includes(storytime[:caller_location], __FILE__) end end @@ -407,13 +411,13 @@ class CustomSerializationEnum < T::Enum end it 'raises an assertion if to_str is called (implicitly) and also returns the serialized value' do + # rubocop:disable Style/StringConcatenation assert_equal('foo heart', 'foo ' + CardSuit::HEART) + # rubocop:enable Style/StringConcatenation end end describe 'string value comparison assertions' do - # rubocop:disable Style/YodaCondition - it 'returns false if the types mismatch for ==' do assert_equal(false, 'spade' == CardSuit::SPADE) assert_equal(false, 'diamond' == CardSuit::SPADE) @@ -428,8 +432,6 @@ class CustomSerializationEnum < T::Enum assert_equal(false, CardSuit::CLUB === 'spade') end - # rubocop:enable Style/YodaCondition - it 'returns false for a string in a `when` compared to an enum value' do val = CardSuit::SPADE @@ -456,8 +458,9 @@ class CustomSerializationEnum < T::Enum ENUM_COMPARE_MSG = 'Enum to string comparison not allowed. Compare to the Enum instance directly instead. See go/enum-migration' before do T::Configuration.enable_legacy_t_enum_migration_mode - T::Configuration.expects(:soft_assert_handler).at_least_once.with do |message| + T::Configuration.expects(:soft_assert_handler).at_least_once.with do |message, storytime:| assert_equal(ENUM_COMPARE_MSG, message) + assert_includes(storytime[:caller_location], __FILE__) end end @@ -465,8 +468,6 @@ class CustomSerializationEnum < T::Enum T::Configuration.disable_legacy_t_enum_migration_mode end - # rubocop:disable Style/YodaCondition - it 'raises an assertion if string is lhs of comparison' do assert_equal(true, 'spade' == CardSuit::SPADE) @@ -491,8 +492,6 @@ class CustomSerializationEnum < T::Enum assert_equal(false, CardSuit::CLUB === 'spade') end - # rubocop:enable Style/YodaCondition - it 'raises an assertion for a string in a `when` compared to an enum value' do val = CardSuit::SPADE diff --git a/gems/sorbet-runtime/test/types/final_method.rb b/gems/sorbet-runtime/test/types/final_method.rb index f70974c23a..b824df118d 100644 --- a/gems/sorbet-runtime/test/types/final_method.rb +++ b/gems/sorbet-runtime/test/types/final_method.rb @@ -462,7 +462,7 @@ def self.foo; end assert_overridden_err('foo', CLASS_CLASS_REGEX_STR, CLASS_CLASS_REGEX_STR, __LINE__ - 7, __LINE__ - 3, err) end - it "allows toggling a final method's visbility in the same class" do + it "allows toggling a final method's visibility in the same class" do Class.new do extend T::Sig sig(:final) {void} diff --git a/gems/sorbet-runtime/test/types/interface_validation.rb b/gems/sorbet-runtime/test/types/interface_validation.rb index 9a9ba2f6cc..14b839f534 100644 --- a/gems/sorbet-runtime/test/types/interface_validation.rb +++ b/gems/sorbet-runtime/test/types/interface_validation.rb @@ -144,7 +144,7 @@ def foo; end T::Private::Abstract::Validate.validate_abstract_module(base) end - it "raises an error if a void method has an incompatible implementation" do + it "does not raise an error if a void method does not have a void implementation" do base = Module.new do extend T::Sig extend T::Helpers @@ -169,10 +169,7 @@ def foo include mod end - err = assert_raises(RuntimeError) do - klass.new.foo - end - assert_includes(err.message, "Incompatible return type in signature for implementation of method") + klass.new.foo end end diff --git a/gems/sorbet-runtime/test/types/interface_wrapper.rb b/gems/sorbet-runtime/test/types/interface_wrapper.rb deleted file mode 100644 index 2bebf0780b..0000000000 --- a/gems/sorbet-runtime/test/types/interface_wrapper.rb +++ /dev/null @@ -1,166 +0,0 @@ -# frozen_string_literal: true -require_relative '../test_helper' - -class Opus::Types::Test::InterfaceWrapperTest < Critic::Unit::UnitTest - module BaseMixin - def base; end - end - - module Mixin - include BaseMixin - - def foo(bar) - bar + yield - end - - private def priv - 42 - end - - def kwarg(bar:) - bar + 1 - end - end - - module Mixin2 - def mixin2 - nil - end - end - - class Implementation - include Mixin - include Mixin2 - - def bar - 43 - end - end - - before do - @obj = Implementation.new - @wrapper = T::InterfaceWrapper.wrap_instance(@obj, Mixin) - @array = T::InterfaceWrapper.wrap_instances([@obj], Mixin) - end - - describe "method calls" do - it "proxies methods in the module" do - assert_equal("ab", @wrapper.foo("a") {"b"}) - end - - it "does not proxy methods not in the module" do - assert_raises(NoMethodError) do - @wrapper.bar - end - end - - it "keeps private methods private" do - err = assert_raises(NoMethodError) do - @wrapper.priv - end - - assert_includes(err.message, "private method `priv'") - assert_equal(42, @wrapper.send(:priv)) - end - end - - describe "dynamic_cast" do - it "returns nil for an unrelated type" do - res = T::InterfaceWrapper.dynamic_cast(@wrapper, Array) - assert_nil(res) - end - - it "returns the same object when it matches the type and is not a wrapper" do - foo = Object.new - res = T::InterfaceWrapper.dynamic_cast(foo, Object) - assert_same(foo, res) - end - - it "returns the wrapped object when passing the wrapped object's type" do - res = T::InterfaceWrapper.dynamic_cast(@wrapper, Implementation) - assert_same(@obj, res) - end - end - - describe "wrapped_dynamic_cast" do - it "returns the wrapped object when passing the wrapped object's type" do - res = T::InterfaceWrapper.wrapped_dynamic_cast(@wrapper, Implementation) - assert_same(@obj, res) - end - - it "returns a proxy to a different module on the wrapped object" do - res = T::InterfaceWrapper.wrapped_dynamic_cast(@wrapper, Mixin2) - assert_kind_of(T::InterfaceWrapper, @wrapper) - assert_respond_to(res, :mixin2) - refute_respond_to(res, :foo) - refute_respond_to(res, :bar) - end - - it "doesn't create a wrapper to a wrapper when upcasting on a wrapper" do - res = T::InterfaceWrapper.wrapped_dynamic_cast(@wrapper, BaseMixin) - refute_kind_of(T::InterfaceWrapper, res.__target_obj_DO_NOT_USE) - end - - it "returns the same object when asking for an indentical wrapper" do - res = T::InterfaceWrapper.wrapped_dynamic_cast(@wrapper, Mixin) - assert_same(res, @wrapper) - end - end - - describe "is_a?" do - it "returns true for the interface module" do - assert_kind_of(Mixin, @wrapper) - end - - it "returns true for InterfaceWrapper" do - assert_kind_of(T::InterfaceWrapper, @wrapper) - end - - it "returns false for the object class" do - refute_kind_of(Implementation, @wrapper) - end - end - - describe "array wrapper" do - it "returns object in array" do - assert_kind_of(Mixin, @array[0]) - res = T::InterfaceWrapper.wrapped_dynamic_cast(@array[0], Implementation) - assert_same(@obj, res) - end - - it "raises if any element is not castable" do - assert_raises(RuntimeError) {T::InterfaceWrapper.wrap_instances([@obj, nil], Mixin)} - assert_raises(RuntimeError) {T::InterfaceWrapper.wrap_instances([@obj, "foo"], Mixin)} - assert_raises(RuntimeError) {T::InterfaceWrapper.wrap_instances([@obj, 5], Mixin)} - end - end - - describe "argument forwarding" do - it "does not cause Ruby keyword argument warnings" do - # this test has a few different failure modes: - # - on 2.6 and below, we want to make sure we don't call the - # `ruby2_keywords` helper method because it doesn't exist yet - # - on 2.7, we want to make sure we don't get warnings about - # bad kwarg usage: to do this, we enable the relevant deprecation - # warnings for the duration of the test and assert that the warn - # method is never called - # - on 3 and above, simply calling a wrapped kwarg method is - # sufficient to show that the behavior is correct: if we didn't - # specify `ruby2_keywords`, then it would fail with a "wrong - # number of arguments" error. - - if RUBY_VERSION.start_with?("2.7") - Warning[:deprecated] = true - replaced = T::Private::ClassUtils.replace_method(Warning, :warn) do |warning| - raise "Found kwarg warning: #{warning}" - end - end - @wrapper.kwarg(bar: 5) - ensure - if RUBY_VERSION.start_with?("2.7") - replaced&.restore - Warning[:deprecated] = false - end - end - end -end diff --git a/gems/sorbet-runtime/test/types/method_modes.rb b/gems/sorbet-runtime/test/types/method_modes.rb index 612cdd6897..cf182608da 100644 --- a/gems/sorbet-runtime/test/types/method_modes.rb +++ b/gems/sorbet-runtime/test/types/method_modes.rb @@ -202,7 +202,7 @@ def foo; end assert_includes( err.message, - "You marked `foo` as .override, but that method doesn't already exist in this class/module to be overriden" + "You marked `foo` as .override, but that method doesn't already exist in this class/module to be overridden" ) end diff --git a/gems/sorbet-runtime/test/types/method_validation.rb b/gems/sorbet-runtime/test/types/method_validation.rb index 35b5e399e0..b26fde5b2e 100644 --- a/gems/sorbet-runtime/test/types/method_validation.rb +++ b/gems/sorbet-runtime/test/types/method_validation.rb @@ -120,7 +120,7 @@ def mod2.bar; end mod = Module.new do extend T::Sig sig {params(a: Integer, b: Integer).returns(Integer)} - def self.foo(a: 1, b:) + def self.foo(a: 1, b:) # rubocop:disable Style/KeywordParametersOrder a + b end end @@ -225,7 +225,7 @@ def @mod.foo(x, y) if check_alloc_counts expected_allocations = T::Configuration::AT_LEAST_RUBY_2_7 ? 1 : 2 - assert_equal(expected_allocations, allocated) # dmitry: for some reason, when run locally this numeber is 0, in CI it's 2. IDK why. + assert_equal(expected_allocations, allocated) # dmitry: for some reason, when run locally this number is 0, in CI it's 2. IDK why. end end end @@ -257,7 +257,7 @@ def @mod.foo assert_equal("Return value: Expected type String, got type Symbol with value :foo", lines[0]) # Note that the paths here could be relative or absolute depending on how this test was invoked. assert_match(%r{\ACaller: .*test.*/types/method_validation.rb:#{__LINE__ - 6}\z}, lines[1]) - assert_match(%r{\ADefinition: .*test.*/types/method_validation.rb:#{__LINE__ - 12}\z}, lines[2]) + assert_match(%r{\ADefinition: .*test.*/types/method_validation.rb:#{__LINE__ - 12} \(.*foo\)\z}, lines[2]) assert_empty(lines[3..-1]) end @@ -275,7 +275,7 @@ def @mod.foo(bar) assert_equal("Parameter 'bar': Expected type Integer, got type NilClass", lines[0]) # Note that the paths here could be relative or absolute depending on how this test was invoked. assert_match(%r{\ACaller: .*test.*/types/method_validation.rb:#{__LINE__ - 6}\z}, lines[1]) - assert_match(%r{\ADefinition: .*test.*/types/method_validation.rb:#{__LINE__ - 12}\z}, lines[2]) + assert_match(%r{\ADefinition: .*test.*/types/method_validation.rb:#{__LINE__ - 12} \(.*foo\)\z}, lines[2]) assert_empty(lines[3..-1]) end @@ -293,7 +293,7 @@ def @mod.foo(&blk) assert_equal("Block parameter 'blk': Expected type Proc, got type NilClass", lines[0]) # Note that the paths here could be relative or absolute depending on how this test was invoked. assert_match(%r{\ACaller: .*test.*/types/method_validation.rb:#{__LINE__ - 6}\z}, lines[1]) - assert_match(%r{\ADefinition: .*test.*/types/method_validation.rb:#{__LINE__ - 12}\z}, lines[2]) + assert_match(%r{\ADefinition: .*test.*/types/method_validation.rb:#{__LINE__ - 12} \(.*foo\)\z}, lines[2]) assert_empty(lines[3..-1]) end @@ -311,14 +311,14 @@ def @mod.foo assert_match(/Bind: Expected type Integer, got type Module with value # true' do + it 'errors if a prop is overridden without override => true' do error = assert_raises(ArgumentError) do class OverrideProps1 < OverrideProps prop :a, Integer diff --git a/gems/sorbet-runtime/test/types/props/constructor.rb b/gems/sorbet-runtime/test/types/props/constructor.rb index a5e0f5ffda..1921403be7 100644 --- a/gems/sorbet-runtime/test/types/props/constructor.rb +++ b/gems/sorbet-runtime/test/types/props/constructor.rb @@ -21,6 +21,9 @@ class UntypedField < T::Struct prop :untyped_prop, T.untyped, default: nil end + class NoProps < T::Struct + end + it "raises when omitting a required prop" do err = assert_raises(ArgumentError) do MyStruct.new(foo: 'foo') @@ -28,6 +31,20 @@ class UntypedField < T::Struct assert_equal("Missing required prop `name` for class `Opus::Types::Test::Props::ConstructorTest::MyStruct`", err.message) end + it "raises when giving unknown props" do + err = assert_raises(ArgumentError) do + MyStruct.new(name: 'Alex', foo: {}, bar1: 'bar1', bar2: 'bar2') + end + assert_equal("Opus::Types::Test::Props::ConstructorTest::MyStruct: Unrecognized properties: bar1, bar2", err.message) + end + + it "raises when giving unknown props to a no-prop class" do + err = assert_raises(ArgumentError) do + NoProps.new(bar1: 'bar1', bar2: 'bar2') + end + assert_equal("Opus::Types::Test::Props::ConstructorTest::NoProps: Unrecognized properties: bar1, bar2", err.message) + end + it 'allows required props to be omitted if they have a default value' do m = MyStruct.new(name: "Alex", foo: {color: :red}) @@ -86,6 +103,53 @@ def initialize(hash={}) end end + class WeakConstructorCustomInitializeStructInitializeBeforeProps + include T::Props + include T::Props::Serializable + include T::Props::WeakConstructor + + def initialize(hash={}) + @name = 'Doe' + @greeting = nil + @farewell = 'Ciao' + @bool = false + @color = 'red' + @type = 'value' + super + end + + prop :name, String + prop :greeting, String, default: "Hi" + prop :farewell, String, default: "Bye" + prop :bool, T::Boolean + prop :color, T.nilable(String) + prop :type, T.nilable(String), raise_on_nil_write: true + end + + class WeakConstructorCustomInitializeStructInitializeBetweenProps + include T::Props + include T::Props::Serializable + include T::Props::WeakConstructor + + prop :name, String + prop :greeting, String, default: "Hi" + prop :farewell, String, default: "Bye" + + def initialize(hash={}) + @name = 'Doe' + @greeting = nil + @farewell = 'Ciao' + @bool = false + @color = 'red' + @type = 'value' + super + end + + prop :bool, T::Boolean + prop :color, T.nilable(String) + prop :type, T.nilable(String), raise_on_nil_write: true + end + it 'does not clobber custom initialize T::Props::WeakConstructor' do c = WeakConstructorCustomInitializeStruct.new assert_equal('Doe', c.name) @@ -102,6 +166,38 @@ def initialize(hash={}) assert_equal(true, c.bool) assert_equal('blue', c.color) assert_equal('other', c.type) + + c = WeakConstructorCustomInitializeStructInitializeBeforeProps.new + assert_equal('Doe', c.name) + assert_equal('Hi', c.greeting) + assert_equal('Bye', c.farewell) + assert_equal(false, c.bool) + assert_equal('red', c.color) + assert_equal('value', c.type) + + c = WeakConstructorCustomInitializeStructInitializeBeforeProps.new(name: 'Alex', greeting: 'hello', farewell: 'goodbye', bool: true, color: 'blue', type: 'other') + assert_equal('Alex', c.name) + assert_equal('hello', c.greeting) + assert_equal('goodbye', c.farewell) + assert_equal(true, c.bool) + assert_equal('blue', c.color) + assert_equal('other', c.type) + + c = WeakConstructorCustomInitializeStructInitializeBetweenProps.new + assert_equal('Doe', c.name) + assert_equal('Hi', c.greeting) + assert_equal('Bye', c.farewell) + assert_equal(false, c.bool) + assert_equal('red', c.color) + assert_equal('value', c.type) + + c = WeakConstructorCustomInitializeStructInitializeBetweenProps.new(name: 'Alex', greeting: 'hello', farewell: 'goodbye', bool: true, color: 'blue', type: 'other') + assert_equal('Alex', c.name) + assert_equal('hello', c.greeting) + assert_equal('goodbye', c.farewell) + assert_equal(true, c.bool) + assert_equal('blue', c.color) + assert_equal('other', c.type) end class CustomInitializeStruct < T::Struct @@ -123,6 +219,45 @@ def initialize(hash={}) end end + class CustomInitializeStructInitializeBeforeProps < T::Struct + def initialize(hash={}) + @name = 'Doe' + @greeting = nil + @farewell = 'Ciao' + @bool = false + @color = 'red' + @type = 'value' + super + end + + prop :name, String + prop :greeting, String, default: "Hi" + prop :farewell, String, default: "Bye" + prop :bool, T::Boolean + prop :color, T.nilable(String) + prop :type, T.nilable(String), raise_on_nil_write: true + end + + class CustomInitializeStructInitializeBetweenProps < T::Struct + prop :name, String + prop :greeting, String, default: "Hi" + prop :farewell, String, default: "Bye" + + def initialize(hash={}) + @name = 'Doe' + @greeting = nil + @farewell = 'Ciao' + @bool = false + @color = 'red' + @type = 'value' + super + end + + prop :bool, T::Boolean + prop :color, T.nilable(String) + prop :type, T.nilable(String), raise_on_nil_write: true + end + it 'does not clobber custom initialize for T::Struct' do c = CustomInitializeStruct.new(name: 'Alex', bool: true, type: 'other') assert_equal('Alex', c.name) @@ -144,5 +279,372 @@ def initialize(hash={}) CustomInitializeStruct.new(bool: true, type: 'other') end assert_equal("Missing required prop `name` for class `Opus::Types::Test::Props::ConstructorTest::CustomInitializeStruct`", err.message) + + c = CustomInitializeStructInitializeBeforeProps.new(name: 'Alex', bool: true, type: 'other') + assert_equal('Alex', c.name) + assert_equal('Hi', c.greeting) + assert_equal('Bye', c.farewell) + assert_equal(true, c.bool) + assert_nil(c.color) + assert_equal('other', c.type) + + c = CustomInitializeStructInitializeBeforeProps.new(name: 'Alex', greeting: 'hello', farewell: 'goodbye', bool: true, color: 'blue', type: 'other') + assert_equal('Alex', c.name) + assert_equal('hello', c.greeting) + assert_equal('goodbye', c.farewell) + assert_equal(true, c.bool) + assert_equal('blue', c.color) + assert_equal('other', c.type) + + err = assert_raises(ArgumentError) do + CustomInitializeStructInitializeBeforeProps.new(bool: true, type: 'other') + end + assert_equal("Missing required prop `name` for class `Opus::Types::Test::Props::ConstructorTest::CustomInitializeStructInitializeBeforeProps`", err.message) + + c = CustomInitializeStructInitializeBetweenProps.new(name: 'Alex', bool: true, type: 'other') + assert_equal('Alex', c.name) + assert_equal('Hi', c.greeting) + assert_equal('Bye', c.farewell) + assert_equal(true, c.bool) + assert_nil(c.color) + assert_equal('other', c.type) + + c = CustomInitializeStructInitializeBetweenProps.new(name: 'Alex', greeting: 'hello', farewell: 'goodbye', bool: true, color: 'blue', type: 'other') + assert_equal('Alex', c.name) + assert_equal('hello', c.greeting) + assert_equal('goodbye', c.farewell) + assert_equal(true, c.bool) + assert_equal('blue', c.color) + assert_equal('other', c.type) + + err = assert_raises(ArgumentError) do + CustomInitializeStructInitializeBetweenProps.new(bool: true, type: 'other') + end + assert_equal("Missing required prop `name` for class `Opus::Types::Test::Props::ConstructorTest::CustomInitializeStructInitializeBetweenProps`", err.message) + end + + class DefaultsStruct < T::Struct + prop :prop1, T.nilable(String), default: "this is prop 1" + prop :prop2, T.nilable(Integer), factory: -> {123} + prop :trueprop, T::Boolean, default: true + prop :falseprop, T::Boolean, default: false + prop :factoryprop, T::Boolean, factory: -> {true} + + prop :untyped_prop1, T.untyped, default: nil + const :untyped_const1, T.untyped, default: nil + end + + class InvalidDefaultsStruct < T::Struct + prop :default_nilable, T.nilable(T::Boolean), default: 1 + prop :default_non_nilable, T::Boolean, default: 1 + prop :factory_nilable, T.nilable(T::Boolean), factory: -> {1} + prop :factory_non_nilable, T::Boolean, factory: -> {1} + end + + class NilDefaultRequired < T::Struct + const :integer_const_nil_default, Integer, default: nil + prop :integer_prop_nil_default, Integer, default: nil + end + + describe ':default and :factory' do + it 'defaults do not override on given values' do + m = DefaultsStruct.new(prop1: 'value', prop2: 17) + assert_equal('value', m.prop1) + assert_equal(17, m.prop2) + end + + it 'defaults and factories replace missing keys' do + m = DefaultsStruct.new(prop1: nil, prop2: nil) + assert_nil(m.prop1) + assert_nil(m.prop2) + assert_equal(true, m.trueprop) + assert_equal(true, m.factoryprop) + + m = DefaultsStruct.new + assert_equal('this is prop 1', m.prop1) + assert_equal(123, m.prop2) + assert_equal(true, m.trueprop) + assert_equal(true, m.factoryprop) + end + + it 'fixed defaults are not type-checked, factory defaults are type-checked' do + m = InvalidDefaultsStruct.new(factory_nilable: false, factory_non_nilable: false) + assert_equal(1, m.default_nilable) + assert_equal(1, m.default_non_nilable) + assert_equal(false, m.factory_nilable) + assert_equal(false, m.factory_non_nilable) + + err = assert_raises(TypeError) do + InvalidDefaultsStruct.new + end + assert_includes(err.message, "Can't set Opus::Types::Test::Props::ConstructorTest::InvalidDefaultsStruct.factory_nilable to 1 (instance of Integer) - need a T::Boolean") + + err = assert_raises(TypeError) do + InvalidDefaultsStruct.new(default_nilable: 1) + end + assert_includes(err.message, "Can't set Opus::Types::Test::Props::ConstructorTest::InvalidDefaultsStruct.default_nilable to 1 (instance of Integer) - need a T::Boolean") + + err = assert_raises(TypeError) do + InvalidDefaultsStruct.new(factory_nilable: 2) + end + assert_includes(err.message, "Can't set Opus::Types::Test::Props::ConstructorTest::InvalidDefaultsStruct.factory_nilable to 2 (instance of Integer) - need a T::Boolean") + end + end + + it 'returns the right required props' do + assert_equal( + Set[:trueprop, :falseprop, :factoryprop], + DefaultsStruct.decorator.required_props.to_set + ) + + assert_equal( + Set[:integer_const_nil_default, :integer_prop_nil_default], + NilDefaultRequired.decorator.required_props.to_set + ) + end + + class DefaultStringStruct < T::Struct + prop :stringprop_mutable, String, default: String.new('value') # rubocop:disable Performance/UnfreezeString + prop :stringprop_frozen, String, default: 'value' + end + + class DefaultArrayStruct < T::Struct + prop :arrayprop, T::Array[String], default: [] + prop :arrayprop_with_values, T::Array[String], default: [1] + end + + class DefaultHashStruct < T::Struct + prop :hashprop, T::Hash[String, String], default: {} + prop :hashprop_with_values, T::Hash[String, String], default: {1 => 1} + end + + describe 'default: with literals' do + it 'does not share structure for mutable strings' do + a = DefaultStringStruct.new + b = DefaultStringStruct.new + + refute_equal(a.stringprop_mutable.object_id, b.stringprop_mutable.object_id) + end + + it 'shares structure for frozen strings' do + a = DefaultStringStruct.new + b = DefaultStringStruct.new + + assert_equal(a.stringprop_frozen.object_id, b.stringprop_frozen.object_id) + end + + it 'does not share structure for empty arrays' do + a = DefaultArrayStruct.new + b = DefaultArrayStruct.new + + refute_equal(a.arrayprop.object_id, b.arrayprop.object_id) + end + + it 'does not share for populated arrays' do + a = DefaultArrayStruct.new + b = DefaultArrayStruct.new + + refute_equal(a.arrayprop_with_values.object_id, b.arrayprop_with_values.object_id) + end + + it 'does not share structure for empty hashes' do + a = DefaultHashStruct.new + b = DefaultHashStruct.new + + refute_equal(a.hashprop.object_id, b.hashprop.object_id) + end + + it 'does not share for populated hashes' do + a = DefaultHashStruct.new + b = DefaultHashStruct.new + + refute_equal(a.hashprop_with_values.object_id, b.hashprop_with_values.object_id) + end + end + + class ParentWithNoDefault < T::InexactStruct + prop :prop, String + end + + class ChildWithDefault < ParentWithNoDefault + prop :prop, String, default: '', override: true + end + + it 'uses default set on child in constructor' do + assert_equal('', ChildWithDefault.new.prop) + end + + class NilFieldStruct < T::Struct + prop :foo, T.nilable(Integer), raise_on_nil_write: true + prop :bar, T.nilable(String), raise_on_nil_write: true + prop :required, String, raise_on_nil_write: true + end + + class UntypedStruct < T::Struct + prop :untyped, T.untyped + prop :untyped_with_default, T.untyped, default: 123 + prop :untyped_with_raise_on_nil_write, T.untyped, raise_on_nil_write: true + end + + it 'forbids nil in constructor if raise_on_nil_write=true' do + err = assert_raises(ArgumentError) do + NilFieldStruct.new + end + assert_equal("Missing required prop `foo` for class `Opus::Types::Test::Props::ConstructorTest::NilFieldStruct`", err.message) + + err = assert_raises(TypeError) do + NilFieldStruct.new(foo: nil, bar: nil, required: nil) + end + assert_includes(err.message, "Can't set Opus::Types::Test::Props::ConstructorTest::NilFieldStruct.foo to nil (instance of NilClass) - need a Integer") + + err = assert_raises(TypeError) do + NilFieldStruct.new(foo: 1, bar: 'hey', required: nil) + end + assert_includes(err.message, "Can't set Opus::Types::Test::Props::ConstructorTest::NilFieldStruct.required to nil (instance of NilClass) - need a String") + end + + it 'does not forbid nil in constructor for T.untyped' do + UntypedStruct.new + UntypedStruct.new(untyped: nil) + UntypedStruct.new(untyped_with_default: true) + UntypedStruct.new(untyped_with_raise_on_nil_write: nil) + UntypedStruct.new(untyped: nil, untyped_with_default: nil, untyped_with_raise_on_nil_write: nil) + end + + it 'returns the right required props for T.untyped' do + assert_equal( + Set[:untyped, :untyped_with_default, :untyped_with_raise_on_nil_write], + UntypedStruct.decorator.required_props.to_set + ) + end + + class SetterValidate < T::Struct + prop :nilable_validated, T.nilable(Integer), setter_validate: ->(_prop, _value) {raise Error.new 'nilable_validated invalid'} + prop :default_validated, Integer, default: 1, setter_validate: ->(_prop, _value) {raise Error.new 'default_validated invalid'} + end + + class SetterValidateUntyped < T::Struct + prop :untyped, T.untyped, setter_validate: ->(_prop, _value) {raise Error.new 'untyped invalid'} + end + + class SetterValidateRaiseOnNilWrite < T::Struct + prop :raise_on_nil_write, T.nilable(Integer), setter_validate: ->(_prop, _value) {raise Error.new 'raise_on_nil_write invalid'}, raise_on_nil_write: true + end + + describe 'setter_validate' do + it 'does not run when a nilable is nil' do + SetterValidate.new + SetterValidate.new(nilable_validated: nil) + end + + it 'runs when a nilable is non-nil' do + err = assert_raises {SetterValidate.new(nilable_validated: 5)} + assert_equal('nilable_validated invalid', err.message) + end + + it 'runs on T.untyped' do + err = assert_raises {SetterValidateUntyped.new} + assert_equal('untyped invalid', err.message) + + err = assert_raises {SetterValidateUntyped.new(untyped: nil)} + assert_equal('untyped invalid', err.message) + + err = assert_raises {SetterValidateUntyped.new(untyped: 123)} + assert_equal('untyped invalid', err.message) + end + + it 'runs when raise_on_nil_write=true' do + err = assert_raises(ArgumentError) do + SetterValidateRaiseOnNilWrite.new + end + assert_equal("Missing required prop `raise_on_nil_write` for class `Opus::Types::Test::Props::ConstructorTest::SetterValidateRaiseOnNilWrite`", err.message) + + err = assert_raises {SetterValidateRaiseOnNilWrite.new(raise_on_nil_write: 5)} + assert_equal('raise_on_nil_write invalid', err.message) + + err = assert_raises {SetterValidateRaiseOnNilWrite.new(raise_on_nil_write: nil)} + assert_includes(err.message, "Can't set Opus::Types::Test::Props::ConstructorTest::SetterValidateRaiseOnNilWrite.raise_on_nil_write to nil (instance of NilClass) - need a Integer") + end + end + + class MyEnum < T::Enum + enums do + FooOne = new + FooTwo = new + end + end + + class MySerializable < T::Struct + prop :name, T.nilable(String) + prop :foo, T.nilable(T::Hash[T.any(String, Symbol), Object]) + end + + class ComplexStruct < T::Struct + prop :primitive, Integer + prop :nilable, T.nilable(Integer) + prop :nilable_on_read, T.nilable(Integer), raise_on_nil_write: true + prop :primitive_default, Integer, default: 0 + prop :primitive_nilable_default, T.nilable(Integer), default: 0 + prop :factory, Integer, factory: -> {0} + prop :primitive_array, T::Array[Integer] + prop :array_default, T::Array[Integer], default: [] + prop :primitive_hash, T::Hash[String, Integer] + prop :array_of_nilable, T::Array[T.nilable(Integer)] + prop :nilable_array, T.nilable(T::Array[Integer]) + prop :substruct, MySerializable + prop :nilable_substract, T.nilable(MySerializable) + prop :default_substruct, MySerializable, default: MySerializable.new + prop :array_of_substruct, T::Array[MySerializable] + prop :hash_of_substruct, T::Hash[String, MySerializable] + prop :infinity_float, Float, default: Float::INFINITY + prop :negative_infinity_float, Float, default: -Float::INFINITY + prop :nan_float, Float, default: Float::NAN + prop :enum, MyEnum + prop :nilable_enum, T.nilable(MyEnum) + prop :default_enum, MyEnum, default: MyEnum::FooOne + prop :deprecated_enum, Symbol, enum: %i[foo_one foo_two] + prop :nilable_deprecated_enum, T.nilable(Symbol), enum: %i[foo_one foo_two] + prop :default_deprecated_enum, Symbol, enum: %i[foo_one foo_two], default: :foo_one + end + + it 'can construct complex object' do + attributes = { + primitive: 1, + nilable_on_read: 1, + primitive_array: [1], + primitive_hash: {'1' => 1}, + array_of_nilable: [1, nil], + substruct: MySerializable.new(name: 'foo1'), + array_of_substruct: [MySerializable.new(name: 'foo2')], + hash_of_substruct: {'3' => MySerializable.new(name: 'foo3')}, + enum: MyEnum::FooOne, + deprecated_enum: :foo_one, + } + + assert_equal( + { + "primitive" => 1, + "nilable_on_read" => 1, + "primitive_default" => 0, + "primitive_nilable_default" => 0, + "factory" => 0, + "primitive_array" => [1], + "array_default" => [], + "primitive_hash" => {"1" => 1}, + "array_of_nilable" => [1, nil], + "substruct" => {"name" => "foo1"}, + "default_substruct" => {}, + "array_of_substruct" => [{"name" => "foo2"}], + "hash_of_substruct" => {"3" => {"name" => "foo3"}}, + "infinity_float" => Float::INFINITY, + "negative_infinity_float" => -Float::INFINITY, + "nan_float" => Float::NAN, + "enum" => "fooone", + "default_enum" => "fooone", + "deprecated_enum" => :foo_one, + "default_deprecated_enum" => :foo_one, + }, + ComplexStruct.new(attributes).serialize + ) end end diff --git a/gems/sorbet-runtime/test/types/props/decorator.rb b/gems/sorbet-runtime/test/types/props/decorator.rb index cde70db025..9bed83c71e 100644 --- a/gems/sorbet-runtime/test/types/props/decorator.rb +++ b/gems/sorbet-runtime/test/types/props/decorator.rb @@ -302,7 +302,7 @@ class ConstArrayStruct < T::Struct e = assert_raises(NoMethodError) do m.immutable = 'world' end - assert_match(/undefined method `immutable='/, e.message) + assert_match(/undefined method [`']immutable='/, e.message) end it 'const creates an immutable prop' do @@ -346,7 +346,7 @@ class MatrixStruct e = assert_raises do MatrixStruct.new.c = nil end - assert_match(/undefined method `c='/, e.message) + assert_match(/undefined method [`']c='/, e.message) e = assert_raises do MatrixStruct.new.d = nil end diff --git a/gems/sorbet-runtime/test/types/props/optional.rb b/gems/sorbet-runtime/test/types/props/optional.rb index acb9655f38..53b6734684 100644 --- a/gems/sorbet-runtime/test/types/props/optional.rb +++ b/gems/sorbet-runtime/test/types/props/optional.rb @@ -18,6 +18,9 @@ def self.prop2_source prop :prop2, T.nilable(Integer), factory: -> {DefaultsStruct.prop2_source += 1} prop :trueprop, T::Boolean, default: true prop :falseprop, T::Boolean, default: false + prop :default_integer, T.nilable(String), default: 123 + prop :default_array, T.nilable(String), default: [] + prop :default_mutable_string, T.nilable(String), default: String.new('hello') # rubocop:disable Performance/UnfreezeString end it 'uses default and factory props' do @@ -32,6 +35,24 @@ def self.prop2_source assert_equal(52, DefaultsStruct.prop2_source) end + it 'shares structure where appropriate' do + DefaultsStruct.prop2_source = 0 + m1 = DefaultsStruct.new + m2 = DefaultsStruct.new + + # Same object + assert_same(m1.prop1, m2.prop1) + assert_same(m1.trueprop, m2.trueprop) + assert_same(m1.falseprop, m2.falseprop) + assert_same(m1.default_integer, m2.default_integer) + + # Equal, but different object + refute_same(m1.default_array, m2.default_array) + refute_same(m1.default_mutable_string, m2.default_mutable_string) + assert_equal(m1.default_array, m2.default_array) + assert_equal(m1.default_mutable_string, m2.default_mutable_string) + end + it 'overrides defaults' do DefaultsStruct.prop2_source = 0 m = DefaultsStruct.new(prop1: 'foo', diff --git a/gems/sorbet-runtime/test/types/props/serializable.rb b/gems/sorbet-runtime/test/types/props/serializable.rb index 3c30135967..620daaf4f4 100644 --- a/gems/sorbet-runtime/test/types/props/serializable.rb +++ b/gems/sorbet-runtime/test/types/props/serializable.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true require_relative '../../test_helper' -require 'parser/current' - class Opus::Types::Test::Props::SerializableTest < Critic::Unit::UnitTest def assert_prop_error(match, &blk) ex = assert_raises(ArgumentError) do @@ -263,7 +261,7 @@ class ChildWithDefault < ParentWithNoDefault MySerializable.from_hash({'foo' => "Won't respond like hash"}) end - assert_includes(e.message, "undefined method `transform_values'") + assert_includes(e.message.tr("`", "'"), "undefined method 'transform_values'") assert_includes(e.message, "foo") assert_includes(e.message, "val.transform_values {|v| T::Props::Utils.deep_clone_object(v)}") end @@ -275,7 +273,7 @@ class ChildWithDefault < ParentWithNoDefault m.serialize end - assert_includes(e.message, "undefined method `transform_values'") + assert_includes(e.message.tr("`", "'"), "undefined method 'transform_values'") assert_includes(e.message, 'h["foo"] = @foo.transform_values {|v| T::Props::Utils.deep_clone_object(v)}') end end @@ -741,7 +739,7 @@ class CustomTypeWrapper struct.serialize end - assert_includes(e.message, "undefined method `serialize'") + assert_includes(e.message.tr("`", "'"), "undefined method 'serialize'") end it 'raises deserialize errors when props with a serializable subtype store the wrong datatype' do @@ -765,7 +763,7 @@ class CustomTypeWrapper struct.serialize end - assert_includes(e.message, "undefined method `map'") + assert_includes(e.message.tr("`", "'"), "undefined method 'map'") end it 'raises deserialize errors when props with an array of a custom subtype store the wrong datatype' do @@ -786,7 +784,7 @@ class CustomTypeWrapper assert_equal(CustomTypeStruct, storytime[:klass]) assert_equal(:array, storytime[:prop]) assert_equal(obj, storytime[:value]) - assert_includes(storytime[:error], "undefined method `map'") + assert_includes(storytime[:error].tr("`", "'"), "undefined method 'map'") end it 'round trips as hash key' do @@ -801,7 +799,7 @@ class CustomTypeWrapper struct.serialize end - assert_includes(e.message, "undefined method `transform_keys'") + assert_includes(e.message.tr("`", "'"), "undefined method 'transform_keys'") end it 'raises deserialize errors when props with a hash with keys of a custom subtype store the wrong datatype' do @@ -822,7 +820,7 @@ class CustomTypeWrapper assert_equal(CustomTypeStruct, storytime[:klass]) assert_equal(:hash_key, storytime[:prop]) assert_equal(obj, storytime[:value]) - assert_includes(storytime[:error], "undefined method `transform_keys'") + assert_includes(storytime[:error].tr("`", "'"), "undefined method 'transform_keys'") end it 'round trips as hash value' do @@ -837,7 +835,7 @@ class CustomTypeWrapper struct.serialize end - assert_includes(e.message, "undefined method `transform_values'") + assert_includes(e.message.tr("`", "'"), "undefined method 'transform_values'") end it 'raises deserialize errors when props with a hash with values of a custom subtype store the wrong datatype' do @@ -858,7 +856,7 @@ class CustomTypeWrapper assert_equal(CustomTypeStruct, storytime[:klass]) assert_equal(:hash_value, storytime[:prop]) assert_equal(obj, storytime[:value]) - assert_includes(storytime[:error], "undefined method `transform_values'") + assert_includes(storytime[:error].tr("`", "'"), "undefined method 'transform_values'") end it 'round trips as hash key and value' do @@ -873,7 +871,7 @@ class CustomTypeWrapper struct.serialize end - assert_includes(e.message, "undefined method `each_with_object'") + assert_includes(e.message.tr("`", "'"), "undefined method 'each_with_object'") end it 'raises deserialize errors when props with a hash with keys/values of a custom subtype store the wrong datatype' do @@ -894,7 +892,7 @@ class CustomTypeWrapper assert_equal(CustomTypeStruct, storytime[:klass]) assert_equal(:hash_both, storytime[:prop]) assert_equal(obj, storytime[:value]) - assert_includes(storytime[:error], "undefined method `each_with_object'") + assert_includes(storytime[:error].tr("`", "'"), "undefined method 'each_with_object'") end end @@ -1244,7 +1242,7 @@ class ComplexStruct < T::Struct prop :nilable_array, T.nilable(T::Array[Integer]) prop :substruct, MySerializable prop :nilable_substract, T.nilable(MySerializable) - prop :default_substruct, MySerializable, default: MySerializable.new + prop :default_substruct, MySerializable, default: MySerializable.from_hash({'name' => 'default'}) prop :array_of_substruct, T::Array[MySerializable] prop :hash_of_substruct, T::Hash[String, MySerializable] prop :custom_type, CustomType @@ -1258,6 +1256,124 @@ class ComplexStruct < T::Struct prop :defaulted_unidentified_type, Object, default: Object.new prop :hash_with_unidentified_types, T::Hash[Object, Object] prop :infinity_float, Float, default: Float::INFINITY + prop :negative_infinity_float, Float, default: -Float::INFINITY + prop :nan_float, Float, default: Float::NAN + prop :enum, MyEnum + prop :nilable_enum, T.nilable(MyEnum) + prop :default_enum, MyEnum, default: MyEnum::FOO + prop :deprecated_enum, Symbol, enum: %i[foo_one foo_two] + prop :nilable_deprecated_enum, T.nilable(Symbol), enum: %i[foo_one foo_two] + prop :default_deprecated_enum, Symbol, enum: %i[foo_one foo_two], default: :foo_one + end + + it 'can serialize a complex object' do + attributes = { + primitive: 1, + nilable_on_read: 1, + primitive_array: [1], + primitive_hash: {'1' => 1}, + array_of_nilable: [1, nil], + substruct: MySerializable.from_hash({'name' => 'foo1'}), + array_of_substruct: [MySerializable.from_hash({'name' => 'foo2'})], + hash_of_substruct: {'3' => MySerializable.from_hash({'name' => 'foo3'})}, + custom_type: CustomType.deserialize('foo1'), + array_of_custom_type: [CustomType.deserialize('foo2')], + hash_of_custom_type_to_substruct: {CustomType.deserialize('foo3') => MySerializable.from_hash({'name' => 'foo4'})}, + unidentified_type: 1, + array_of_unidentified_type: [1], + hash_with_unidentified_types: {2 => 3}, + defaulted_unidentified_type: {3 => 4}, + enum: MyEnum::FOO, + deprecated_enum: :foo_one, + } + + assert_equal( + { + "primitive" => 1, + "nilable_on_read" => 1, + "primitive_default" => 0, + "primitive_nilable_default" => 0, + "factory" => 0, + "primitive_array" => [1], + "array_default" => [], + "primitive_hash" => {"1" => 1}, + "array_of_nilable" => [1, nil], + "substruct" => {"name" => "foo1"}, + "default_substruct" => {"name" => "default"}, + "array_of_substruct" => [{"name" => "foo2"}], + "hash_of_substruct" => {"3" => {"name" => "foo3"}}, + "custom_type" => "foo1", + "default_custom_type" => nil, + "array_of_custom_type" => ["foo2"], + "hash_of_custom_type_to_substruct" => {"foo3" => {"name" => "foo4"}}, + "unidentified_type" => 1, + "array_of_unidentified_type" => [1], + "defaulted_unidentified_type" => {3 => 4}, + "hash_with_unidentified_types" => {2 => 3}, + "infinity_float" => Float::INFINITY, + "negative_infinity_float" => -Float::INFINITY, + "nan_float" => Float::NAN, + "enum" => "foo", + "default_enum" => "foo", + "deprecated_enum" => :foo_one, + "default_deprecated_enum" => :foo_one, + }, ComplexStruct.new(attributes).serialize(false) + ) + end + + it 'can deserialize a complex object' do + attributes = { + 'primitive' => 1, + 'nilable_on_read' => 1, + 'primitive_array' => [1], + 'primitive_hash' => {'1' => 1}, + 'array_of_nilable' => [1, nil], + 'substruct' => {'name' => 'foo1'}, + 'array_of_substruct' => [{'name' => 'foo2'}], + 'hash_of_substruct' => {'3' => {'name' => 'foo3'}}, + 'custom_type' => 'foo1', + 'array_of_custom_type' => ['foo2'], + 'hash_of_custom_type_to_substruct' => {'foo3' => {'name' => 'foo4'}}, + 'unidentified_type' => 1, + 'array_of_unidentified_type' => [1], + 'hash_with_unidentified_types' => {2 => 3}, + 'defaulted_unidentified_type' => {3 => 4}, + 'enum' => 'foo', + 'deprecated_enum' => :foo_one, + } + + assert_equal( + { + "primitive" => 1, + "nilable_on_read" => 1, + "primitive_default" => 0, + # "primitive_nilable_default"=>0, # Possible bug: This is not in the expected hash, but seems like it should be. + "factory" => 0, + "primitive_array" => [1], + "array_default" => [], + "primitive_hash" => {"1" => 1}, + "array_of_nilable" => [1, nil], + "substruct" => {"name" => "foo1"}, + "default_substruct" => {"name" => "default"}, + "array_of_substruct" => [{"name" => "foo2"}], + "hash_of_substruct" => {"3" => {"name" => "foo3"}}, + "custom_type" => "foo1", + "default_custom_type" => nil, + "array_of_custom_type" => ["foo2"], + "hash_of_custom_type_to_substruct" => {"foo3" => {"name" => "foo4"}}, + "unidentified_type" => 1, + "array_of_unidentified_type" => [1], + "defaulted_unidentified_type" => {3 => 4}, + "hash_with_unidentified_types" => {2 => 3}, + "infinity_float" => Float::INFINITY, + "negative_infinity_float" => -Float::INFINITY, + "nan_float" => Float::NAN, + "enum" => "foo", + "default_enum" => "foo", + "deprecated_enum" => :foo_one, + "default_deprecated_enum" => :foo_one, + }, ComplexStruct.from_hash(attributes).serialize(false) + ) end describe 'generated code' do diff --git a/gems/sorbet-runtime/test/types/props/struct.rb b/gems/sorbet-runtime/test/types/props/struct.rb index 70c207e12f..0d0e0d4b8d 100644 --- a/gems/sorbet-runtime/test/types/props/struct.rb +++ b/gems/sorbet-runtime/test/types/props/struct.rb @@ -90,7 +90,7 @@ class TestStruct < T::Struct prop :foo5, T.nilable(T::Array[SubStruct]) end - class StructWithReqiredField < T::Struct + class StructWithRequiredField < T::Struct prop :foo1, Integer prop :foo2, Integer end @@ -146,30 +146,30 @@ class StructWithReqiredField < T::Struct end it 'tstruct need to initialize required fields' do - doc = StructWithReqiredField.new(foo1: 10, foo2: 20) + doc = StructWithRequiredField.new(foo1: 10, foo2: 20) assert_equal(10, doc.foo1) assert_equal(20, doc.foo2) assert_raises(ArgumentError) do - StructWithReqiredField.new(foo2: 20) + StructWithRequiredField.new(foo2: 20) end assert_raises(ArgumentError) do - StructWithReqiredField.new(foo1: 10) + StructWithRequiredField.new(foo1: 10) end end it 'tstruct deserialize different fields' do - doc = StructWithReqiredField.from_hash({'foo1' => 10, 'foo2' => 20}) + doc = StructWithRequiredField.from_hash({'foo1' => 10, 'foo2' => 20}) assert_equal(10, doc.foo1) assert_equal(20, doc.foo2) assert_raises(RuntimeError) do - StructWithReqiredField.from_hash({'foo2' => 20}) + StructWithRequiredField.from_hash({'foo2' => 20}) end # The code should behave for deserialization. assert_raises(RuntimeError) do - StructWithReqiredField.from_hash({'foo1' => 10}) + StructWithRequiredField.from_hash({'foo1' => 10}) end end end diff --git a/gems/sorbet-runtime/test/types/sig.rb b/gems/sorbet-runtime/test/types/sig.rb index 4508d05bf7..9adeed9dcd 100644 --- a/gems/sorbet-runtime/test/types/sig.rb +++ b/gems/sorbet-runtime/test/types/sig.rb @@ -34,7 +34,7 @@ def foo e = assert_raises do klass.new.foo end - assert_match(/undefined method `sig' for/, e.message) + assert_match(/undefined method [`']sig' for/, e.message) end # Enable $VERBOSE and redirect stderr to a string for the duration of the diff --git a/gems/sorbet-runtime/test/types/types.rb b/gems/sorbet-runtime/test/types/types.rb index 013c592d26..731f9ce85a 100644 --- a/gems/sorbet-runtime/test/types/types.rb +++ b/gems/sorbet-runtime/test/types/types.rb @@ -233,6 +233,10 @@ def self.name it 'valid? does not allocate' do skip unless check_alloc_counts + + # Call a method on the type to trigger the lazy initialization + assert_equal("T.nilable(T.any(Integer, T::Boolean))", @type.name) + allocs_when_valid = counting_allocations {@type.valid?(0)} assert_equal(0, allocs_when_valid) @@ -372,6 +376,9 @@ def self.name @klass.include(Mixin1) @klass.include(Mixin2) + # Call a method on the type to trigger the lazy initialization + assert_equal("T.all(Opus::Types::Test::TypesTest::Mixin1, Opus::Types::Test::TypesTest::Mixin2)", @type.name) + allocs_when_valid = counting_allocations {@type.valid?(@klass)} assert_equal(0, allocs_when_valid) @@ -407,6 +414,10 @@ def self.name it 'valid? does not allocate' do skip unless check_alloc_counts + + # Call a method on the type to trigger the lazy initialization + assert_equal("[String, T::Boolean]", @type.name) + arr = ["foo", false] allocs_when_valid = counting_allocations {@type.valid?(arr)} assert_equal(0, allocs_when_valid) @@ -459,6 +470,10 @@ def self.name it 'valid? does not allocate' do skip unless check_alloc_counts + + # Call a method on the type to trigger the lazy initialization + assert_equal("{a: String, b: T::Boolean, c: T.nilable(Numeric)}", @type.name) + h = {a: 'foo', b: false, c: nil} allocs_when_valid = counting_allocations {@type.valid?(h)} assert_equal(0, allocs_when_valid) @@ -1090,18 +1105,42 @@ def each end end + describe 'NotTyped' do + it 'builds properly' do + type = T::Private::Types::NotTyped.new + type.build_type + assert_equal('', type.name) + end + end + + describe 'StringHolder' do + it 'builds properly' do + type = T::Private::Types::StringHolder.new("String") + type.build_type + assert_equal('String', type.name) + end + end + describe 'TypeAlias' do + it 'builds properly' do + type = T.type_alias {String} + type.build_type + assert_equal('String', type.name) + end + it 'delegates name' do type = T.type_alias {T.any(Integer, String)} assert_equal('T.any(Integer, String)', type.name) end it 'delegates equality' do + # rubocop:disable Lint/BinaryOperatorWithIdenticalOperands assert(T.any(Integer, String) == T.type_alias {T.any(Integer, String)}) assert(T.type_alias {T.any(Integer, String)} == T.any(Integer, String)) assert(T.type_alias {T.any(Integer, String)} == T.type_alias {T.any(Integer, String)}) refute(T.type_alias {T.any(Integer, Float)} == T.type_alias {T.any(Integer, String)}) + # rubocop:enable Lint/BinaryOperatorWithIdenticalOperands end it 'passes a validation' do @@ -1170,21 +1209,21 @@ def goodbye it 'fails validation with a value not from the enum' do msg = check_error_message_for_obj(@type, :baz) - assert_equal("Expected type T.deprecated_enum([:foo, :bar]), got :baz", msg) + assert_equal("Expected type T.deprecated_enum([:bar, :foo]), got :baz", msg) end it 'does not coerce types' do msg = check_error_message_for_obj(@type, 'foo') - assert_equal('Expected type T.deprecated_enum([:foo, :bar]), got "foo"', msg) + assert_equal('Expected type T.deprecated_enum([:bar, :foo]), got "foo"', msg) type = T.deprecated_enum(%w[foo bar]) msg = check_error_message_for_obj(type, :foo) - assert_equal('Expected type T.deprecated_enum(["foo", "bar"]), got :foo', msg) + assert_equal('Expected type T.deprecated_enum(["bar", "foo"]), got :foo', msg) end it 'fails validation with a nil value' do msg = check_error_message_for_obj(@type, nil) - assert_equal("Expected type T.deprecated_enum([:foo, :bar]), got nil", msg) + assert_equal("Expected type T.deprecated_enum([:bar, :foo]), got nil", msg) end end @@ -1675,6 +1714,12 @@ def refute_subtype(lhs, rhs) end describe 'type variables' do + it 'builds properly' do + type = T::Types::TypeParameter.new(:FOO) + type.build_type + assert_equal('T.type_parameter(:FOO)', type.name) + end + it 'type members are subtypes of everything' do assert_subtype(T::Types::TypeMember.new(:in), T.untyped) assert_subtype(T::Types::TypeMember.new(:in), String) diff --git a/gems/sorbet-runtime/test/types/validate_override_shape.rb b/gems/sorbet-runtime/test/types/validate_override_shape.rb index f9e5d39087..4a843af01c 100644 --- a/gems/sorbet-runtime/test/types/validate_override_shape.rb +++ b/gems/sorbet-runtime/test/types/validate_override_shape.rb @@ -219,7 +219,7 @@ def foo; end klass = Class.new(AbstractBase) do extend T::Sig sig {override.void} - def initialize; end + def initialize; super; end def foo 0 @@ -236,7 +236,7 @@ def foo .params(x: Integer) .void end - def initialize(x); end + def initialize(x); super; end end err = assert_raises(RuntimeError) do klass.new(0) diff --git a/gems/sorbet-runtime/test/types/validate_override_types.rb b/gems/sorbet-runtime/test/types/validate_override_types.rb index 1c2d81a6f0..929b43c051 100644 --- a/gems/sorbet-runtime/test/types/validate_override_types.rb +++ b/gems/sorbet-runtime/test/types/validate_override_types.rb @@ -124,6 +124,22 @@ def example; Object; end child.new.example end + it "allows T::Class to be compatible with Class" do + parent = Class.new do + extend T::Sig + sig {overridable.returns(T::Class[T.anything])} + def example; Object; end + end + + child = Class.new(parent) do + extend T::Sig + sig {override.returns(Class)} + def example; Object; end + end + + child.new.example + end + it "allows T::Class to be compatible with T.class_of in child" do parent = Class.new do extend T::Sig @@ -156,5 +172,21 @@ def example; {}; end child.new.example end + it "allows returns(T.anything) to be compatible with .void" do + parent = Class.new do + extend T::Sig + sig {overridable.void} + def example; end + end + + child = Class.new(parent) do + extend T::Sig + sig {override.returns(T.anything)} + def example; end + end + + child.new.example + end + end end diff --git a/gems/sorbet-runtime/test/wholesome/tenum_oj.test.rb b/gems/sorbet-runtime/test/wholesome/tenum_oj.test.rb index 6c7d7aef70..2db8290ec8 100644 --- a/gems/sorbet-runtime/test/wholesome/tenum_oj.test.rb +++ b/gems/sorbet-runtime/test/wholesome/tenum_oj.test.rb @@ -15,7 +15,7 @@ module Wholesome; end end end -class Opus::Types::Test::Wholesome::TEnumOj < MiniTest::Spec +class Opus::Types::Test::Wholesome::TEnumOj < Minitest::Spec class MyEnum < T::Enum enums do X = new diff --git a/gems/sorbet-runtime/tools/generate_call_validation.cc b/gems/sorbet-runtime/tools/generate_call_validation.cc index f0fa935f7f..817df66bb4 100644 --- a/gems/sorbet-runtime/tools/generate_call_validation.cc +++ b/gems/sorbet-runtime/tools/generate_call_validation.cc @@ -13,6 +13,7 @@ struct Options { enum class ValidatorKind { Method, + MethodSkipReturn, Procedure, }; @@ -25,6 +26,8 @@ string_view validatorKindToString(ValidatorKind kind) { switch (kind) { case ValidatorKind::Method: return "method"sv; + case ValidatorKind::MethodSkipReturn: + return "method_skip_return"sv; case ValidatorKind::Procedure: return "procedure"sv; } @@ -79,6 +82,8 @@ void generateCreateValidatorFastDispatcher(ValidatorKind kind, TypeKind type) { " end\n", typeString); break; + case ValidatorKind::MethodSkipReturn: + break; case ValidatorKind::Procedure: break; } @@ -125,6 +130,7 @@ void generateCreateValidatorFast(const Options &options, ValidatorKind kind, Typ case ValidatorKind::Method: returnTypeArg = ", return_type"; break; + case ValidatorKind::MethodSkipReturn: case ValidatorKind::Procedure: returnTypeArg = ""; break; @@ -182,6 +188,7 @@ void generateCreateValidatorFast(const Options &options, ValidatorKind kind, Typ case ValidatorKind::Method: returnValueVar = "return_value = "; break; + case ValidatorKind::MethodSkipReturn: case ValidatorKind::Procedure: returnValueVar = ""; break; @@ -197,22 +204,25 @@ void generateCreateValidatorFast(const Options &options, ValidatorKind kind, Typ } fmt::print("&blk)\n"); - const char *returnValueTypecheck; - switch (type) { - case TypeKind::Simple: - returnValueTypecheck = "return_value.is_a?(return_type)"; - break; - case TypeKind::Complex: - returnValueTypecheck = "return_type.valid?(return_value)"; - break; - } - switch (kind) { case ValidatorKind::Procedure: fmt::print(" T::Private::Types::Void::VOID\n"); break; - case ValidatorKind::Method: + case ValidatorKind::MethodSkipReturn: + break; + + case ValidatorKind::Method: { + const char *returnValueTypecheck; + switch (type) { + case TypeKind::Simple: + returnValueTypecheck = "return_value.is_a?(return_type)"; + break; + case TypeKind::Complex: + returnValueTypecheck = "return_type.valid?(return_value)"; + break; + } + fmt::print(" unless {}\n" " message = method_sig.return_type.error_message_for_obj(return_value)\n" " if message\n" @@ -230,6 +240,7 @@ void generateCreateValidatorFast(const Options &options, ValidatorKind kind, Typ " return_value\n", returnValueTypecheck); break; + } } fmt::print(" end\n" @@ -252,6 +263,11 @@ int generateCallValidation(const Options &options) { generateCreateValidatorFast(options, ValidatorKind::Method, TypeKind::Simple, i); } + generateCreateValidatorFastDispatcher(ValidatorKind::MethodSkipReturn, TypeKind::Simple); + for (size_t i = 0; i <= MAX_ARITY; i++) { + generateCreateValidatorFast(options, ValidatorKind::MethodSkipReturn, TypeKind::Simple, i); + } + generateCreateValidatorFastDispatcher(ValidatorKind::Procedure, TypeKind::Simple); for (size_t i = 0; i <= MAX_ARITY; i++) { generateCreateValidatorFast(options, ValidatorKind::Procedure, TypeKind::Simple, i); @@ -262,6 +278,11 @@ int generateCallValidation(const Options &options) { generateCreateValidatorFast(options, ValidatorKind::Method, TypeKind::Complex, i); } + generateCreateValidatorFastDispatcher(ValidatorKind::MethodSkipReturn, TypeKind::Complex); + for (size_t i = 0; i <= MAX_ARITY; i++) { + generateCreateValidatorFast(options, ValidatorKind::MethodSkipReturn, TypeKind::Complex, i); + } + generateCreateValidatorFastDispatcher(ValidatorKind::Procedure, TypeKind::Complex); for (size_t i = 0; i <= MAX_ARITY; i++) { generateCreateValidatorFast(options, ValidatorKind::Procedure, TypeKind::Complex, i); diff --git a/gems/sorbet/bin/srb b/gems/sorbet/bin/srb index cfc2b3f443..d3691755b3 100755 --- a/gems/sorbet/bin/srb +++ b/gems/sorbet/bin/srb @@ -72,7 +72,7 @@ typecheck() { gems_path="${without_bin_srb%/sorbet*}" # /path/to/gems/sorbet-static-0.0.1-darwin-17/libexec/sorbet - # (assumes people only have one platform-depdendent gem installed per version) + # (assumes people only have one platform-dependent gem installed per version) guess_sorbet=("$gems_path/sorbet-static$version_suffix"*/libexec/sorbet) if [[ -f "${guess_sorbet[0]}" ]]; then diff --git a/gems/sorbet/bin/srb-rbi b/gems/sorbet/bin/srb-rbi index 16b708797a..4eedc5d814 100755 --- a/gems/sorbet/bin/srb-rbi +++ b/gems/sorbet/bin/srb-rbi @@ -43,7 +43,7 @@ module Sorbet::Private::Main To switch, add Tapioca to your Gemfile then run #{cyan("bundle install")} to install it: - #{cyan("gem \"tapioca\", require: false, :group => :development")} + #{cyan("gem \"tapioca\", require: false, :group => [:development, :test]")} Once Tapioca is installed, simply run #{cyan("tapioca init")} to initialize your project with Sorbet and generate the necessary RBI files: @@ -120,7 +120,7 @@ actions: Kernel.exit(1) end else - puts "\nNot running interactivly. Set SRB_YES=1 environment variable to proceed" + puts "\nNot running interactively. Set SRB_YES=1 environment variable to proceed" Kernel.exit(1) end end diff --git a/gems/sorbet/lib/gem_loader.rb b/gems/sorbet/lib/gem_loader.rb index 2698860176..66858d744a 100644 --- a/gems/sorbet/lib/gem_loader.rb +++ b/gems/sorbet/lib/gem_loader.rb @@ -1183,7 +1183,7 @@ class Sorbet::Private::GemLoader end, } - # This is so that the autoloader doesn't treat these as manditory requires + # This is so that the autoloader doesn't treat these as mandatory requires # before loading this file def self.my_require(gem) require gem # rubocop:disable PrisonGuard/NoDynamicRequire diff --git a/gems/sorbet/lib/hidden-definition-finder.rb b/gems/sorbet/lib/hidden-definition-finder.rb index 5a05391900..2ff789be7f 100755 --- a/gems/sorbet/lib/hidden-definition-finder.rb +++ b/gems/sorbet/lib/hidden-definition-finder.rb @@ -436,7 +436,7 @@ def capture_stderr valid.split("\n").each do |line| category = categorize(line) if category == :errors - # Don't ever switch to errors output permanantly + # Don't ever switch to errors output permanently output[category] << line + "\n" next end diff --git a/gems/sorbet/lib/real_stdlib.rb b/gems/sorbet/lib/real_stdlib.rb index 4a83f5d11f..93e5b22549 100644 --- a/gems/sorbet/lib/real_stdlib.rb +++ b/gems/sorbet/lib/real_stdlib.rb @@ -7,6 +7,11 @@ def self.real_is_a?(o, klass) @real_is_a.bind(o).call(klass) end + def self.real_respond_to?(o, method) + @real_respond_to ||= Object.instance_method(:respond_to?) + @real_respond_to.bind(o).call(method) + end + def self.real_constants(mod) @real_constants ||= Module.instance_method(:constants) @real_constants.bind(mod).call(false) diff --git a/gems/sorbet/lib/serialize.rb b/gems/sorbet/lib/serialize.rb index 3f9c7e64b9..966ef78119 100644 --- a/gems/sorbet/lib/serialize.rb +++ b/gems/sorbet/lib/serialize.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true # typed: true -require 'bigdecimal' - class Sorbet::Private::Serialize DENYLIST_CONSTANTS = [ ['DidYouMean', :NameErrorCheckers], # https://github.com/yuki24/did_you_mean/commit/b72fdbe194401f1be21f8ad7b6e3f784a0ad197d @@ -56,7 +54,7 @@ def class_or_module(class_name) superclass_str = !superclass_str || superclass_str.empty? ? '' : " < #{superclass_str}" ret << (Sorbet::Private::RealStdlib.real_is_a?(klass, Class) ? "class #{class_name}#{superclass_str}\n" : "module #{class_name}\n") - # We don't use .included_modules since that also has all the aweful things + # We don't use .included_modules since that also has all the awful things # that are mixed into Object. This way we at least have a delimiter before # the awefulness starts (the superclass). Sorbet::Private::RealStdlib.real_ancestors(klass).each do |ancestor| @@ -144,14 +142,14 @@ def class_or_module(class_name) initialize = nil end if initialize && initialize.owner == klass - # This method never apears in the reflection list... + # This method never appears in the reflection list... instance_methods += [:initialize] end Sorbet::Private::RealStdlib.real_ancestors(klass).reject {|ancestor| @constant_cache.name_by_class(ancestor)}.each do |ancestor| instance_methods += ancestor.instance_methods(false) end - # uniq here is required because we populate additional methos from anonymous superclasses and there + # uniq here is required because we populate additional methods from anonymous superclasses and there # might be duplicates methods += instance_methods.sort.uniq.map do |method_sym| begin @@ -189,8 +187,9 @@ def alias(base, other_name) end def comparable?(value) - return false if Sorbet::Private::RealStdlib.real_is_a?(value, BigDecimal) && value.nan? - return false if Sorbet::Private::RealStdlib.real_is_a?(value, Float) && value.nan? + return false if Sorbet::Private::RealStdlib.real_is_a?(value, Numeric) && + Sorbet::Private::RealStdlib.real_respond_to?(value, :nan?) && + value.nan? return false if Sorbet::Private::RealStdlib.real_is_a?(value, Complex) true end diff --git a/hashing/hashing.cc b/hashing/hashing.cc index 9e30198528..ab2fd7447e 100644 --- a/hashing/hashing.cc +++ b/hashing/hashing.cc @@ -76,9 +76,11 @@ unique_ptr computeFileHashForAST(spdlog::logger &logger, unique_ vector single; single.emplace_back(move(file)); + // We run computeFileHashForAST with the empty set of options which means we can skip calling pipeline::package() + auto workers = WorkerPool::create(0, lgs->tracer()); core::FoundDefHashes foundHashes; // out parameter - realmain::pipeline::resolve(lgs, move(single), opts(), *workers, &foundHashes); + realmain::pipeline::nameAndResolve(lgs, move(single), opts(), *workers, &foundHashes); return make_unique(move(*lgs->hash()), move(usageHash), move(foundHashes)); } @@ -90,6 +92,8 @@ core::FileRef makeEmptyGlobalStateForFile(spdlog::logger &logger, shared_ptrrequiresAncestorEnabled = hashingOpts.requiresAncestorEnabled; lgs->ruby3KeywordArgs = hashingOpts.ruby3KeywordArgs; + lgs->typedSuper = hashingOpts.typedSuper; + lgs->suppressPayloadSuperclassRedefinitionFor = hashingOpts.suppressPayloadSuperclassRedefinitionFor; { core::UnfreezeFileTable fileTableAccess(*lgs); auto fref = lgs->enterFile(forWhat); @@ -164,7 +168,7 @@ void Hashing::computeFileHashes(const vector> &files, spd vector Hashing::indexAndComputeFileHashes(unique_ptr &gs, const realmain::options::Options &opts, - spdlog::logger &logger, vector &files, + spdlog::logger &logger, absl::Span files, WorkerPool &workers, const unique_ptr &kvstore) { auto asts = realmain::pipeline::index(*gs, files, opts, workers, kvstore); diff --git a/hashing/hashing.h b/hashing/hashing.h index cb78a897b3..e4ec2fab06 100644 --- a/hashing/hashing.h +++ b/hashing/hashing.h @@ -50,7 +50,7 @@ class Hashing final { */ static std::vector indexAndComputeFileHashes(std::unique_ptr &gs, const realmain::options::Options &opts, - spdlog::logger &logger, std::vector &files, WorkerPool &workers, + spdlog::logger &logger, absl::Span files, WorkerPool &workers, const std::unique_ptr &kvstore); }; } // namespace sorbet::hashing diff --git a/infer/SigSuggestion.cc b/infer/SigSuggestion.cc index 86b98c32f0..e5e1f57cac 100644 --- a/infer/SigSuggestion.cc +++ b/infer/SigSuggestion.cc @@ -52,7 +52,8 @@ core::TypePtr extractArgType(core::Context ctx, cfg::Send &send, core::DispatchC void extractSendArgumentKnowledge(core::Context ctx, core::LocOffsets bindLoc, cfg::Send *snd, const UnorderedMap> &blockLocals, - UnorderedMap &blockArgRequirements) { + UnorderedMap &blockArgRequirements, + const core::NameRef currentMethodName) { InlinedVector, 2> typeAndOriginsOwner; InlinedVector args; @@ -87,7 +88,8 @@ void extractSendArgumentKnowledge(core::Context ctx, core::LocOffsets bindLoc, c snd->link, originForUninitialized, snd->isPrivateOk, - suppressErrors}; + suppressErrors, + currentMethodName}; auto dispatchInfo = snd->recv.type.dispatchCall(ctx, dispatchArgs); // See if we can learn what types should they have @@ -206,7 +208,9 @@ UnorderedMap guessArgumentTypes(core::Context ctx, } if (shouldFindArgumentTypes) { - extractSendArgumentKnowledge(ctx, bind.loc, snd, blockLocals, blockArgRequirements); + auto currentMethodName = cfg->symbol.data(ctx)->name; + extractSendArgumentKnowledge(ctx, bind.loc, snd, blockLocals, blockArgRequirements, + currentMethodName); } } @@ -257,8 +261,8 @@ UnorderedMap guessArgumentTypes(core::Context ctx, return argTypesForBBToPass[cfg->deadBlock()->id]; } -core::MethodRef closestOverridenMethod(core::Context ctx, core::ClassOrModuleRef enclosingClassSymbol, - core::NameRef name) { +core::MethodRef closestOverriddenMethod(core::Context ctx, core::ClassOrModuleRef enclosingClassSymbol, + core::NameRef name) { auto enclosingClass = enclosingClassSymbol.data(ctx); ENFORCE(enclosingClass->flags.isLinearizationComputed, "Should have been linearized by resolver"); @@ -278,7 +282,7 @@ core::MethodRef closestOverridenMethod(core::Context ctx, core::ClassOrModuleRef if (superMethod.exists()) { return superMethod; } else { - return closestOverridenMethod(ctx, superClass, name); + return closestOverriddenMethod(ctx, superClass, name); } } @@ -292,7 +296,7 @@ bool childNeedsOverride(core::Context ctx, core::MethodRef childSymbol, core::Me !parentSymbol.data(ctx)->loc().file().data(ctx).isRBI() && // that isn't the constructor... childSymbol.data(ctx)->name != core::Names::initialize() && - // and wasn't Rewriter synthesized (beause we can't change DSL'd sigs). + // and wasn't Rewriter synthesized (because we can't change DSL'd sigs). !parentSymbol.data(ctx)->flags.isRewriterSynthesized && // It has a sig... parentSymbol.data(ctx)->resultType != nullptr && @@ -318,7 +322,13 @@ optional SigSuggestion::maybeSuggestSig(core::Conte } core::TypePtr guessedReturnType; - if (!constr.isEmpty()) { + if (ctx.file.data(ctx).isRBI()) { + // We will always infer a return type of `NilClass` in an RBI file, because all RBI files + // have empty bodies. That's not useful--we'd rather infer `T.untyped` so that LSP + // replaces it with a tabstop the user can cycle through. + guessedReturnType = core::Types::untypedUntracked(); + + } else if (!constr.isEmpty()) { if (!constr.solve(ctx)) { return nullopt; } @@ -350,7 +360,7 @@ optional SigSuggestion::maybeSuggestSig(core::Conte auto guessedArgumentTypes = guessArgumentTypes(ctx, methodSymbol, cfg); auto enclosingClass = methodSymbol.enclosingClass(ctx); - auto closestMethod = closestOverridenMethod(ctx, enclosingClass, methodSymbol.data(ctx)->name); + auto closestMethod = closestOverriddenMethod(ctx, enclosingClass, methodSymbol.data(ctx)->name); fmt::memory_buffer ss; if (closestMethod.exists()) { @@ -425,7 +435,7 @@ optional SigSuggestion::maybeSuggestSig(core::Conte // TODO: maybe combine the old and new types in some way? chosenType = oldType; } - auto options = core::ShowOptions().withShowForRBI(); + auto options = core::ShowOptions().withUseValidSyntax(); fmt::format_to(std::back_inserter(ss), "{}: {}", argSym.argumentName(ctx), chosenType.show(ctx, options)); } fmt::format_to(std::back_inserter(ss), ")."); @@ -445,7 +455,7 @@ optional SigSuggestion::maybeSuggestSig(core::Conte if (suggestsVoid) { fmt::format_to(std::back_inserter(ss), "void }}"); } else { - auto options = core::ShowOptions().withShowForRBI(); + auto options = core::ShowOptions().withUseValidSyntax(); fmt::format_to(std::back_inserter(ss), "returns({}) }}", guessedReturnType.show(ctx, options)); } diff --git a/infer/environment.cc b/infer/environment.cc index 1a34a6c6de..c479b3384e 100644 --- a/infer/environment.cc +++ b/infer/environment.cc @@ -136,7 +136,7 @@ KnowledgeRef KnowledgeRef::under(core::Context ctx, const Environment &env, cfg: copy.markDead(); return copy; } - bool enteringLoop = (bb->flags & cfg::CFG::LOOP_HEADER) != 0; + bool enteringLoop = bb->flags.isLoopHeader; for (auto &pair : env.vars()) { auto local = pair.first; auto &state = pair.second; @@ -597,7 +597,23 @@ void Environment::updateKnowledge(core::Context ctx, cfg::LocalRef local, core:: whoKnows.falsy().addNoTypeTest(local, typeTestsWithVar, send->recv.variable, ty); } whoKnows.sanityCheck(); + } else if (auto *klassTypeApp = core::cast_type(klassType)) { + if (klassTypeApp->klass == core::Symbols::Class()) { + auto currentAlignment = core::Types::alignBaseTypeArgs(ctx, klassTypeApp->klass, klassTypeApp->targs, + core::Symbols::Class()); + auto it = absl::c_find_if(currentAlignment, [&](auto tmRef) { + return tmRef.data(ctx)->name == core::Names::Constants::AttachedClass(); + }); + ENFORCE(it != currentAlignment.end()); + auto instanceTy = klassTypeApp->targs[distance(currentAlignment.begin(), it)]; + if (!instanceTy.isUntyped()) { + whoKnows.truthy().addYesTypeTest(local, typeTestsWithVar, send->recv.variable, instanceTy); + // Omitting falsy().addNoTypeTest because #4358 is even more prevalent with `T::Class` types + // https://github.com/sorbet/sorbet/issues/4358 + } + } } + return; } @@ -666,6 +682,21 @@ void Environment::updateKnowledge(core::Context ctx, cfg::LocalRef local, core:: whoKnows.truthy().addYesTypeTest(local, typeTestsWithVar, send->args[0].variable, representedType); whoKnows.falsy().addNoTypeTest(local, typeTestsWithVar, send->args[0].variable, representedType); } + } else if (auto *recvApp = core::cast_type(recvType)) { + if (recvApp->klass == core::Symbols::Class()) { + auto currentAlignment = + core::Types::alignBaseTypeArgs(ctx, recvApp->klass, recvApp->targs, core::Symbols::Class()); + auto it = absl::c_find_if(currentAlignment, [&](auto tmRef) { + return tmRef.data(ctx)->name == core::Names::Constants::AttachedClass(); + }); + ENFORCE(it != currentAlignment.end()); + auto instanceTy = recvApp->targs[distance(currentAlignment.begin(), it)]; + if (!instanceTy.isUntyped()) { + whoKnows.truthy().addYesTypeTest(local, typeTestsWithVar, send->args[0].variable, instanceTy); + // Omitting falsy().addNoTypeTest because #4358 is even more prevalent with `T::Class` types + // https://github.com/sorbet/sorbet/issues/4358 + } + } } // `when` against singleton @@ -688,12 +719,12 @@ void Environment::updateKnowledge(core::Context ctx, cfg::LocalRef local, core:: if (core::isa_type(argType)) { auto argClass = core::cast_type_nonnull(argType); - if (!recvKlass.derivesFrom(ctx, core::Symbols::Class()) || + if (!recvKlass.derivesFrom(ctx, core::Symbols::Module()) || !argClass.symbol.data(ctx)->derivesFrom(ctx, core::Symbols::Class())) { return; } } else if (auto *argClass = core::cast_type(argType)) { - if (!recvKlass.derivesFrom(ctx, core::Symbols::Class()) || + if (!recvKlass.derivesFrom(ctx, core::Symbols::Module()) || !argClass->klass.data(ctx)->derivesFrom(ctx, core::Symbols::Class())) { return; } @@ -755,8 +786,7 @@ void Environment::assumeKnowledge(core::Context ctx, bool isTrue, cfg::LocalRef } else { core::TypeAndOrigins tp = getTypeAndOrigin(ctx, cond); tp.origins.emplace_back(loc); - tp.type = core::Types::dropSubtypesOf(ctx, core::Types::dropSubtypesOf(ctx, tp.type, core::Symbols::NilClass()), - core::Symbols::FalseClass()); + tp.type = core::Types::dropSubtypesOf(ctx, tp.type, core::Types::falsySymbols()); if (tp.type.isBottom()) { isDead = true; return; @@ -829,7 +859,7 @@ void Environment::mergeWith(core::Context ctx, const Environment &other, cfg::CF pair.second.knownTruthy = other.getKnownTruthy(var); } - if (((bb->flags & cfg::CFG::LOOP_HEADER) != 0) && bb->outerLoops <= var.maxLoopWrite(inWhat)) { + if (bb->flags.isLoopHeader && bb->outerLoops <= var.maxLoopWrite(inWhat)) { continue; } bool canBeFalsy = core::Types::canBeFalsy(ctx, otherTO.type) && !other.getKnownTruthy(var); @@ -917,7 +947,7 @@ void Environment::populateFrom(core::Context ctx, const Environment &other) { } core::TypePtr flatmapHack(core::Context ctx, const core::TypePtr &receiver, const core::TypePtr &returnType, - core::NameRef fun, const core::Loc &loc) { + core::NameRef fun, const core::Loc &loc, const core::NameRef currentMethodName) { if (fun != core::Names::flatMap()) { return returnType; } @@ -945,7 +975,7 @@ core::TypePtr flatmapHack(core::Context ctx, const core::TypePtr &receiver, cons }; core::DispatchArgs dispatchArgs{core::Names::flatten(), locs, 1, args, recvType.type, recvType, - recvType.type, nullptr, loc, true, false}; + recvType.type, nullptr, loc, true, false, currentMethodName}; auto dispatched = recvType.type.dispatchCall(ctx, dispatchArgs); if (dispatched.main.errors.empty()) { @@ -996,11 +1026,14 @@ Environment::processBinding(core::Context ctx, const cfg::CFG &inWhat, cfg::Bind }; // This is the main place where we type check a method, so we default by assuming - // that we want to report all errors (supressing nothing). + // that we want to report all errors (suppressing nothing). auto suppressErrors = false; - core::DispatchArgs dispatchArgs{ - send.fun, locs, send.numPosArgs, args, recvType.type, recvType, recvType.type, - send.link, ownerLoc, send.isPrivateOk, suppressErrors}; + core::DispatchArgs dispatchArgs{send.fun, locs, + send.numPosArgs, args, + recvType.type, recvType, + recvType.type, send.link, + ownerLoc, send.isPrivateOk, + suppressErrors, inWhat.symbol.data(ctx)->name}; auto dispatched = recvType.type.dispatchCall(ctx, dispatchArgs); auto it = &dispatched; @@ -1246,7 +1279,7 @@ Environment::processBinding(core::Context ctx, const cfg::CFG &inWhat, cfg::Bind type = i.link->result->returnType; } auto loc = ctx.locAt(bind.loc); - type = flatmapHack(ctx, main.receiver, type, i.link->fun, loc); + type = flatmapHack(ctx, main.receiver, type, i.link->fun, loc, inWhat.symbol.data(ctx)->name); tp.type = std::move(type); tp.origins.emplace_back(loc); }, @@ -1392,7 +1425,8 @@ Environment::processBinding(core::Context ctx, const cfg::CFG &inWhat, cfg::Bind block, ctx.locAt(bind.loc), isPrivateOk, - suppressErrors}; + suppressErrors, + inWhat.symbol.data(ctx)->name}; auto dispatched = recvType.type.dispatchCall(ctx, dispatchArgs); tp.type = dispatched.returnType; } @@ -1409,11 +1443,13 @@ Environment::processBinding(core::Context ctx, const cfg::CFG &inWhat, cfg::Bind tp.origins.emplace_back(ctx.locAt(bind.loc)); const core::TypeAndOrigins &typeAndOrigin = getAndFillTypeAndOrigin(ctx, i.what); - if (core::Types::isSubType(ctx, core::Types::void_(), methodReturnType)) { + if (methodReturnType == core::Types::void_()) { methodReturnType = core::Types::top(); } + core::ErrorSection::Collector errorDetailsCollector; if (!core::Types::isSubTypeUnderConstraint(ctx, constr, typeAndOrigin.type, methodReturnType, - core::UntypedMode::AlwaysCompatible)) { + core::UntypedMode::AlwaysCompatible, + errorDetailsCollector)) { if (auto e = ctx.beginError(bind.loc, core::errors::Infer::ReturnTypeMismatch)) { auto owner = ctx.owner; e.setHeader("Expected `{}` but found `{}` for method result type", methodReturnType.show(ctx), @@ -1422,7 +1458,8 @@ Environment::processBinding(core::Context ctx, const cfg::CFG &inWhat, cfg::Bind e.addErrorSection( core::TypeAndOrigins::explainExpected(ctx, methodReturnType, owner.loc(ctx), for_)); e.addErrorSection(typeAndOrigin.explainGot(ctx, ownerLoc)); - core::TypeErrorDiagnostics::explainTypeMismatch(ctx, e, methodReturnType, typeAndOrigin.type); + core::TypeErrorDiagnostics::explainTypeMismatch(ctx, e, errorDetailsCollector, methodReturnType, + typeAndOrigin.type); if (i.whatLoc != inWhat.implicitReturnLoc) { auto replaceLoc = ctx.locAt(i.whatLoc); core::TypeErrorDiagnostics::maybeAutocorrect(ctx, e, replaceLoc, constr, methodReturnType, @@ -1432,7 +1469,8 @@ Environment::processBinding(core::Context ctx, const cfg::CFG &inWhat, cfg::Bind } else if (!methodReturnType.isUntyped() && !methodReturnType.isTop() && typeAndOrigin.type.isUntyped()) { auto what = core::errors::Infer::errorClassForUntyped(ctx, ctx.file, typeAndOrigin.type); - if (auto e = ctx.beginError(bind.loc, what)) { + auto errLoc = ctx.locAt(bind.loc).truncateToFirstLine(ctx); + if (auto e = ctx.state.beginError(errLoc, what)) { e.setHeader("Value returned from method is `{}`", "T.untyped"); core::TypeErrorDiagnostics::explainUntyped(ctx, e, what, typeAndOrigin, ownerLoc); } @@ -1448,12 +1486,13 @@ Environment::processBinding(core::Context ctx, const cfg::CFG &inWhat, cfg::Bind expectedType = core::Types::top(); } bool isSubtype; + core::ErrorSection::Collector errorDetailsCollector; if (i.link->result->main.constr) { - isSubtype = - core::Types::isSubTypeUnderConstraint(ctx, *i.link->result->main.constr, typeAndOrigin.type, - expectedType, core::UntypedMode::AlwaysCompatible); + isSubtype = core::Types::isSubTypeUnderConstraint( + ctx, *i.link->result->main.constr, typeAndOrigin.type, expectedType, + core::UntypedMode::AlwaysCompatible, errorDetailsCollector); } else { - isSubtype = core::Types::isSubType(ctx, typeAndOrigin.type, expectedType); + isSubtype = core::Types::isSubType(ctx, typeAndOrigin.type, expectedType, errorDetailsCollector); } if (!isSubtype) { if (auto e = ctx.beginError(bind.loc, core::errors::Infer::ReturnTypeMismatch)) { @@ -1466,7 +1505,8 @@ Environment::processBinding(core::Context ctx, const cfg::CFG &inWhat, cfg::Bind core::TypeAndOrigins::explainExpected(ctx, expectedType, bspec.loc, "block result type")); e.addErrorSection(typeAndOrigin.explainGot(ctx, ownerLoc)); - core::TypeErrorDiagnostics::explainTypeMismatch(ctx, e, expectedType, typeAndOrigin.type); + core::TypeErrorDiagnostics::explainTypeMismatch(ctx, e, errorDetailsCollector, expectedType, + typeAndOrigin.type); } } else if (!expectedType.isUntyped() && !expectedType.isTop() && typeAndOrigin.type.isUntyped()) { auto what = core::errors::Infer::errorClassForUntyped(ctx, ctx.file, typeAndOrigin.type); @@ -1509,7 +1549,8 @@ Environment::processBinding(core::Context ctx, const cfg::CFG &inWhat, cfg::Bind tp.origins.emplace_back(ctx.locAt(bind.loc)); }, [&](cfg::GetCurrentException &i) { - tp.type = core::Types::untyped(core::Symbols::Magic_UntypedSource_GetCurrentException()); + tp.type = core::Types::any(ctx, core::make_type(core::Symbols::Exception()), + core::Types::nilClass()); tp.origins.emplace_back(ctx.locAt(bind.loc)); }, [&](cfg::LoadSelf &l) { @@ -1559,18 +1600,21 @@ Environment::processBinding(core::Context ctx, const cfg::CFG &inWhat, cfg::Bind // TODO(jez) Should we allow `T.let` / `T.cast` opt out of the untyped code error? if (c.cast != core::Names::cast()) { + core::ErrorSection::Collector errorDetailsCollector; if (c.cast == core::Names::assertType() && ty.type.isUntyped()) { if (auto e = ctx.beginError(bind.loc, core::errors::Infer::CastTypeMismatch)) { e.setHeader("Expected a type but found `{}` for `{}`", "T.untyped", "T.assert_type!"); e.addErrorSection(ty.explainGot(ctx, ownerLoc)); e.addErrorNote("You may need to add additional `{}` annotations", "sig"); } - } else if (!core::Types::isSubType(ctx, ty.type, castType)) { + } else if (!core::Types::isSubType(ctx, ty.type, castType, errorDetailsCollector)) { if (c.cast == core::Names::assumeType()) { if (auto e = ctx.beginError(bind.loc, core::errors::Infer::IncorrectlyAssumedType)) { e.setHeader("Assumed expression had type `{}` but found `{}`", castType.show(ctx), ty.type.show(ctx)); e.addErrorSection(ty.explainGot(ctx, ownerLoc)); + core::TypeErrorDiagnostics::explainTypeMismatch(ctx, e, errorDetailsCollector, castType, + ty.type); e.addErrorNote("Please add an explicit type annotation to correct this assumption"); if (bind.loc.exists() && c.valueLoc.exists()) { e.replaceWith("Add explicit annotation", ctx.locAt(bind.loc), "T.let({}, {})", @@ -1581,36 +1625,43 @@ Environment::processBinding(core::Context ctx, const cfg::CFG &inWhat, cfg::Bind if (auto e = ctx.beginError(bind.loc, core::errors::Infer::CastTypeMismatch)) { e.setHeader("Argument does not have asserted type `{}`", castType.show(ctx)); e.addErrorSection(ty.explainGot(ctx, ownerLoc)); + e.addErrorSections(std::move(errorDetailsCollector)); core::TypeErrorDiagnostics::maybeAutocorrect(ctx, e, ctx.locAt(c.valueLoc), constr, castType, ty.type); } } } } else if (!bind.value.isSynthetic()) { - if (castType.isUntyped()) { - if (auto e = ctx.beginError(bind.loc, core::errors::Infer::InvalidCast)) { - e.setHeader("Please use `{}` to cast to `{}`", "T.unsafe", "T.untyped"); - auto argLoc = core::Loc{ctx.file, c.valueLoc}; - if (argLoc.exists()) { - e.replaceWith("Replace with `T.unsafe`", ctx.locAt(bind.loc), "T.unsafe({})", - argLoc.source(ctx).value()); - } - } - } else if (!ty.type.isUntyped() && core::Types::isSubType(ctx, ty.type, castType)) { - if (auto e = ctx.beginError(bind.loc, core::errors::Infer::InvalidCast)) { - e.setHeader("`{}` is useless because `{}` is already a subtype of `{}`", "T.cast", - ty.type.show(ctx), castType.show(ctx)); - e.addErrorSection(ty.explainGot(ctx, ownerLoc)); - auto argLoc = ctx.locAt(c.valueLoc); - if (argLoc.exists()) { - if (ctx.state.suggestUnsafe.has_value()) { - e.replaceWith("Convert to `T.unsafe`", ctx.locAt(bind.loc), "{}({})", - ctx.state.suggestUnsafe.value(), argLoc.source(ctx).value()); - } else { - e.replaceWith("Delete `T.cast`", ctx.locAt(bind.loc), "{}", + // The bind.bind.variable check is to detect a T.bind call on self. + // Since T.bind has already been desugared to a T.cast, we can't check that directly. + // However, self = ... is not valid ruby syntax, so if the target of this binding is self, + // we know if actually came from a T.bind that was desugared to a T.cast + if (bind.bind.variable != cfg::LocalRef::selfVariable()) { + if (castType.isUntyped()) { + if (auto e = ctx.beginError(bind.loc, core::errors::Infer::InvalidCast)) { + e.setHeader("Please use `{}` to cast to `{}`", "T.unsafe", "T.untyped"); + auto argLoc = core::Loc{ctx.file, c.valueLoc}; + if (argLoc.exists()) { + e.replaceWith("Replace with `T.unsafe`", ctx.locAt(bind.loc), "T.unsafe({})", argLoc.source(ctx).value()); } } + } else if (!ty.type.isUntyped() && core::Types::isSubType(ctx, ty.type, castType)) { + if (auto e = ctx.beginError(bind.loc, core::errors::Infer::InvalidCast)) { + e.setHeader("`{}` is useless because `{}` is already a subtype of `{}`", "T.cast", + ty.type.show(ctx), castType.show(ctx)); + e.addErrorSection(ty.explainGot(ctx, ownerLoc)); + auto argLoc = ctx.locAt(c.valueLoc); + if (argLoc.exists()) { + if (ctx.state.suggestUnsafe.has_value()) { + e.replaceWith("Convert to `T.unsafe`", ctx.locAt(bind.loc), "{}({})", + ctx.state.suggestUnsafe.value(), argLoc.source(ctx).value()); + } else { + e.replaceWith("Delete `T.cast`", ctx.locAt(bind.loc), "{}", + argLoc.source(ctx).value()); + } + } + } } } } @@ -1642,8 +1693,9 @@ Environment::processBinding(core::Context ctx, const cfg::CFG &inWhat, cfg::Bind (pin != pinnedTypes.end()) ? pin->second : getTypeAndOrigin(ctx, bind.bind.variable); // TODO(jez) What should we do about untyped code and pinning? + core::ErrorSection::Collector errorDetailsCollector; bool asGoodAs = core::Types::isSubType(ctx, core::Types::dropLiteral(ctx, tp.type), - core::Types::dropLiteral(ctx, cur.type)); + core::Types::dropLiteral(ctx, cur.type), errorDetailsCollector); { switch (bindMinLoops) { @@ -1657,7 +1709,8 @@ Environment::processBinding(core::Context ctx, const cfg::CFG &inWhat, cfg::Bind // message, but we don't have a convenient way to compute this at the moment. e.addErrorSection(cur.explainExpected(ctx, "field defined here", ownerLoc)); e.addErrorSection(tp.explainGot(ctx, ownerLoc)); - core::TypeErrorDiagnostics::explainTypeMismatch(ctx, e, cur.type, tp.type); + core::TypeErrorDiagnostics::explainTypeMismatch(ctx, e, errorDetailsCollector, cur.type, + tp.type); auto replaceLoc = ctx.locAt(bind.loc); // We are not processing a method call, so there is no constraint. auto &constr = core::TypeConstraint::EmptyFrozenConstraint; @@ -1673,6 +1726,8 @@ Environment::processBinding(core::Context ctx, const cfg::CFG &inWhat, cfg::Bind e.setHeader("Incompatible assignment to variable declared via `{}`: `{}` is not a " "subtype of `{}`", "let", tp.type.show(ctx), cur.type.show(ctx)); + core::TypeErrorDiagnostics::explainTypeMismatch(ctx, e, errorDetailsCollector, tp.type, + cur.type); } tp = cur; } @@ -1687,7 +1742,7 @@ Environment::processBinding(core::Context ctx, const cfg::CFG &inWhat, cfg::Bind if (ident->what.data(inWhat)._name == core::Names::selfRestore()) { // this is a restoration of `self` variable. - // our current analysis isn't smart enogh to see that it's safe to do this by + // our current analysis isn't smart enough to see that it's safe to do this by // construction either https://github.com/sorbet/sorbet/issues/222 or // https://github.com/sorbet/sorbet/issues/224 should allow us to remove this // case @@ -1709,13 +1764,26 @@ Environment::processBinding(core::Context ctx, const cfg::CFG &inWhat, cfg::Bind !inWhat.symbol.data(ctx)->loc().contains(cur.origins[0]))) { auto suggest = core::Types::any(ctx, dropConstructor(ctx, tp.origins[0], tp.type), cur.type); - auto replacement = suggest.show(ctx, core::ShowOptions().withShowForRBI()); + auto replacement = suggest.show(ctx, core::ShowOptions().withUseValidSyntax()); e.replaceWith(fmt::format("Initialize as `{}`", replacement), cur.origins[0], "T.let({}, {})", cur.origins[0].source(ctx).value(), replacement); } else { e.addErrorSection(core::ErrorSection("Original type from:", cur.origins2Explanations(ctx, ownerLoc))); } + core::TypeErrorDiagnostics::explainTypeMismatch(ctx, e, errorDetailsCollector, tp.type, + cur.type); + + if (!cur.origins.empty() && !tp.origins.empty() && + absl::c_any_of(cur.origins, + [&](auto loc) { return loc.source(ctx) == "rescue"; }) && + absl::c_any_of(tp.origins, [&](auto loc) { return loc.source(ctx) == "rescue"; })) { + e.addErrorNote( + "The exception variables of two `{}` blocks conflict with each other because\n" + " the second is inside a loop. Either use `{}` to initialize the exception\n" + " variable before the first `{}`, or pick unique variable names.\n", + "rescue", "T.let", "rescue"); + } } tp.type = core::Types::untypedUntracked(); @@ -1754,7 +1822,7 @@ void Environment::cloneFrom(const Environment &rhs) { core::TypeAndOrigins Environment::getTypeFromRebind(core::Context ctx, const core::DispatchComponent &main, cfg::LocalRef fallback) { - auto rebind = main.blockSpec.rebind; + auto rebind = main.rebind; if (rebind.exists()) { core::TypeAndOrigins result; @@ -1772,7 +1840,7 @@ core::TypeAndOrigins Environment::getTypeFromRebind(core::Context ctx, const cor result.type = rebind.data(ctx)->selfType(ctx); } - result.origins.emplace_back(main.blockSpec.loc); + result.origins.emplace_back(main.rebindLoc); return result; } else { return getTypeAndOrigin(ctx, fallback); @@ -1809,11 +1877,7 @@ void Environment::setUninitializedVarsToNil(const core::Context &ctx, core::Loc namespace { core::TypeAndOrigins nilTypesWithOriginWithLoc(core::Loc loc) { - // I'd love to have this, but keepForIDE intentionally has Loc::none() and - // sometimes ends up here... - // ENFORCE(loc.exists()); - core::TypeAndOrigins ret{core::Types::nilClass(), loc}; - return ret; + return {core::Types::nilClass(), loc}; } } // namespace diff --git a/infer/inference.cc b/infer/inference.cc index a762e6338b..02e546494e 100644 --- a/infer/inference.cc +++ b/infer/inference.cc @@ -12,6 +12,42 @@ using namespace std; namespace sorbet::infer { +bool Inference::willRun(core::Context ctx, core::LocOffsets loc, core::MethodRef method) { + // With scip-ruby, we might as well try making progress with untyped code. + auto minStrictLevel = ctx.state.isSCIPRuby ? core::StrictLevel::False : core::StrictLevel::True; + if (ctx.file.data(ctx).strictLevel < minStrictLevel) { + return false; + } + + const auto &methodData = method.data(ctx); + auto name = methodData->name; + auto isMangleRenameOverload = name.kind() == core::NameKind::UNIQUE && + name.dataUnique(ctx)->uniqueNameKind == core::UniqueNameKind::MangleRenameOverload; + if (isMangleRenameOverload || methodData->flags.isOverloaded) { + if (auto e = ctx.beginError(loc, core::errors::Infer::TypecheckOverloadBody)) { + e.setHeader("Refusing to typecheck `{}` against an overloaded signature", method.show(ctx)); + auto overloadMethod = method; + if (isMangleRenameOverload) { + overloadMethod = + methodData->owner.data(ctx)->findMember(ctx, name.dataUnique(ctx)->original).asMethodRef(); + } + e.addErrorLine(overloadMethod.data(ctx)->loc(), "Given an overloaded signature here"); + e.addErrorNote("Overloads are only supported in RBI files.\n" + " To silence this error, mark this file `{}`\n" + " Read the error doc for how to avoid using overloaded methods in the first place.", + "# typed: false"); + } + + return false; + } + + if (methodData->flags.isAbstract && ctx.file.data(ctx).compiledLevel != core::CompiledLevel::True) { + return false; + } + + return true; +} + unique_ptr Inference::run(core::Context ctx, unique_ptr cfg) { Timer timeit(ctx.state.tracer(), "Inference::run", {{"func", string(cfg->symbol.toStringFullName(ctx))}}); ENFORCE(cfg->symbol == ctx.owner.asMethodRef()); @@ -230,12 +266,10 @@ unique_ptr Inference::run(core::Context ctx, unique_ptr cfg) bool andAndOrOr = false; if (ident != nullptr) { auto name = ident->what.data(*cfg)._name; - if (name.kind() == core::NameKind::UNIQUE && - name.dataUnique(ctx)->original == core::Names::andAnd()) { + if (name.isUniqueNameOf(ctx, core::Names::andAnd())) { e.setHeader("Left side of `{}` condition was always `{}`", "&&", "truthy"); andAndOrOr = true; - } else if (name.kind() == core::NameKind::UNIQUE && - name.dataUnique(ctx)->original == core::Names::orOr()) { + } else if (name.isUniqueNameOf(ctx, core::Names::orOr())) { e.setHeader("Left side of `{}` condition was always `{}`", "||", "falsy"); andAndOrOr = true; } @@ -247,7 +281,7 @@ unique_ptr Inference::run(core::Context ctx, unique_ptr cfg) for (const auto &prevBasicBlock : bb->backEdges) { const auto &prevEnv = outEnvironments[prevBasicBlock->id]; if (prevEnv.isDead) { - // This prevous block doesn't actually matter, because it was dead + // This previous block doesn't actually matter, because it was dead // (never got to evaluating its jump condition), so don't clutter // the error message. continue; @@ -264,6 +298,23 @@ unique_ptr Inference::run(core::Context ctx, unique_ptr cfg) auto alwaysWhat = prevBasicBlock->bexit.thenb->id == bb->id ? "falsy" : "truthy"; auto bexitLoc = ctx.locAt(prevBasicBlock->bexit.loc); + + auto bexitVar = cond.variable.data(*cfg)._name; + if ((bexitVar.isUniqueNameOf(ctx, core::Names::andAnd()) || + bexitVar.isUniqueNameOf(ctx, core::Names::orOr())) && + !prevBasicBlock->exprs.empty() && + prevBasicBlock->exprs.back().bind.variable == cond.variable) { + // ^ This condition is a hack that hardcodes the most common structure of the CFG + // we'd need to handle. If we had SSA form in Sorbet's CFG, we wouldn't have to pray + // that the bexit var's initializer is the .back() of the expression in the block + // (despite how rare it is for that to _not_ be the case). + + // We want to show one location for the "Conditional branch on untyped" warning/error, + // but setting that location clobbers the location we need for this autocorrect to work. + // So we have to claw back what the LHS of the || or && would have been. + bexitLoc = ctx.locAt(prevBasicBlock->exprs.back().loc); + } + e.addErrorLine(bexitLoc, "This condition was always `{}` (`{}`)", alwaysWhat, cond.type.show(ctx)); @@ -328,6 +379,15 @@ unique_ptr Inference::run(core::Context ctx, unique_ptr cfg) e.setHeader("Conditional branch on `{}`", "T.untyped"); core::TypeErrorDiagnostics::explainUntyped(ctx, e, what, bexitTpo, methodLoc); } + } else if (bb->bexit.cond.variable != cfg::LocalRef::unconditional() && bexitTpo.type.hasTopLevelVoid()) { + if (auto e = ctx.beginError(bb->bexit.loc, core::errors::Infer::BranchOnVoid)) { + e.setHeader("Branching on `{}` value", "void"); + e.addErrorSection(bexitTpo.explainGot(ctx, methodLoc)); + e.addErrorNote("Methods which return `{}` and which are checked at runtime have their\n" + " return value replaced with a special void singleton value when called.\n" + " It does not make sense to branch on this value.", + "void"); + } } } else { ENFORCE(bb->firstDeadInstructionIdx != -1); diff --git a/infer/inference.h b/infer/inference.h index 5d4bd36b08..99e2c8827c 100644 --- a/infer/inference.h +++ b/infer/inference.h @@ -8,6 +8,7 @@ namespace sorbet::infer { class Inference final { public: + static bool willRun(core::Context ctx, core::LocOffsets loc, core::MethodRef method); static std::unique_ptr run(core::Context ctx, std::unique_ptr cfg); }; } // namespace sorbet::infer diff --git a/infer/test/infer_test.cc b/infer/test/infer_test.cc index 69ca58ef20..1f57d8de6f 100644 --- a/infer/test/infer_test.cc +++ b/infer/test/infer_test.cc @@ -41,7 +41,8 @@ void processSource(core::GlobalState &cb, string str) { trees.emplace_back(move(tree)); auto workers = WorkerPool::create(0, *logger); core::FoundDefHashes foundHashes; // compute this just for test coverage - trees = move(namer::Namer::run(cb, move(trees), *workers, &foundHashes).result()); + auto cancelled = namer::Namer::run(cb, absl::Span(trees), *workers, &foundHashes); + ENFORCE(!cancelled); auto resolved = resolver::Resolver::run(cb, move(trees), *workers); for (auto &tree : resolved.result()) { sorbet::core::MutableContext ctx(cb, core::Symbols::root(), tree.file); diff --git a/lldbinit.py b/lldbinit.py index 7f759e3605..960827e6ae 100644 --- a/lldbinit.py +++ b/lldbinit.py @@ -63,7 +63,7 @@ def cmd_rubysourcemap(debugger, command, result, dict): dir_search = re.search('^File: (.*)/main.c', output) if dir_search: dirname = dir_search.group(1) - ruby_path = ('%s/bazel-sorbet/bazel/sorbet_ruby_2_7_for_compiler' % project_root) + ruby_path = ('%s/bazel-sorbet/bazel/sorbet_ruby_3_1' % project_root) source_map_command = 'settings set -- target.source-map %s %s' % (dirname, ruby_path) print('(lldb) %s' % source_map_command) ci.HandleCommand(source_map_command, result) diff --git a/local_vars/local_vars.cc b/local_vars/local_vars.cc index 9cb67d29ef..b4576934b8 100644 --- a/local_vars/local_vars.cc +++ b/local_vars/local_vars.cc @@ -210,6 +210,7 @@ class LocalNameInserter { auto &original = ast::cast_tree_nonnull(tree); ENFORCE(original.numPosArgs() == 1 && ast::isa_tree(original.getPosArg(0))); + ENFORCE(original.fun == core::Names::super() || original.fun == core::Names::untypedSuper()); ast::ExpressionPtr originalBlock; if (auto *rawBlock = original.rawBlock()) { @@ -218,6 +219,7 @@ class LocalNameInserter { // Clear out the args (which are just [ZSuperArgs]) in the original send. (Note that we want this cleared even // if we error out below, because later `ENFORCE`s will be triggered if we don't.) + auto zSuperArgsLoc = original.getPosArg(0).loc(); original.clearArgs(); if (!scopeStack.back().insideMethod) { @@ -283,17 +285,17 @@ class LocalNameInserter { ENFORCE(kwArgKeyEntries.empty(), "Saw positional arg after keyword arg"); ENFORCE(kwArgsHash == nullptr, "Saw positional arg after keyword splat"); - posArgsEntries.emplace_back(ast::make_expression(original.loc, arg.arg)); + posArgsEntries.emplace_back(ast::make_expression(zSuperArgsLoc, arg.arg)); } else if (arg.flags.isPositionalSplat()) { ENFORCE(posArgsArray == nullptr, "Saw multiple positional splats"); ENFORCE(kwArgKeyEntries.empty(), "Saw positional splat after keyword arg"); ENFORCE(kwArgsHash == nullptr, "Saw positional splat after keyword splat"); - posArgsArray = ast::MK::Splat(original.loc, ast::make_expression(original.loc, arg.arg)); + posArgsArray = ast::MK::Splat(zSuperArgsLoc, ast::make_expression(zSuperArgsLoc, arg.arg)); if (!posArgsEntries.empty()) { - posArgsArray = ast::MK::Send1(original.loc, ast::MK::Array(original.loc, std::move(posArgsEntries)), - core::Names::concat(), original.loc.copyWithZeroLength(), - std::move(posArgsArray)); + posArgsArray = ast::MK::Send1( + zSuperArgsLoc, ast::MK::Array(zSuperArgsLoc, std::move(posArgsEntries)), core::Names::concat(), + zSuperArgsLoc.copyWithZeroLength(), std::move(posArgsArray)); posArgsEntries.clear(); } } else if (arg.flags.isKeyword()) { @@ -301,27 +303,28 @@ class LocalNameInserter { auto name = arg.arg._name; kwArgKeyEntries.emplace_back(ast::MK::Literal( - original.loc, core::make_type(core::Symbols::Symbol(), name))); - kwArgValueEntries.emplace_back(ast::make_expression(original.loc, arg.arg)); + zSuperArgsLoc, core::make_type(core::Symbols::Symbol(), name))); + kwArgValueEntries.emplace_back(ast::make_expression(zSuperArgsLoc, arg.arg)); } else if (arg.flags.isKeywordSplat()) { ENFORCE(kwArgsHash == nullptr, "Saw multiple keyword splats"); // TODO(aprocter): is it necessary to duplicate the hash here? - kwArgsHash = ast::MK::Send1(original.loc, ast::MK::Magic(original.loc), core::Names::toHashDup(), - original.loc.copyWithZeroLength(), - ast::make_expression(original.loc, arg.arg)); + kwArgsHash = ast::MK::Send1(zSuperArgsLoc, ast::MK::Magic(zSuperArgsLoc), core::Names::toHashDup(), + zSuperArgsLoc.copyWithZeroLength(), + ast::make_expression(zSuperArgsLoc, arg.arg)); if (!kwArgKeyEntries.empty()) { // TODO(aprocter): it might make more sense to replace this with an InsSeq that calls // ::, which is what's done in the desugarer. kwArgsHash = ast::MK::Send1( - original.loc, - ast::MK::Hash(original.loc, std::move(kwArgKeyEntries), std::move(kwArgValueEntries)), - core::Names::merge(), original.loc.copyWithZeroLength(), std::move(kwArgsHash)); + zSuperArgsLoc, + ast::MK::Hash(zSuperArgsLoc, std::move(kwArgKeyEntries), std::move(kwArgValueEntries)), + core::Names::merge(), zSuperArgsLoc.copyWithZeroLength(), std::move(kwArgsHash)); kwArgKeyEntries.clear(); kwArgValueEntries.clear(); } } else if (arg.flags.block) { - blockArg = ast::make_expression(original.loc, arg.arg); + auto blockLoc = arg.arg._name == core::Names::blkArg() ? core::LocOffsets::none() : zSuperArgsLoc; + blockArg = ast::make_expression(blockLoc, arg.arg); } else if (arg.flags.shadow) { ENFORCE(false, "Shadow only come from blocks, but super only looks at a method's args"); } else { @@ -341,13 +344,17 @@ class LocalNameInserter { posArgsEntries.clear(); } - auto method = ast::MK::Literal( - original.loc, core::make_type(core::Symbols::Symbol(), core::Names::super())); + auto method = ast::MK::Literal(original.loc, + core::make_type(core::Symbols::Symbol(), original.fun)); + + auto shouldForwardBlockArg = + blockArg.loc().exists() || ctx.file.data(ctx).strictLevel < core::StrictLevel::Strict; if (posArgsArray != nullptr) { // We wrap self with T.unsafe in order to get around the requirement for and // that the shapes of the splatted hashes be known statically. This is a bit of // a hack, but 'super' is currently treated as untyped anyway. + // TODO(neil): this probably blames to unsafe, we should find a way to blame to super maybe? original.addPosArg(ast::MK::Unsafe(original.loc, std::move(original.recv))); original.addPosArg(std::move(method)); @@ -387,12 +394,15 @@ class LocalNameInserter { original.fun = core::Names::callWithSplat(); // Re-add block argument original.setBlock(std::move(originalBlock)); - } else { + } else if (shouldForwardBlockArg) { // (..., &blk) original.fun = core::Names::callWithSplatAndBlock(); original.addPosArg(std::move(blockArg)); + } else { + // (...) + original.fun = core::Names::callWithSplat(); } - } else if (originalBlock == nullptr) { + } else if (originalBlock == nullptr && shouldForwardBlockArg) { // No positional splat and no "do", so we need to forward & with . original.reserveArguments(3 + posArgsEntries.size(), kwArgKeyEntries.size(), /* hasKwSplat */ kwArgsHash != nullptr, /* hasBlock */ false); @@ -437,7 +447,9 @@ class LocalNameInserter { } } // Re-add original block - original.setBlock(std::move(originalBlock)); + if (originalBlock) { + original.setBlock(std::move(originalBlock)); + } kwArgKeyEntries.clear(); kwArgValueEntries.clear(); } diff --git a/main/autogen/autogen.cc b/main/autogen/autogen.cc index a480524c11..0382da45c4 100644 --- a/main/autogen/autogen.cc +++ b/main/autogen/autogen.cc @@ -3,6 +3,7 @@ #include "ast/Helpers.h" #include "ast/ast.h" #include "ast/treemap/treemap.h" +#include "common/common.h" #include "common/strings/formatting.h" #include "main/autogen/crc_builder.h" @@ -15,7 +16,20 @@ class AutogenWalk { vector defs; vector refs; vector requireStatements; - vector nesting; + vector> nestings; + + struct NestingStackEntry { + DefinitionRef ref; + optional nestingEntry; + + NestingStackEntry() = default; + NestingStackEntry(DefinitionRef ref) : ref(ref) {} + + NestingStackEntry(NestingStackEntry &&) = default; + NestingStackEntry &operator=(NestingStackEntry &&) = default; + }; + + vector nestingStack; const AutogenConfig *autogenCfg; enum class ScopeType { Class, Block }; @@ -24,6 +38,8 @@ class AutogenWalk { UnorderedMap refMap; + UnorderedSet> seenRefsByLoc; + // Convert a symbol name into a fully qualified name vector symbolName(core::Context ctx, core::SymbolRef sym) { vector out; @@ -43,7 +59,25 @@ class AutogenWalk { auto &original = ast::cast_tree_nonnull(cnst->original); out.emplace_back(original.cnst); cnst = ast::cast_tree(original.scope); + + // If any part of the constant literal scope is a class alias, the final name should be scoped + // under the *dealiased* scope. This allows subconstants-of-aliases to be referenced + // correctly -- otherwise, we'd have missing edges in our static analysis dependency graphs, which + // drive pre-loading for our Ruby services at Stripe, and also have to jump hoops with complicated + // heuristics in our package generation tooling to determine whether something was resolved via an alias. + if (cnst != nullptr && cnst->original != nullptr) { + auto scopeSym = cnst->symbol; + if (scopeSym.isStaticField(ctx) && scopeSym.asFieldRef().data(ctx)->isClassAlias()) { + auto resolvedScopeName = symbolName(ctx, scopeSym); + + // append the name in reverse order to the hitherto-built name vector, + // then break out of the loop. + out.insert(out.end(), std::move(resolvedScopeName).rbegin(), std::move(resolvedScopeName).rend()); + break; + } + } } + reverse(out.begin(), out.end()); return out; } @@ -55,7 +89,7 @@ class AutogenWalk { def.type = Definition::Type::Module; def.defines_behavior = false; def.is_empty = false; - nesting.emplace_back(def.id); + nestingStack.emplace_back(def.id); autogenCfg = &autogenConfig; } @@ -102,6 +136,8 @@ class AutogenWalk { ENFORCE(it != refMap.end()); // ...so we can use that reference as the 'defining reference' def.defining_ref = it->second; + // ...we also grab the symbol reference of the defining reference + def.sym = refs[it->second.id()].sym; // update that reference with the relevant metadata so we know 1. it's the defining ref and 2. it encompasses // the entire class, not just the constant name refs[it->second.id()].is_defining_ref = true; @@ -118,7 +154,7 @@ class AutogenWalk { // The rest of the ancestors are all references inside the class body (i.e. uses of `include` or `extend`) so // add the current class to the scoping - nesting.emplace_back(def.id); + nestingStack.emplace_back(def.id); // ...and then run the treemap over all the includes and extends for (; ait != original.ancestors.end(); ++ait) { @@ -162,7 +198,7 @@ class AutogenWalk { } // remove the stuff added to handle the class scope here - nesting.pop_back(); + nestingStack.pop_back(); scopeTypes.pop_back(); } @@ -175,7 +211,7 @@ class AutogenWalk { } // `true` if the constant is fully qualified and can be traced back to the root scope, `false` otherwise - bool isCBaseConstant(ast::ConstantLit &cnstRef) { + bool isCBaseConstant(const ast::ConstantLit &cnstRef) { auto *cnst = &cnstRef; while (cnst != nullptr && cnst->original != nullptr) { auto &original = ast::cast_tree_nonnull(cnst->original); @@ -187,12 +223,50 @@ class AutogenWalk { return false; } + void setNestingAndScope(Reference &ref, const ast::ConstantLit &cnstRef) { + // if it's a constant we can resolve from the root... + if (isCBaseConstant(cnstRef)) { + // then its scope is easy + auto &entry = nestingStack.front(); + if (!entry.nestingEntry.has_value()) { + vector refs; + auto nestingId = nestings.size(); + nestings.emplace_back(move(refs)); + entry.nestingEntry.emplace(nestingId); + } + ref.nestingId = *entry.nestingEntry; + ref.scope = entry.ref; + } else { + // otherwise we need to figure out how it's nested in the current scope and mark that + auto &entry = nestingStack.back(); + + if (!entry.nestingEntry.has_value()) { + vector refs; + refs.reserve(nestingStack.size()); + // This is effectively trying to do: + // + // transform(nestingStack.begin(), nestingStack.end() ...); + // reverse(refs.begin(), refs.end()); + // refs.pop_back(); + // + // in a single call. + transform(nestingStack.rbegin(), nestingStack.rend() - 1, back_inserter(refs), + [](auto &entry) { return entry.ref; }); + auto nestingId = nestings.size(); + nestings.emplace_back(move(refs)); + entry.nestingEntry.emplace(nestingId); + } + ref.nestingId = *entry.nestingEntry; + ref.scope = entry.ref; + } + } + void postTransformConstantLit(core::Context ctx, ast::ExpressionPtr &tree) { auto &original = ast::cast_tree_nonnull(tree); if (!ignoring.empty()) { - // this is either a constant in a `keepForIde` node (in which case we don't care) or it was an `include` or - // an `extend` which already got handled in `preTransformClassDef` (in which case don't handle it again) + // this is an `include` or an `extend` which already got handled in `preTransformClassDef` + // (in which case don't handle it again) return; } if (original.original == nullptr) { @@ -204,21 +278,16 @@ class AutogenWalk { return; } + auto entry = make_pair(tree.loc(), original.symbol); + if (seenRefsByLoc.contains(entry)) { + return; + } + seenRefsByLoc.emplace(move(entry)); + // Create a new `Reference` auto &ref = refs.emplace_back(); ref.id = refs.size() - 1; - - // if it's a constant we can resolve from the root... - if (isCBaseConstant(original)) { - // then its scope is easy - ref.scope = nesting.front(); - } else { - // otherwise we need to figure out how it's nested in the current scope and mark that - ref.nesting = nesting; - reverse(ref.nesting.begin(), ref.nesting.end()); - ref.nesting.pop_back(); - ref.scope = nesting.back(); - } + setNestingAndScope(ref, original); ref.loc = original.loc; // the reference location is the location of constant, but this might get updated if the reference corresponds @@ -227,15 +296,15 @@ class AutogenWalk { ref.definitionLoc = original.loc; ref.name = QualifiedName::fromFullName(constantName(ctx, original)); auto sym = original.symbol; + ref.sym = sym; if (!sym.isClassOrModule() || sym != core::Symbols::StubModule()) { ref.resolved = QualifiedName::fromFullName(symbolName(ctx, sym)); } - ref.is_resolved_statically = true; ref.is_defining_ref = false; // if we're already in the scope of the class (which will be the newest-created one) then we're looking at the // `ancestors` or `singletonAncestors` values. Otherwise, (at least for the parent relationships we care about) // we're looking at the first `class Child < Parent` relationship, so we change `is_subclassing` to true. - if (!defs.empty() && !nesting.empty() && defs.back().id._id != nesting.back()._id) { + if (!defs.empty() && !nestingStack.empty() && defs.back().id._id != nestingStack.back().ref._id) { ref.parentKind = ClassKind::Class; } // now, add it to the refmap @@ -258,9 +327,22 @@ class AutogenWalk { } if (ctx.file.data(ctx).isRBI()) { - // We are only concerned with references in RBI files so that dependencies can be + std::string_view filePath = ctx.file.data(ctx).path(); + // Mostly, we are only concerned with references in RBI files so that dependencies can be // accurately tracked. Definitions of casgns are not needed. - return; + // + // The exception to this case is RBIs generated by special plugins like the coinbase protoc plugin, + // which can define net-new casgns for proto messages that aren't present in the corresponding + // generated Ruby code itself. + // + // Hence, we capture RBI files specified in the behaviorAllowedInRBIsPaths list (also used in the classDef + // logic above) as allowed sources of casgn definitions. + bool ignoreRBI = !absl::c_any_of(autogenCfg->behaviorAllowedInRBIsPaths, [&](auto &allowedPath) { + return absl::StartsWith(filePath, allowedPath); + }); + if (ignoreRBI) { + return; + } } // Create the Definition for it @@ -290,6 +372,8 @@ class AutogenWalk { auto &ref = refs[refMap[original.lhs.get()].id()]; // ...and mark that this is the defining ref for that one def.defining_ref = ref.id; + // ...we also store the new symbol reference + def.sym = ref.sym; ref.is_defining_ref = true; ref.definitionLoc = original.loc; @@ -302,12 +386,11 @@ class AutogenWalk { auto *original = ast::cast_tree(tree); bool inBlock = !scopeTypes.empty() && scopeTypes.back() == ScopeType::Block; - // Ignore keepForIde nodes. Also ignore include/extend sends iff they are directly at the - // class/module level. These cases are handled in `preTransformClassDef`. Do not ignore in - // block scope so that we a ref to the included module is still rendered. - if (original->fun == core::Names::keepForIde() || - (!inBlock && original->recv.isSelfReference() && - (original->fun == core::Names::include() || original->fun == core::Names::extend()))) { + // Ignore include/extend sends iff they are directly at the class/module level. + // These cases are handled in `preTransformClassDef`. + // Do not ignore in block scope so that we a ref to the included module is still rendered. + if (!inBlock && original->recv.isSelfReference() && + (original->fun == core::Names::include() || original->fun == core::Names::extend())) { ignoring.emplace_back(original); } // This means it's a `require`; mark it as such @@ -321,7 +404,7 @@ class AutogenWalk { void postTransformSend(core::Context ctx, ast::ExpressionPtr &tree) { auto *original = ast::cast_tree(tree); - // if this send was something we were ignoring (i.e. a `keepForIde` or an `include` or `require`) then pop this + // if this send was something we were ignoring (i.e. an `include` or `require`) then pop this if (!ignoring.empty() && ignoring.back() == original) { ignoring.pop_back(); } @@ -331,6 +414,7 @@ class AutogenWalk { ENFORCE(scopeTypes.empty()); ParsedFile out; + out.nestings = move(nestings); out.refs = move(refs); out.defs = move(defs); out.requireStatements = move(requireStatements); diff --git a/main/autogen/constant_hash.cc b/main/autogen/constant_hash.cc index a2b829bc62..fbc347e339 100644 --- a/main/autogen/constant_hash.cc +++ b/main/autogen/constant_hash.cc @@ -74,16 +74,6 @@ struct ConstantHashWalk { hashConstant(ctx, arg); } hashSoFar = core::mix(hashSoFar, core::_hash(")")); - } else if (send.fun == core::Names::autoloaderCompatibility()) { - hashSoFar = core::mix(hashSoFar, core::_hash("(a")); - if (send.hasPosArgs()) { - if (auto str = ast::cast_tree(send.posArgs().front())) { - if (str->isString()) { - hashSoFar = core::mix(hashSoFar, core::_hash(str->asString().shortName(ctx))); - } - } - } - hashSoFar = core::mix(hashSoFar, core::_hash(")")); } } diff --git a/main/autogen/data/BUILD b/main/autogen/data/BUILD index 21dea32db7..98bfb833c9 100644 --- a/main/autogen/data/BUILD +++ b/main/autogen/data/BUILD @@ -3,6 +3,7 @@ cc_library( srcs = [ "definitions.cc", "msgpack.cc", + "msgpack_lite.cc", ], hdrs = [ "definitions.h", diff --git a/main/autogen/data/definitions.cc b/main/autogen/data/definitions.cc index d792348189..dccc1a07ae 100644 --- a/main/autogen/data/definitions.cc +++ b/main/autogen/data/definitions.cc @@ -122,7 +122,8 @@ string ParsedFile::toString(const core::GlobalState &gs, int version) const { fmt::format_to(std::back_inserter(out), "## refs:\n"); for (auto &ref : refs) { vector nestingStrings; - for (auto &scope : ref.nesting) { + auto &nesting = nestings[ref.nestingId]; + for (auto &scope : nesting) { auto fullScopeName = showFullName(gs, scope); nestingStrings.emplace_back(fmt::format("[{}]", fmt::map_join(fullScopeName, " ", nameToString))); } @@ -170,8 +171,23 @@ vector ParsedFile::listAllClasses(core::Context ctx) { // Convert this parsedfile to a msgpack representation string ParsedFile::toMsgpack(core::Context ctx, int version, const AutogenConfig &autogenCfg) { - MsgpackWriter write(version); - return write.pack(ctx, *this, autogenCfg); + if (autogenCfg.msgpackSkipReferenceMetadata) { + MsgpackWriterLite write(version); + + return write.pack(ctx, *this, autogenCfg); + } else { + MsgpackWriter write(version); + + return write.pack(ctx, *this, autogenCfg); + } +} + +string ParsedFile::msgpackGlobalHeader(int version, size_t numFiles, const AutogenConfig &autogenCfg) { + if (autogenCfg.msgpackSkipReferenceMetadata) { + return MsgpackWriterLite::msgpackGlobalHeader(version, numFiles); + } else { + return MsgpackWriter::msgpackGlobalHeader(version, numFiles); + } } } // namespace sorbet::autogen diff --git a/main/autogen/data/definitions.h b/main/autogen/data/definitions.h index 3a2dac2255..201a308b78 100644 --- a/main/autogen/data/definitions.h +++ b/main/autogen/data/definitions.h @@ -84,13 +84,16 @@ struct ReferenceRef { // A constant definition---a class, module, constant definition, or constant alias---along with relevant metadata struct Definition { - enum class Type : uint64_t { Module, Class, Casgn, Alias, TypeAlias }; + enum class Type : uint8_t { Module, Class, Casgn, Alias, TypeAlias }; // the reference to this definition. Once `AutogenWalk` is completed and a full `ParsedFile` has been created, it // should always be the case that // definition.id.data(pf) == definition DefinitionRef id; + // The symbol reference for this reference + core::SymbolRef sym; + // is this a class, module, constant, or alias Type type; // does this define behavior? (i.e. is it a casgn or class, not simply a namespace?) @@ -110,20 +113,23 @@ struct Definition { }; // A `Reference` corresponds to a simple use of a constant name in a file. After a `ParsedFile` has been created, every -// constant use should have a `Reference` corresponding to it _unless_ it appears in a `keep_for_ide` call. +// constant use should have a `Reference` corresponding to it struct Reference { // the reference to this reference. Once `AutogenWalk` is completed and a full `ParsedFile` has been created, it // should always be the case that // reference.id.data(pf) == reference ReferenceRef id; + // The symbol reference for this reference + core::SymbolRef sym; + // In which class or module was this reference used? DefinitionRef scope; // its full qualified name QualifiedName name; - // the full nesting of this constant. If it's a constant resolved from the root, this will be an empty vector - std::vector nesting; + // the nesting ID of this constant + uint32_t nestingId; // the resolved name iff we have it from Sorbet QualifiedName resolved; @@ -131,9 +137,6 @@ struct Reference { core::LocOffsets loc; core::LocOffsets definitionLoc; - // this is always true - // TODO(gdritter): delete this, of course - bool is_resolved_statically; // `true` if this is the appearance of the constant name associated with a definition: i.e. the name of a class or // module or the LHS of a casgn bool is_defining_ref; @@ -148,6 +151,7 @@ struct Reference { struct AutogenConfig { const std::vector behaviorAllowedInRBIsPaths; + const bool msgpackSkipReferenceMetadata = false; }; // A `ParsedFile` contains all the `Definition`s and `References` used in a particular file @@ -160,6 +164,8 @@ struct ParsedFile { uint32_t cksum; // the path on disk to this file std::string path; + // nesting for every ref in this file + std::vector> nestings; // every statically-known constant defined by this file std::vector defs; // every static constant usage in this file @@ -169,6 +175,8 @@ struct ParsedFile { std::string toString(const core::GlobalState &gs, int version) const; std::string toMsgpack(core::Context ctx, int version, const AutogenConfig &autogenCfg); + static std::string msgpackGlobalHeader(int version, size_t numFiles, const AutogenConfig &autogenCfg); + std::vector showFullName(const core::GlobalState &gs, DefinitionRef id) const; QualifiedName showQualifiedName(const core::GlobalState &gs, DefinitionRef id) const; std::vector listAllClasses(core::Context ctx); diff --git a/main/autogen/data/msgpack.cc b/main/autogen/data/msgpack.cc index f3faa21819..37c1a7c6e6 100644 --- a/main/autogen/data/msgpack.cc +++ b/main/autogen/data/msgpack.cc @@ -9,7 +9,7 @@ using namespace std; namespace sorbet::autogen { -void MsgpackWriter::packName(core::NameRef nm) { +void MsgpackWriter::packName(mpack_writer_t *writer, core::NameRef nm) { uint32_t id; typename decltype(symbolIds)::value_type v{nm, 0}; auto [it, inserted] = symbolIds.insert(v); @@ -20,66 +20,76 @@ void MsgpackWriter::packName(core::NameRef nm) { } else { id = it->second; } - mpack_write_u32(&writer, id); + mpack_write_u32(writer, id); } -void MsgpackWriter::packNames(vector &names) { - mpack_start_array(&writer, names.size()); +void MsgpackWriter::packNames(mpack_writer_t *writer, vector &names) { + mpack_start_array(writer, names.size()); for (auto nm : names) { - packName(nm); + packName(writer, nm); } - mpack_finish_array(&writer); + mpack_finish_array(writer); } -void MsgpackWriter::packString(string_view str) { - mpack_write_str(&writer, str.data(), str.size()); +void packString(mpack_writer_t *writer, string_view str) { + mpack_write_str(writer, str.data(), str.size()); } -void MsgpackWriter::packBool(bool b) { +void MsgpackWriter::packBool(mpack_writer_t *writer, bool b) { if (b) { - mpack_write_true(&writer); + mpack_write_true(writer); } else { - mpack_write_false(&writer); + mpack_write_false(writer); } } -void MsgpackWriter::packReferenceRef(ReferenceRef ref) { +void MsgpackWriter::packReferenceRef(mpack_writer_t *writer, ReferenceRef ref) { if (!ref.exists()) { - mpack_write_nil(&writer); + mpack_write_nil(writer); } else { - mpack_write_u16(&writer, ref.id()); + mpack_write_u16(writer, ref.id()); } } -void MsgpackWriter::packDefinitionRef(DefinitionRef ref) { +void MsgpackWriter::packDefinitionRef(mpack_writer_t *writer, DefinitionRef ref) { if (!ref.exists()) { - mpack_write_nil(&writer); + mpack_write_nil(writer); } else { - mpack_write_u16(&writer, ref.id()); + mpack_write_u16(writer, ref.id()); } } -void MsgpackWriter::packRange(uint32_t begin, uint32_t end) { - mpack_write_u64(&writer, ((uint64_t)begin << 32) | end); +void MsgpackWriter::packRange(mpack_writer_t *writer, uint32_t begin, uint32_t end) { + if (version <= 4) { + mpack_write_u64(writer, ((uint64_t)begin << 32) | end); + } else { + // The above scheme almost certainly implies that the value written + // will be larger than 2**32 and therefore will take up a full nine + // bytes of space in the msgpack file. + // + // Writing the values as individual u32s means that each can take + // advantage of being written as a fixuint/u8/u16/u32 in the output; + // the length of the range will be usually a fixuint or a u8, resulting + // in even smaller output than writing the endpoint of the range. + mpack_write_u32(writer, begin); + mpack_write_u32(writer, end - begin); + } } -void MsgpackWriter::packDefinition(core::Context ctx, ParsedFile &pf, Definition &def, +void MsgpackWriter::packDefinition(mpack_writer_t *writer, core::Context ctx, ParsedFile &pf, Definition &def, const AutogenConfig &autogenCfg) { - mpack_start_array(&writer, defAttrs[version].size()); + mpack_start_array(writer, defAttrs[version].size()); // raw_full_name auto raw_full_name = pf.showFullName(ctx, def.id); - packNames(raw_full_name); + packNames(writer, raw_full_name); // type auto defType = def.type; - if (version <= 2 && defType == Definition::Type::TypeAlias) { - defType = Definition::Type::Casgn; - } - mpack_write_u8(&writer, static_cast(defType)); + mpack_write_u8(writer, static_cast(defType)); // defines_behavior - packBool(def.defines_behavior); + packBool(writer, def.defines_behavior); const auto &filePath = ctx.file.data(ctx).path(); ENFORCE(!def.defines_behavior || !ctx.file.data(ctx).isRBI() || absl::c_any_of(autogenCfg.behaviorAllowedInRBIsPaths, @@ -87,89 +97,143 @@ void MsgpackWriter::packDefinition(core::Context ctx, ParsedFile &pf, Definition "RBI files should never define behavior"); // isEmpty - packBool(def.is_empty); + packBool(writer, def.is_empty); // parent_ref - packReferenceRef(def.parent_ref); + packReferenceRef(writer, def.parent_ref); // aliased_ref - packReferenceRef(def.aliased_ref); + packReferenceRef(writer, def.aliased_ref); // defining_ref - packReferenceRef(def.defining_ref); - mpack_finish_array(&writer); + packReferenceRef(writer, def.defining_ref); + mpack_finish_array(writer); } -void MsgpackWriter::packReference(core::Context ctx, ParsedFile &pf, Reference &ref) { - mpack_start_array(&writer, refAttrs[version].size()); +void MsgpackWriter::packReference(mpack_writer_t *writer, core::Context ctx, ParsedFile &pf, Reference &ref) { + mpack_start_array(writer, refAttrs[version].size()); // scope - packDefinitionRef(ref.scope.id()); + packDefinitionRef(writer, ref.scope.id()); // name - packNames(ref.name.nameParts); + packNames(writer, ref.name.nameParts); // nesting - mpack_start_array(&writer, ref.nesting.size()); - for (auto &scope : ref.nesting) { - packDefinitionRef(scope.id()); + if (version >= 6) { + mpack_write_u32(writer, ref.nestingId); + } else { + auto &nesting = pf.nestings[ref.nestingId]; + mpack_start_array(writer, nesting.size()); + for (auto &scope : nesting) { + packDefinitionRef(writer, scope.id()); + } + mpack_finish_array(writer); } - mpack_finish_array(&writer); // expression_range auto expression_range = ctx.locAt(ref.definitionLoc).position(ctx); - packRange(expression_range.first.line, expression_range.second.line); + packRange(writer, expression_range.first.line, expression_range.second.line); // expression_pos_range - packRange(ref.loc.beginPos(), ref.loc.endPos()); + packRange(writer, ref.loc.beginPos(), ref.loc.endPos()); // resolved if (ref.resolved.empty()) { - mpack_write_nil(&writer); + mpack_write_nil(writer); + } else if (version >= 6 && absl::c_equal(ref.name.nameParts, ref.resolved.nameParts)) { + mpack_write_true(writer); } else { - packNames(ref.resolved.nameParts); + packNames(writer, ref.resolved.nameParts); } // is_defining_ref - packBool(ref.is_defining_ref); + packBool(writer, ref.is_defining_ref); // parent_of - packDefinitionRef(ref.parent_of); - mpack_finish_array(&writer); + packDefinitionRef(writer, ref.parent_of); + + mpack_finish_array(writer); } -// symbols[0..(typeCount-1)] are reserved for the Type aliases MsgpackWriter::MsgpackWriter(int version) : version(assertValidVersion(version)), refAttrs(refAttrMap.at(version)), defAttrs(defAttrMap.at(version)), - symbols(typeCount.at(version)) {} + pfAttrs(parsedFileAttrMap.at(version)) {} + +void writeSymbols(core::Context ctx, mpack_writer_t *writer, const vector &symbols) { + mpack_start_array(writer, symbols.size()); + for (auto sym : symbols) { + auto str = sym.shortName(ctx); + packString(writer, str); + } + mpack_finish_array(writer); +} string MsgpackWriter::pack(core::Context ctx, ParsedFile &pf, const AutogenConfig &autogenCfg) { char *body; size_t bodySize; + mpack_writer_t writer; mpack_writer_init_growable(&writer, &body, &bodySize); + + bool symbolsInBody = version >= 6; + mpack_start_array(&writer, 6); mpack_write_true(&writer); // did_resolution - packString(ctx.state.getPrintablePath(pf.path)); + packString(&writer, ctx.state.getPrintablePath(pf.path)); mpack_write_u32(&writer, pf.cksum); // requires mpack_start_array(&writer, pf.requireStatements.size()); for (auto nm : pf.requireStatements) { - packString(nm.show(ctx)); + packString(&writer, nm.show(ctx)); } mpack_finish_array(&writer); - mpack_start_array(&writer, pf.defs.size()); - for (auto &def : pf.defs) { - packDefinition(ctx, pf, def, autogenCfg); + size_t preDefsSize = mpack_writer_buffer_used(&writer); + + // This is a little awkward. We want to write the symbols used by + // defs and refs here, but the symbols hash isn't populated until after + // we've written the defs and refs. So we're going to redirect + // everything into a temporary buffer, write the now-populated + // symbols, then write the temporary buffer as raw bytes. + char *temporary; + size_t temporarySize; + mpack_writer_t temporaryWriter; + mpack_writer_init_growable(&temporaryWriter, &temporary, &temporarySize); + { + mpack_start_array(&temporaryWriter, pf.defs.size()); + for (auto &def : pf.defs) { + packDefinition(&temporaryWriter, ctx, pf, def, autogenCfg); + } + mpack_finish_array(&temporaryWriter); + + if (version >= 6) { + mpack_start_array(&temporaryWriter, pf.nestings.size()); + for (auto &nesting : pf.nestings) { + mpack_start_array(&temporaryWriter, nesting.size()); + for (auto &scope : nesting) { + packDefinitionRef(&temporaryWriter, scope.id()); + } + mpack_finish_array(&temporaryWriter); + } + mpack_finish_array(&temporaryWriter); + } + + mpack_start_array(&temporaryWriter, pf.refs.size()); + for (auto &ref : pf.refs) { + packReference(&temporaryWriter, ctx, pf, ref); + } + mpack_finish_array(&temporaryWriter); } - mpack_finish_array(&writer); - mpack_start_array(&writer, pf.refs.size()); - for (auto &ref : pf.refs) { - packReference(ctx, pf, ref); + if (symbolsInBody) { + writeSymbols(ctx, &writer, symbols); } - mpack_finish_array(&writer); + + mpack_writer_destroy(&temporaryWriter); + mpack_write_object_bytes(&writer, temporary, temporarySize); + MPACK_FREE(temporary); + mpack_finish_array(&writer); mpack_writer_destroy(&writer); @@ -179,68 +243,50 @@ string MsgpackWriter::pack(core::Context ctx, ParsedFile &pf, const AutogenConfi size_t headerSize; mpack_writer_init_growable(&writer, &header, &headerSize); - mpack_start_map(&writer, 5); + mpack_start_array(&writer, pfAttrs.size()); - packString("symbols"); - int i = -1; - int numTypes = typeCount.at(version); - mpack_start_array(&writer, symbols.size()); - for (auto sym : symbols) { - ++i; - string_view str; - if (i < numTypes) { - switch ((Definition::Type)i) { - case Definition::Type::Module: - str = "module"sv; - break; - case Definition::Type::Class: - str = "class"sv; - break; - case Definition::Type::Casgn: - str = "casgn"sv; - break; - case Definition::Type::Alias: - str = "alias"sv; - break; - case Definition::Type::TypeAlias: - str = "typealias"sv; - break; - default: { - // shouldn't happen - auto v = sym.shortName(ctx); - static_assert(std::is_same_v, "shortName doesn't return the right thing"); - str = v; - break; - } - } - } else { - auto v = sym.shortName(ctx); - static_assert(std::is_same_v, "shortName doesn't return the right thing"); - str = v; - } + if (!symbolsInBody) { + writeSymbols(ctx, &writer, symbols); + } - packString(str); + if (version >= 6) { + uint32_t value = 0; + + switch (pf.tree.file.data(ctx).strictLevel) { + case sorbet::core::StrictLevel::Ignore: + value = 1; + break; + case sorbet::core::StrictLevel::False: + value = 2; + break; + case sorbet::core::StrictLevel::True: + value = 3; + break; + case sorbet::core::StrictLevel::Strict: + value = 4; + break; + case sorbet::core::StrictLevel::Strong: + value = 5; + break; + default: + // Default value already set at 0. + break; + } + mpack_write_u32(&writer, value); } - mpack_finish_array(&writer); - packString("ref_count"); mpack_write_u32(&writer, pf.refs.size()); - packString("def_count"); mpack_write_u32(&writer, pf.defs.size()); - packString("ref_attrs"); - mpack_start_array(&writer, refAttrs.size()); - for (auto attr : refAttrs) { - packString(attr); + if (symbolsInBody) { + mpack_write_u32(&writer, symbols.size()); } - mpack_finish_array(&writer); - packString("def_attrs"); - mpack_start_array(&writer, defAttrs.size()); - for (auto attr : defAttrs) { - packString(attr); - } - mpack_finish_array(&writer); + // v5 and up record the size of the parsed file's body to enable fast skipping + // of the entire data chunk, rather than reading and discarding + // individual msgpack fields. + size_t fieldsSize = bodySize - preDefsSize; + mpack_write_u64(&writer, fieldsSize); mpack_write_object_bytes(&writer, body, bodySize); MPACK_FREE(body); @@ -253,71 +299,106 @@ string MsgpackWriter::pack(core::Context ctx, ParsedFile &pf, const AutogenConfi return ret; } -// Support back-compat down to V2. V3 includes an additional -// symbol for definition Type, namely TypeAlias. -const map MsgpackWriter::typeCount{ - {2, 4}, - {3, 5}, - {4, 5}, -}; +string buildGlobalHeader(int version, int serializedVersion, size_t numFiles, const std::vector &refAttrs, + const std::vector &defAttrs, const std::vector &pfAttrs) { + string header; -const map> MsgpackWriter::refAttrMap{ - { - 2, - { - "scope", - "name", - "nesting", - "expression_range", - "expression_pos_range", - "resolved", - "is_defining_ref", - "parent_of", - }, - }, + mpack_writer_t writer; + char *body; + size_t bodySize; + mpack_writer_init_growable(&writer, &body, &bodySize); + + mpack_write_u32(&writer, serializedVersion); + + mpack_start_array(&writer, pfAttrs.size()); + for (const auto &attr : pfAttrs) { + packString(&writer, attr); + } + + mpack_start_array(&writer, refAttrs.size()); + for (const auto &attr : refAttrs) { + packString(&writer, attr); + } + mpack_finish_array(&writer); + + mpack_start_array(&writer, defAttrs.size()); + for (const auto &attr : defAttrs) { + packString(&writer, attr); + } + mpack_finish_array(&writer); + + mpack_write_u64(&writer, numFiles); + + mpack_writer_destroy(&writer); + + header = string(body, bodySize); + MPACK_FREE(body); + + return header; +} + +string MsgpackWriter::msgpackGlobalHeader(int version, size_t numFiles) { + const vector &pfAttrs = parsedFileAttrMap.at(version); + const vector &refAttrs = refAttrMap.at(version); + const vector &defAttrs = defAttrMap.at(version); + + return buildGlobalHeader(version, version, numFiles, refAttrs, defAttrs, pfAttrs); +} + +const map> MsgpackWriter::parsedFileAttrMap{ { - 3, + 5, { - "scope", - "name", - "nesting", - "expression_range", - "expression_pos_range", - "resolved", - "is_defining_ref", - "parent_of", + "symbols", + "ref_count", + "def_count", + "defs_and_refs_size", }, }, { - 4, + 6, { - "scope", - "name", - "nesting", - "expression_range", - "expression_pos_range", - "resolved", - "is_defining_ref", - "parent_of", + "typed_level", + "ref_count", + "def_count", + "sym_count", + "body_size", }, }, }; +const map> MsgpackWriter::refAttrMap{ + {5, + { + "scope", + "name", + "nesting", + "expr_range_start", + "expr_range_len", + "expr_pos_range_start", + "expr_pos_range_len", + "resolved", + "is_defining_ref", + "parent_of", + }}, + {6, + { + "scope", + "name", + "nesting", + "expr_range_start", + "expr_range_len", + "expr_pos_range_start", + "expr_pos_range_len", + "resolved", + "is_defining_ref", + "parent_of", + }}, +}; + const map> MsgpackWriter::defAttrMap{ { - 2, - { - "raw_full_name", - "type", - "defines_behavior", - "is_empty", - "parent_ref", - "aliased_ref", - "defining_ref", - }, - }, - { - 3, + 5, { "raw_full_name", "type", @@ -329,7 +410,7 @@ const map> MsgpackWriter::defAttrMap{ }, }, { - 4, + 6, { "raw_full_name", "type", diff --git a/main/autogen/data/msgpack.h b/main/autogen/data/msgpack.h index 07ef990657..73bb7b6af9 100644 --- a/main/autogen/data/msgpack.h +++ b/main/autogen/data/msgpack.h @@ -9,29 +9,29 @@ namespace sorbet::autogen { class MsgpackWriter { -private: +protected: int version; const std::vector &refAttrs; const std::vector &defAttrs; - mpack_writer_t writer; + const std::vector &pfAttrs; std::vector symbols; UnorderedMap symbolIds; static const std::map> refAttrMap; static const std::map> defAttrMap; - static const std::map typeCount; + static const std::map> parsedFileAttrMap; // a bunch of helpers - void packName(core::NameRef nm); - void packNames(std::vector &names); - void packString(std::string_view str); - void packBool(bool b); - void packReferenceRef(ReferenceRef ref); - void packDefinitionRef(DefinitionRef ref); - void packRange(uint32_t begin, uint32_t end); - void packDefinition(core::Context ctx, ParsedFile &pf, Definition &def, const AutogenConfig &autogenCfg); - void packReference(core::Context ctx, ParsedFile &pf, Reference &ref); + void packName(mpack_writer_t *writer, core::NameRef nm); + void packNames(mpack_writer_t *writer, std::vector &names); + void packBool(mpack_writer_t *writer, bool b); + void packReferenceRef(mpack_writer_t *writer, ReferenceRef ref); + void packDefinitionRef(mpack_writer_t *writer, DefinitionRef ref); + void packRange(mpack_writer_t *writer, uint32_t begin, uint32_t end); + virtual void packDefinition(mpack_writer_t *writer, core::Context ctx, ParsedFile &pf, Definition &def, + const AutogenConfig &autogenCfg); + virtual void packReference(mpack_writer_t *writer, core::Context ctx, ParsedFile &pf, Reference &ref); static int assertValidVersion(int version) { if (version < AutogenVersion::MIN_VERSION || version > AutogenVersion::MAX_VERSION) { Exception::raise("msgpack version {} not in available range [{}, {}]", version, AutogenVersion::MIN_VERSION, @@ -43,9 +43,49 @@ class MsgpackWriter { public: MsgpackWriter(int version); + static std::string msgpackGlobalHeader(int version, size_t numFiles); + virtual std::string pack(core::Context ctx, ParsedFile &pf, const AutogenConfig &autogenCfg); +}; + +// Lightweight version of writer that skips all reference metadata like expression ranges, inheritance information, +// typing information, etc. Reduces size by ~37% for Stripe code. +class MsgpackWriterLite : public MsgpackWriter { +private: + int version; + const std::vector &refAttrs; + const std::vector &defAttrs; + const std::vector &pfAttrs; + + static const std::map> refAttrMap; + static const std::map> defAttrMap; + static const std::map> parsedFileAttrMap; + + static int assertValidVersion(int version) { + // Lite Msgpack writer is only supported after version 6. + if (version < 6 && version > AutogenVersion::MAX_VERSION) { + Exception::raise("msgpack version {} not in available range [6, {}]", version, 6, + AutogenVersion::MAX_VERSION); + } + return version; + } + + void packDefinition(mpack_writer_t *writer, core::Context ctx, ParsedFile &pf, Definition &def, + const AutogenConfig &autogenCfg); + void packReference(mpack_writer_t *writer, core::Context ctx, ParsedFile &pf, Reference &ref); + +public: + MsgpackWriterLite(int version); + + static std::string msgpackGlobalHeader(int version, size_t numFiles); std::string pack(core::Context ctx, ParsedFile &pf, const AutogenConfig &autogenCfg); }; +void packString(mpack_writer_t *writer, std::string_view str); +void writeSymbols(core::Context ctx, mpack_writer_t *writer, const std::vector &symbols); +std::string buildGlobalHeader(int version, int serializedVersion, size_t numFiles, + const std::vector &refAttrs, const std::vector &defAttrs, + const std::vector &pfAttrs); + } // namespace sorbet::autogen #endif // AUTOGEN_MSGPACK_H diff --git a/main/autogen/data/msgpack_lite.cc b/main/autogen/data/msgpack_lite.cc new file mode 100644 index 0000000000..4c3875e532 --- /dev/null +++ b/main/autogen/data/msgpack_lite.cc @@ -0,0 +1,172 @@ +// has to go first because it violates our poisons +#include "mpack/mpack.h" + +#include "absl/strings/match.h" +#include "core/GlobalState.h" +#include "main/autogen/data/definitions.h" +#include "main/autogen/data/msgpack.h" + +using namespace std; +namespace sorbet::autogen { + +void MsgpackWriterLite::packDefinition(mpack_writer_t *writer, core::Context ctx, ParsedFile &pf, Definition &def, + const AutogenConfig &autogenCfg) { + mpack_start_array(writer, defAttrs[version].size()); + + // raw_full_name + auto raw_full_name = pf.showFullName(ctx, def.id); + packNames(writer, raw_full_name); + + // defines_behavior + packBool(writer, def.defines_behavior); + const auto &filePath = ctx.file.data(ctx).path(); + ENFORCE(!def.defines_behavior || !ctx.file.data(ctx).isRBI() || + absl::c_any_of(autogenCfg.behaviorAllowedInRBIsPaths, + [&](auto &allowedPath) { return absl::StartsWith(filePath, allowedPath); }), + "RBI files should never define behavior"); + + mpack_finish_array(writer); +} + +void MsgpackWriterLite::packReference(mpack_writer_t *writer, core::Context ctx, ParsedFile &pf, Reference &ref) { + mpack_start_array(writer, refAttrs[version].size()); + + // name + packNames(writer, ref.name.nameParts); + + // resolved + if (ref.resolved.empty()) { + mpack_write_nil(writer); + } else if (absl::c_equal(ref.name.nameParts, ref.resolved.nameParts)) { + mpack_write_true(writer); + } else { + packNames(writer, ref.resolved.nameParts); + } + + mpack_finish_array(writer); +} + +MsgpackWriterLite::MsgpackWriterLite(int version) + : MsgpackWriter(version), version(assertValidVersion(version)), refAttrs(refAttrMap.at(version)), + defAttrs(defAttrMap.at(version)), pfAttrs(parsedFileAttrMap.at(version)) {} + +string MsgpackWriterLite::pack(core::Context ctx, ParsedFile &pf, const AutogenConfig &autogenCfg) { + char *body; + size_t bodySize; + mpack_writer_t writer; + mpack_writer_init_growable(&writer, &body, &bodySize); + + mpack_start_array(&writer, 3); + + // path: 1 + packString(&writer, ctx.state.getPrintablePath(pf.path)); + + size_t preDefsSize = mpack_writer_buffer_used(&writer); + + // This is a little awkward. We want to write the symbols used by + // defs and refs here, but the symbols hash isn't populated until after + // we've written the defs and refs. So we're going to redirect + // everything into a temporary buffer, write the now-populated + // symbols, then write the temporary buffer as raw bytes. + char *temporary; + size_t temporarySize; + mpack_writer_t temporaryWriter; + mpack_writer_init_growable(&temporaryWriter, &temporary, &temporarySize); + { + mpack_start_array(&temporaryWriter, pf.defs.size()); + for (auto &def : pf.defs) { + packDefinition(&temporaryWriter, ctx, pf, def, autogenCfg); + } + mpack_finish_array(&temporaryWriter); + + mpack_start_array(&temporaryWriter, pf.refs.size()); + for (auto &ref : pf.refs) { + packReference(&temporaryWriter, ctx, pf, ref); + } + mpack_finish_array(&temporaryWriter); + } + + // symbols: 2 + writeSymbols(ctx, &writer, symbols); + + mpack_writer_destroy(&temporaryWriter); + + // defs / refs: 3 + mpack_write_object_bytes(&writer, temporary, temporarySize); + MPACK_FREE(temporary); + + mpack_finish_array(&writer); + + mpack_writer_destroy(&writer); + + // write header + char *header; + size_t headerSize; + mpack_writer_init_growable(&writer, &header, &headerSize); + + mpack_start_array(&writer, pfAttrs.size()); + + mpack_write_u32(&writer, pf.refs.size()); + mpack_write_u32(&writer, pf.defs.size()); + mpack_write_u32(&writer, symbols.size()); + + // v5 and up record the size of the parsed file's body to enable fast skipping + // of the entire data chunk, rather than reading and discarding + // individual msgpack fields. + size_t fieldsSize = bodySize - preDefsSize; + mpack_write_u64(&writer, fieldsSize); + + mpack_write_object_bytes(&writer, body, bodySize); + MPACK_FREE(body); + + mpack_writer_destroy(&writer); + + auto ret = string(header, headerSize); + MPACK_FREE(header); + + return ret; +} + +// TODO (aadi-stripe, 6/20/2024): Ideally this static function does need to be repeated in the +// MsgpackWriterLite class, consider refactoring by using templates. +string MsgpackWriterLite::msgpackGlobalHeader(int version, size_t numFiles) { + const vector &pfAttrs = parsedFileAttrMap.at(version); + const vector &refAttrs = refAttrMap.at(version); + const vector &defAttrs = defAttrMap.at(version); + + // set a high-bit (bit 8) on version to 1, to indicate "lite" msgpack mode. + int serializedVersion = version + 128; + return buildGlobalHeader(version, serializedVersion, numFiles, refAttrs, defAttrs, pfAttrs); +} + +const map> MsgpackWriterLite::parsedFileAttrMap{ + { + 6, + { + "ref_count", + "def_count", + "sym_count", + "body_size", + }, + }, +}; + +const map> MsgpackWriterLite::refAttrMap{ + {6, + { + "name", + "resolved", + }}, +}; + +const map> MsgpackWriterLite::defAttrMap{ + { + 6, + { + "raw_full_name", + "defines_behavior", + }, + }, +}; + +} // namespace sorbet::autogen diff --git a/main/autogen/data/version.h b/main/autogen/data/version.h index 9f488ec4b7..446d42dc03 100644 --- a/main/autogen/data/version.h +++ b/main/autogen/data/version.h @@ -5,12 +5,13 @@ namespace sorbet::autogen { class AutogenVersion { public: - constexpr static int MIN_VERSION = 2; - constexpr static int MAX_VERSION = 4; + constexpr static int MIN_VERSION = 5; + constexpr static int MAX_VERSION = 6; // Version history: // 3 - Add type alias information in dependency db output // 4 - Include .rbi reference information in dependency db + // 5 - Pack information more tightly in various places constexpr static int VERSION_INCLUDE_RBI = 4; }; diff --git a/main/autogen/subclasses.cc b/main/autogen/subclasses.cc index a20d981435..b655af7014 100644 --- a/main/autogen/subclasses.cc +++ b/main/autogen/subclasses.cc @@ -1,8 +1,10 @@ #include "main/autogen/subclasses.h" +#include "absl/strings/str_split.h" #include "common/FileOps.h" #include "common/sort/sort.h" #include "common/strings/formatting.h" #include "core/GlobalState.h" +#include "core/Unfreeze.h" using namespace std; namespace sorbet::autogen { @@ -35,7 +37,7 @@ bool Subclasses::isFileIgnored(const std::string &path, const std::vector Subclasses::listAllSubclasses(core::Context ctx, ParsedFile &pf, +optional Subclasses::listAllSubclasses(core::Context ctx, const ParsedFile &pf, const vector &absoluteIgnorePatterns, const vector &relativeIgnorePatterns) { // Prepend "/" because absoluteIgnorePatterns and relativeIgnorePatterns are always "/"-prefixed @@ -52,18 +54,8 @@ optional Subclasses::listAllSubclasses(core::Context ctx, Parse continue; } - // Get fully-qualified parent name as string - string parentName = - fmt::format("{}", fmt::map_join(ref.resolved.nameParts, - "::", [&ctx](const core::NameRef &nm) -> string { return nm.show(ctx); })); - - // Add child class to the set identified by its parent - string childName = - fmt::format("{}", fmt::map_join(pf.showFullName(ctx, defn), - "::", [&ctx](const core::NameRef &nm) -> string { return nm.show(ctx); })); - - auto &mapEntry = out[parentName]; - mapEntry.entries.insert(make_pair(childName, defn.data(pf).type)); + auto &mapEntry = out[ref.sym]; + mapEntry.entries.insert(defn.data(pf).sym.asClassOrModuleRef()); mapEntry.classKind = ref.parentKind; } @@ -73,8 +65,8 @@ optional Subclasses::listAllSubclasses(core::Context ctx, Parse // Generate all descendants of a parent class // Recursively walks `childMap`, which stores the IMMEDIATE children of subclassed class. optional Subclasses::descendantsOf(const Subclasses::Map &childMap, - const string &parentName) { - auto fnd = childMap.find(parentName); + const core::SymbolRef &parentRef) { + auto fnd = childMap.find(parentRef); if (fnd == childMap.end()) { return nullopt; } @@ -82,8 +74,8 @@ optional Subclasses::descendantsOf(const Subclasses::M Subclasses::Entries out; out.insert(children.begin(), children.end()); - for (const auto &[name, _type] : children) { - auto descendants = Subclasses::descendantsOf(childMap, name); + for (const auto &childRef : children) { + auto descendants = Subclasses::descendantsOf(childMap, childRef); if (descendants) { out.insert(descendants->entries.begin(), descendants->entries.end()); } @@ -92,35 +84,50 @@ optional Subclasses::descendantsOf(const Subclasses::M return SubclassInfo(fnd->second.classKind, std::move(out)); } +const core::SymbolRef Subclasses::getConstantRef(const core::GlobalState &gs, string rawName) { + core::ClassOrModuleRef sym = core::Symbols::root(); + + for (auto &n : absl::StrSplit(rawName, "::")) { + const auto &nameRef = gs.lookupNameUTF8(n); + if (!nameRef.exists()) + return core::Symbols::noSymbol(); + sym = gs.lookupClassSymbol(sym, gs.lookupNameConstant(nameRef)); + } + return sym; +} + // Manually patch the child map to account for inheritance that happens at runtime `self.included` // Please do not add to this list. -void Subclasses::patchChildMap(Subclasses::Map &childMap) { - childMap["Opus::SafeMachine"].entries.insert(childMap["Opus::Risk::Model::Mixins::RiskSafeMachine"].entries.begin(), - childMap["Opus::Risk::Model::Mixins::RiskSafeMachine"].entries.end()); +void Subclasses::patchChildMap(const core::GlobalState &gs, Subclasses::Map &childMap) { + auto safeMachineRef = getConstantRef(gs, "Opus::SafeMachine"); + auto riskSafeMachineRef = getConstantRef(gs, "Opus::Risk::Model::Mixins::RiskSafeMachine"); + if (!safeMachineRef.exists() || !riskSafeMachineRef.exists()) + return; + childMap[safeMachineRef].entries.insert(childMap[riskSafeMachineRef].entries.begin(), + childMap[riskSafeMachineRef].entries.end()); } -vector Subclasses::serializeSubclassMap(const Subclasses::Map &descendantsMap, - const vector &parentNames) { +vector Subclasses::serializeSubclassMap(const core::GlobalState &gs, const Subclasses::Map &descendantsMap, + const vector &parentNames) { vector descendantsMapSerialized; - - for (const string &parentName : parentNames) { - auto fnd = descendantsMap.find(parentName); + for (const auto &parentRef : parentNames) { + auto fnd = descendantsMap.find(parentRef); if (fnd == descendantsMap.end()) { continue; } const Subclasses::SubclassInfo &children = fnd->second; + string parentName = parentRef.show(gs); + auto type = children.classKind == ClassKind::Class ? "class" : "module"; descendantsMapSerialized.emplace_back(fmt::format("{} {}", type, parentName)); auto subclassesStart = descendantsMapSerialized.size(); - for (const auto &[name, type] : children.entries) { - // Ignore Modules - if (type == autogen::Definition::Type::Class) { - descendantsMapSerialized.emplace_back(fmt::format(" class {}", name)); - } else { - descendantsMapSerialized.emplace_back(fmt::format(" module {}", name)); - } + for (const auto &childRef : children.entries) { + string_view path = gs.getPrintablePath(childRef.data(gs)->loc().file().data(gs).path()); + string childName = childRef.show(gs); + auto type = childRef.data(gs)->isClass() ? "class" : "module"; + descendantsMapSerialized.emplace_back(fmt::format(" {} {} {}", type, childName, path)); } fast_sort_range(descendantsMapSerialized.begin() + subclassesStart, descendantsMapSerialized.end()); @@ -140,30 +147,31 @@ vector Subclasses::serializeSubclassMap(const Subclasses::Map &descendan // // This effectively replaces pay-server's `DescendantsMap` in `InheritedClassesStep` with a much // faster implementation. -vector Subclasses::genDescendantsMap(Subclasses::Map &childMap, vector &parentNames) { - Subclasses::patchChildMap(childMap); +vector Subclasses::genDescendantsMap(const core::GlobalState &gs, Subclasses::Map &childMap, + vector &parentRefs) { + Subclasses::patchChildMap(gs, childMap); // Generate descendants for each passed-in superclass - fast_sort(parentNames); + fast_sort(parentRefs, [&gs](const auto &left, const auto &right) { return left.show(gs) < right.show(gs); }); Subclasses::Map descendantsMap; - for (const string &parentName : parentNames) { + for (const auto &parentRef : parentRefs) { // Skip parents that the user asked for but which don't // exist or are never subclassed. - auto fnd = childMap.find(parentName); + auto fnd = childMap.find(parentRef); if (fnd == childMap.end()) { continue; } - auto descendants = Subclasses::descendantsOf(childMap, parentName); + auto descendants = Subclasses::descendantsOf(childMap, parentRef); if (!descendants) { // Initialize an empty entry associated with parentName. - descendantsMap[parentName]; + descendantsMap[parentRef]; } else { - descendantsMap.emplace(parentName, std::move(*descendants)); + descendantsMap.emplace(parentRef, std::move(*descendants)); } } - return Subclasses::serializeSubclassMap(descendantsMap, parentNames); + return Subclasses::serializeSubclassMap(gs, descendantsMap, parentRefs); }; } // namespace sorbet::autogen diff --git a/main/autogen/subclasses.h b/main/autogen/subclasses.h index 0c54c9df7e..f62ed1227e 100644 --- a/main/autogen/subclasses.h +++ b/main/autogen/subclasses.h @@ -7,8 +7,7 @@ namespace sorbet::autogen { class Subclasses final { public: - using Entry = std::pair; - using Entries = UnorderedSet; + using Entries = UnorderedSet; struct SubclassInfo { ClassKind classKind = ClassKind::Module; Entries entries; @@ -16,20 +15,23 @@ class Subclasses final { SubclassInfo() = default; SubclassInfo(ClassKind classKind, Entries entries) : classKind(classKind), entries(std::move(entries)){}; }; - using Map = UnorderedMap; + using Map = UnorderedMap; - static std::optional listAllSubclasses(core::Context ctx, ParsedFile &pf, + static std::optional listAllSubclasses(core::Context ctx, const ParsedFile &pf, const std::vector &absoluteIgnorePatterns, - const std::vector &relativeIgnorePatterns); - static std::vector genDescendantsMap(Subclasses::Map &childMap, std::vector &parentNames); + const std::vector &relativeIgnorePattern); + static std::vector genDescendantsMap(const core::GlobalState &gs, Subclasses::Map &childMap, + std::vector &parentRefs); + static const core::SymbolRef getConstantRef(const core::GlobalState &gs, std::string rawName); private: - static void patchChildMap(Subclasses::Map &childMap); + static void patchChildMap(const core::GlobalState &gs, Subclasses::Map &childMap); static bool isFileIgnored(const std::string &path, const std::vector &absoluteIgnorePatterns, const std::vector &relativeIgnorePatterns); - static std::optional descendantsOf(const Subclasses::Map &childMap, const std::string &parents); - static std::vector serializeSubclassMap(const Subclasses::Map &descendantsMap, - const std::vector &parentNames); + static std::optional descendantsOf(const Subclasses::Map &childMap, const core::SymbolRef &parentRef); + static std::vector serializeSubclassMap(const core::GlobalState &gs, + const Subclasses::Map &descendantsMap, + const std::vector &parentNames); }; } // namespace sorbet::autogen diff --git a/main/autogen/test/constant_hash_test.cc b/main/autogen/test/constant_hash_test.cc index 3d7664207c..fd44e1edd3 100644 --- a/main/autogen/test/constant_hash_test.cc +++ b/main/autogen/test/constant_hash_test.cc @@ -174,7 +174,7 @@ TEST_CASE("Require") { // NOLINT // changing the name of a require should affect the hash CHECK_NE(req, helper.hashExample("require 'bar'\n")); - // adding a new requires hould affect the hash + // adding a new require should affect the hash CHECK_NE(req, helper.hashExample("require 'foo'\n" "require 'bar'\n")); @@ -186,18 +186,6 @@ TEST_CASE("Require") { // NOLINT "do_the_thing!")); } -TEST_CASE("Package Autoloader Compatibility") { // NOLINT - Helper helper; - - auto req = helper.hashExample("autoloader_compatibility 'legacy'\n"); - - // removing the annotation should affect the hash - CHECK_NE(req, helper.hashExample("\n")); - - // changing the annotation should affect the hash - CHECK_NE(req, helper.hashExample("autoloader_compatibility 'strict'\n")); -} - TEST_CASE("Extend/Include") { Helper helper; diff --git a/main/cache/BUILD b/main/cache/BUILD index 1b5ffb26ae..ef96204242 100644 --- a/main/cache/BUILD +++ b/main/cache/BUILD @@ -1,7 +1,7 @@ cc_library( name = "cache", srcs = select({ - "//tools/config:webasm": ["cache-orig.cc"], + "@platforms//cpu:wasm32": ["cache-orig.cc"], "//conditions:default": ["cache.cc"], }), hdrs = ["cache.h"], @@ -10,10 +10,11 @@ cc_library( "//conditions:default": 1, }), visibility = ["//visibility:public"], - deps = select({ - "//tools/config:webasm": [], + deps = [ + "//common/kvstore", + ] + select({ + "@platforms//cpu:wasm32": [], "//conditions:default": [ - "//common/kvstore", "//main/options", "//main/pipeline", "//payload:interface", @@ -34,5 +35,7 @@ cc_library( "//conditions:default": 1, }), visibility = ["//visibility:public"], - deps = [], + deps = [ + "//common/kvstore", + ], ) diff --git a/main/cache/cache-orig.cc b/main/cache/cache-orig.cc index 599ed6fbfe..6fbffc48ec 100644 --- a/main/cache/cache-orig.cc +++ b/main/cache/cache-orig.cc @@ -1,3 +1,4 @@ +#include "common/kvstore/KeyValueStore.h" #include "main/cache/cache.h" using namespace std; diff --git a/main/lsp/BUILD b/main/lsp/BUILD index 312c0adbde..b72c2394e7 100644 --- a/main/lsp/BUILD +++ b/main/lsp/BUILD @@ -31,6 +31,7 @@ cc_library( ], hdrs = [ "ConvertToSingletonClassMethod.h", + "ExtractVariable.h", "LSPConfiguration.h", "LSPInput.h", "LSPLoop.h", @@ -38,6 +39,7 @@ cc_library( "LSPOutput.h", "LSPPreprocessor.h", "LSPQuery.h", + "MessageQueueState.h", "MoveMethod.h", "json_types.h", "wrapper.h", diff --git a/main/lsp/ConvertToSingletonClassMethod.cc b/main/lsp/ConvertToSingletonClassMethod.cc index 05decbad1e..50594b6133 100644 --- a/main/lsp/ConvertToSingletonClassMethod.cc +++ b/main/lsp/ConvertToSingletonClassMethod.cc @@ -163,7 +163,7 @@ class MethodCallSiteRewriter : public AbstractRewriter { // This doesn't make any attempt to handle methods that are overridden. // // Technically speaking, this code action doesn't make a ton of sense if the method is - // overriden, because converting to a singleton method will kill dynamic dispatch. + // overridden, because converting to a singleton method will kill dynamic dispatch. // // We have two options: // 1. Assume that there are no overrides of this method. diff --git a/main/lsp/DefLocSaver.cc b/main/lsp/DefLocSaver.cc index e6cf777164..64b903b9d6 100644 --- a/main/lsp/DefLocSaver.cc +++ b/main/lsp/DefLocSaver.cc @@ -117,15 +117,17 @@ void matchesQuery(core::Context ctx, ast::ConstantLit *lit, const core::lsp::Que } core::lsp::ConstantResponse::Scopes scopes; - if (symbol == core::Symbols::StubModule()) { + if (symbolBeforeDealias == core::Symbols::StubModule()) { + // If the symbol is an alias to a stub symbol, it actually resolved, and so it won't + // have resolutionScopes. scopes = *lit->resolutionScopes; } else { scopes = {symbol.owner(ctx)}; } auto enclosingMethod = enclosingMethodFromContext(ctx); - auto resp = core::lsp::ConstantResponse(symbol, symbolBeforeDealias, ctx.locAt(lit->loc), scopes, - unresolved.cnst, tp, enclosingMethod); + auto resp = core::lsp::ConstantResponse(symbolBeforeDealias, ctx.locAt(lit->loc), scopes, unresolved.cnst, + tp, enclosingMethod); core::lsp::QueryResponse::pushQueryResponse(ctx, resp); } lit = ast::cast_tree(unresolved.scope); @@ -136,6 +138,19 @@ void matchesQuery(core::Context ctx, ast::ConstantLit *lit, const core::lsp::Que } } +// This decides if we need to keep a node around incase the current LSP query needs type information for it +bool shouldLeaveAncestorForIDE(const ast::ExpressionPtr &anc) { + // used in Desugar <-> resolver to signal classes that did not have explicit superclass + if (ast::isa_tree(anc) || anc.isSelfReference()) { + return false; + } + auto rcl = ast::cast_tree(anc); + if (rcl && rcl->symbol == core::Symbols::todo()) { + return false; + } + return true; +} + } // namespace void DefLocSaver::postTransformConstantLit(core::Context ctx, ast::ExpressionPtr &tree) { @@ -144,4 +159,24 @@ void DefLocSaver::postTransformConstantLit(core::Context ctx, ast::ExpressionPtr matchesQuery(ctx, &lit, lspQuery, lit.symbol); } +void DefLocSaver::preTransformClassDef(core::Context ctx, ast::ExpressionPtr &tree) { + auto &classDef = ast::cast_tree_nonnull(tree); + const core::lsp::Query &lspQuery = ctx.state.lspQuery; + if (ast::isa_tree(classDef.name)) { + ENFORCE(classDef.symbol == core::Symbols::root()); + } else if (auto *ident = ast::cast_tree(classDef.name)) { + ENFORCE(ident->name == core::Names::singleton()); + } else { + // The `` class we wrap all code with uses EmptyTree for the ClassDef::name field. + auto *lit = ast::cast_tree(classDef.name); + matchesQuery(ctx, lit, lspQuery, lit->symbol); + } + + if (classDef.kind == ast::ClassDef::Kind::Class && !classDef.ancestors.empty() && + shouldLeaveAncestorForIDE(classDef.ancestors.front())) { + auto *lit = ast::cast_tree(classDef.ancestors.front()); + matchesQuery(ctx, lit, lspQuery, lit->symbol); + } +} + } // namespace sorbet::realmain::lsp diff --git a/main/lsp/DefLocSaver.h b/main/lsp/DefLocSaver.h index bc52d57c5f..80f2003563 100644 --- a/main/lsp/DefLocSaver.h +++ b/main/lsp/DefLocSaver.h @@ -13,5 +13,8 @@ class DefLocSaver { // Handles loc and symbol requests for constants. void postTransformConstantLit(core::Context ctx, ast::ExpressionPtr &lit); + + // Handles loc and symbol requests for ClassDef names. + void preTransformClassDef(core::Context ctx, ast::ExpressionPtr &lit); }; }; // namespace sorbet::realmain::lsp diff --git a/main/lsp/ErrorReporter.cc b/main/lsp/ErrorReporter.cc index c304866562..f820861307 100644 --- a/main/lsp/ErrorReporter.cc +++ b/main/lsp/ErrorReporter.cc @@ -167,19 +167,57 @@ void ErrorReporter::pushDiagnostics(uint32_t epoch, core::FileRef file, const ve vector> relatedInformation; for (auto §ion : error->sections) { + if (section.isAutocorrectDescription && !section.isDidYouMean) { + // Just show the "fix available" in the error message, and let the code action title + // describe the fix. De-clutters the error message in LSP view. + continue; + } + string sectionHeader = section.header; + if (section.messages.empty()) { + // Sometimes we just use section headers to report extra information, not connected + // to a specific line. The LSP spec needs a location, so let's just re-use the error->loc. + auto location = config->loc2Location(gs, error->loc); + relatedInformation.push_back( + make_unique(move(location), move(sectionHeader))); + continue; + } + + bool usedSectionHeader = false; for (auto &errorLine : section.messages) { - string message = errorLine.formattedMessage.length() > 0 ? errorLine.formattedMessage : sectionHeader; auto location = config->loc2Location(gs, errorLine.loc); if (location == nullptr) { // This was probably from an addErrorNote call. Still want to report the note. location = config->loc2Location(gs, error->loc); - message = "\n " + message; } if (location == nullptr) { continue; } + + string message; + if (section.isAutocorrectDescription && section.isDidYouMean) { + message = fmt::format("{} (fix available)", sectionHeader); + usedSectionHeader = true; + } else if (errorLine.formattedMessage.length() > 0) { + if (!usedSectionHeader) { + relatedInformation.push_back( + make_unique(location->copy(), sectionHeader)); + usedSectionHeader = true; + } + message = errorLine.formattedMessage; + } else { + message = sectionHeader; + } + + // VSCode strips out leading whitespace, but we use leading whitespaces to convey + // indentation/nesting. This replaces all leading whitespace with a NBSP. + auto firstNonWhitespace = message.find_first_not_of(' '); + for (string::size_type pos = 0; pos != string::npos && pos < firstNonWhitespace; + pos = message.find(' ', pos)) { + message.replace(pos, 1, "\u00A0"); + pos += 1; + } relatedInformation.push_back(make_unique(std::move(location), message)); } } diff --git a/main/lsp/ExtractVariable.cc b/main/lsp/ExtractVariable.cc new file mode 100644 index 0000000000..319944a385 --- /dev/null +++ b/main/lsp/ExtractVariable.cc @@ -0,0 +1,271 @@ +#include "main/lsp/ExtractVariable.h" +#include "ast/treemap/treemap.h" + +using namespace std; + +namespace sorbet::realmain::lsp { + +class ExtractVariableWalk { + // The selection loc + core::Loc targetLoc; + core::LocOffsets matchingLoc; + // It's not valid to extract a parameter, or the lhs of an assign. + // This vector stores the locs for those nodes, so that in + // preTransformExpression, we can skip them. + std::vector skippedLocs; + + void updateEnclosingScope(const ast::ExpressionPtr &node, core::LocOffsets nodeLoc) { + if (!nodeLoc.exists() || !nodeLoc.contains(targetLoc.offsets())) { + return; + } + + if (!enclosingScopeLoc.exists() || enclosingScopeLoc.contains(nodeLoc)) { + enclosingScope = &node; + enclosingScopeLoc = nodeLoc; + return; + } + } + + void skipLoc(core::LocOffsets loc) { + if (loc.exists()) { + skippedLocs.push_back(loc); + } + } + + // NOTE: Might want to profile and switch to UnorderedSet. + bool shouldSkipLoc(core::LocOffsets loc) { + return absl::c_find_if(skippedLocs, [loc](auto l) { return l.contains(loc); }) != skippedLocs.end(); + } + +public: + // After the walk is complete, this should point to the deepest scope that contains targetLoc + const ast::ExpressionPtr *enclosingScope; + // enclosingScope is the ClassDef/MethodDef/etc. that contains targetLoc. + // enclosingScopeLoc stores the Loc of the body of that scope. + // For example, the RHS of the ClassDef doesn't have an ExpressionPtr with a Loc, + // but enclosingScopeLoc will be a Loc that represents the body of the ClassDef RHS + // (excluding things like the class name, superclass, and class/end keywords). + core::LocOffsets enclosingScopeLoc; + + ExtractVariableWalk(core::Loc targetLoc) + : targetLoc(targetLoc), matchingLoc(core::LocOffsets::none()), enclosingScopeLoc(core::LocOffsets::none()) {} + + void preTransformExpressionPtr(core::Context ctx, const ast::ExpressionPtr &tree) { + if (tree.loc() == targetLoc.offsets()) { + // It's not valid to extract the following node types + if (!ast::isa_tree(tree) && !ast::isa_tree(tree) && + !ast::isa_tree(tree) && !ast::isa_tree(tree) && + !ast::isa_tree(tree) && !ast::isa_tree(tree)) { + matchingLoc = tree.loc(); + } + } + } + + bool foundExactMatch() { + return matchingLoc.exists() && !shouldSkipLoc(matchingLoc); + } + + void preTransformInsSeq(core::Context ctx, const ast::ExpressionPtr &tree) { + auto &insSeq = ast::cast_tree_nonnull(tree); + updateEnclosingScope(tree, insSeq.loc); + } + + void preTransformClassDef(core::Context ctx, const ast::ExpressionPtr &tree) { + auto &classDef = ast::cast_tree_nonnull(tree); + updateEnclosingScope(tree, classDef.rhs.front().loc().join(classDef.rhs.back().loc())); + } + + void preTransformMethodDef(core::Context ctx, const ast::ExpressionPtr &tree) { + auto &methodDef = ast::cast_tree_nonnull(tree); + if (!methodDef.args.empty()) { + skipLoc(methodDef.args.front().loc().join(methodDef.args.back().loc())); + } + if (methodDef.loc.endPos() == methodDef.rhs.loc().endPos()) { + // methodDef.loc.endPos() represent the location right after the `end`, + // while methodDef.rhs.loc().endPos() is the location right before the `end`. + // If both are the same, that means that this is an endless method. + skipLoc(methodDef.rhs.loc()); + } else { + updateEnclosingScope(tree, methodDef.rhs.loc()); + } + } + + void preTransformBlock(core::Context ctx, const ast::ExpressionPtr &tree) { + auto &block = ast::cast_tree_nonnull(tree); + if (!block.args.empty()) { + skipLoc(block.args.front().loc().join(block.args.back().loc())); + } + updateEnclosingScope(tree, block.body.loc()); + } + + void preTransformAssign(core::Context ctx, const ast::ExpressionPtr &tree) { + auto &assign = ast::cast_tree_nonnull(tree); + skipLoc(assign.lhs.loc()); + } + + void preTransformIf(core::Context ctx, const ast::ExpressionPtr &tree) { + auto &if_ = ast::cast_tree_nonnull(tree); + updateEnclosingScope(tree, if_.thenp.loc()); + updateEnclosingScope(tree, if_.elsep.loc()); + } + + void preTransformRescue(core::Context ctx, const ast::ExpressionPtr &tree) { + auto &rescue = ast::cast_tree_nonnull(tree); + updateEnclosingScope(tree, rescue.body.loc()); + updateEnclosingScope(tree, rescue.else_.loc()); + updateEnclosingScope(tree, rescue.ensure.loc()); + } + + void preTransformRescueCase(core::Context ctx, const ast::ExpressionPtr &tree) { + auto &rescueCase = ast::cast_tree_nonnull(tree); + updateEnclosingScope(tree, rescueCase.body.loc()); + skipLoc(rescueCase.var.loc()); + for (auto &exception : rescueCase.exceptions) { + skipLoc(exception.loc()); + } + } + + void preTransformWhile(core::Context ctx, const ast::ExpressionPtr &tree) { + auto &while_ = ast::cast_tree_nonnull(tree); + updateEnclosingScope(tree, while_.body.loc()); + } +}; + +vector> VariableExtractor::getEdits(LSPTypecheckerDelegate &typechecker, + const LSPConfiguration &config, + const core::Loc selectionLoc) { + auto file = selectionLoc.file(); + const auto &gs = typechecker.state(); + + ExtractVariableWalk extractVariableWalk(selectionLoc); + auto desugaredTree = typechecker.getDesugared(file); + core::Context ctx(gs, core::Symbols::root(), file); + ast::TreeWalk::apply(ctx, extractVariableWalk, desugaredTree); + + if (!extractVariableWalk.foundExactMatch()) { + return {}; + } + + auto locOffsets = selectionLoc.offsets(); + auto whereToInsert = core::LocOffsets::none(); + auto enclosingScope = extractVariableWalk.enclosingScope; + // For all cases except InsSeq and ClassDef, extractVariableWalk.enclosingScopeLoc should be + // the same as what we're pulling out from the ExpressionPtr, but we'll just get it directly + // for consistency. + // The ENFORCE(!ast::isa_tree(...)) are there check that the enclosingScope returned + // by the TreeWalk doesn't contain a further InsSeq, because the preTransformInsSeq should have + // matched on that (if it contains the selectionLoc). + if (auto insSeq = ast::cast_tree(*enclosingScope)) { + for (auto &stat : insSeq->stats) { + if (stat.loc().contains(locOffsets)) { + ENFORCE(!ast::isa_tree(stat)); + whereToInsert = stat.loc(); + break; + } + } + if (insSeq->expr.loc().contains(locOffsets)) { + ENFORCE(!ast::isa_tree(insSeq->expr)); + whereToInsert = insSeq->expr.loc(); + } + } else if (auto classDef = ast::cast_tree(*enclosingScope)) { + if (classDef->rhs.size() == 0) { + ENFORCE(false); + } else { + for (auto &stat : classDef->rhs) { + if (stat.loc().contains(locOffsets)) { + ENFORCE(!ast::isa_tree(stat)); + whereToInsert = stat.loc(); + break; + } + } + } + } else if (auto block = ast::cast_tree(*enclosingScope)) { + ENFORCE(!ast::isa_tree(block->body)); + ENFORCE(block->body.loc() == extractVariableWalk.enclosingScopeLoc, + "loc on node doesn't match the loc found in the tree walk"); + whereToInsert = block->body.loc(); + } else if (auto methodDef = ast::cast_tree(*enclosingScope)) { + // TODO(neil): this will fail for endless methods + ENFORCE(!ast::isa_tree(methodDef->rhs)); + ENFORCE(methodDef->rhs.loc() == extractVariableWalk.enclosingScopeLoc, + "loc on node doesn't match the loc found in the tree walk"); + whereToInsert = methodDef->rhs.loc(); + } else if (auto if_ = ast::cast_tree(*enclosingScope)) { + if (if_->thenp.loc().exists() && if_->thenp.loc().contains(locOffsets)) { + ENFORCE(!ast::isa_tree(if_->thenp)); + whereToInsert = if_->thenp.loc(); + ENFORCE(if_->thenp.loc() == extractVariableWalk.enclosingScopeLoc, + "loc on node doesn't match the loc found in the tree walk"); + } else { + ENFORCE(!ast::isa_tree(if_->elsep)); + whereToInsert = if_->elsep.loc(); + ENFORCE(if_->elsep.loc() == extractVariableWalk.enclosingScopeLoc, + "loc on node doesn't match the loc found in the tree walk"); + } + } else if (auto rescue = ast::cast_tree(*enclosingScope)) { + if (rescue->body.loc().exists() && rescue->body.loc().contains(locOffsets)) { + ENFORCE(!ast::isa_tree(rescue->body)); + ENFORCE(rescue->body.loc() == extractVariableWalk.enclosingScopeLoc, + "loc on node doesn't match the loc found in the tree walk"); + whereToInsert = rescue->body.loc(); + } else if (rescue->else_.loc().exists() && rescue->else_.loc().contains(locOffsets)) { + ENFORCE(!ast::isa_tree(rescue->else_)); + ENFORCE(rescue->else_.loc() == extractVariableWalk.enclosingScopeLoc, + "loc on node doesn't match the loc found in the tree walk"); + whereToInsert = rescue->else_.loc(); + } else if (rescue->ensure.loc().exists() && rescue->ensure.loc().contains(locOffsets)) { + ENFORCE(!ast::isa_tree(rescue->ensure)); + ENFORCE(rescue->ensure.loc() == extractVariableWalk.enclosingScopeLoc, + "loc on node doesn't match the loc found in the tree walk"); + whereToInsert = rescue->ensure.loc(); + } else { + ENFORCE(false) + } + } else if (auto rescueCase = ast::cast_tree(*enclosingScope)) { + ENFORCE(!ast::isa_tree(rescueCase->body)); + ENFORCE(rescueCase->body.loc() == extractVariableWalk.enclosingScopeLoc, + "loc on node doesn't match the loc found in the tree walk"); + whereToInsert = rescueCase->body.loc(); + } else if (auto while_ = ast::cast_tree(*enclosingScope)) { + ENFORCE(!ast::isa_tree(while_->body)); + ENFORCE(while_->body.loc() == extractVariableWalk.enclosingScopeLoc, + "loc on node doesn't match the loc found in the tree walk"); + whereToInsert = while_->body.loc(); + } else { + ENFORCE(false); + } + ENFORCE(whereToInsert.exists()); + + auto whereToInsertLoc = core::Loc(file, whereToInsert.copyWithZeroLength()); + auto [startOfLine, numSpaces] = whereToInsertLoc.findStartOfLine(gs); + + auto trailing = whereToInsertLoc.beginPos() == startOfLine.beginPos() + // If we're inserting at the start of the line (ignoring whitespace), + // let's put the declaration on a new line (above the current one) instead. + ? fmt::format("\n{}", string(numSpaces, ' ')) + : "; "; + + vector> edits; + + if (whereToInsertLoc.endPos() == selectionLoc.beginPos()) { + // if insertion point is touching the selection, let's merge the 2 edits into one, + // to prevent an "overlapping" edit. + whereToInsertLoc = whereToInsertLoc.join(selectionLoc); + edits.emplace_back(make_unique( + Range::fromLoc(gs, whereToInsertLoc), + fmt::format("newVariable = {}{}newVariable", selectionLoc.source(gs).value(), trailing))); + } else { + edits.emplace_back( + make_unique(Range::fromLoc(gs, whereToInsertLoc), + fmt::format("newVariable = {}{}", selectionLoc.source(gs).value(), trailing))); + auto selectionRange = Range::fromLoc(gs, selectionLoc); + edits.emplace_back(make_unique(std::move(selectionRange), "newVariable")); + } + auto docEdit = make_unique( + make_unique(config.fileRef2Uri(gs, file), JSONNullObject()), move(edits)); + + vector> res; + res.emplace_back(move(docEdit)); + return res; +} +} // namespace sorbet::realmain::lsp diff --git a/main/lsp/ExtractVariable.h b/main/lsp/ExtractVariable.h new file mode 100644 index 0000000000..0d944e3f45 --- /dev/null +++ b/main/lsp/ExtractVariable.h @@ -0,0 +1,18 @@ +#ifndef SORBET_EXTRACT_VARIABLE_H +#define SORBET_EXTRACT_VARIABLE_H + +#include "main/lsp/LSPConfiguration.h" +#include "main/lsp/LSPTypechecker.h" +#include "main/lsp/json_types.h" + +namespace sorbet::realmain::lsp { + +class VariableExtractor { +public: + static std::vector> + getEdits(LSPTypecheckerDelegate &typechecker, const LSPConfiguration &config, const core::Loc selectionLoc); +}; + +} // namespace sorbet::realmain::lsp + +#endif diff --git a/main/lsp/FieldFinder.cc b/main/lsp/FieldFinder.cc index 87ec1a2ce3..0636deaa2c 100644 --- a/main/lsp/FieldFinder.cc +++ b/main/lsp/FieldFinder.cc @@ -11,15 +11,13 @@ FieldFinder::FieldFinder(core::ClassOrModuleRef target, ast::UnresolvedIdent::Ki ENFORCE(queryKind != ast::UnresolvedIdent::Kind::Local); } -void FieldFinder::postTransformUnresolvedIdent(core::Context ctx, ast::ExpressionPtr &tree) { +void FieldFinder::postTransformUnresolvedIdent(core::Context ctx, const ast::UnresolvedIdent &ident) { ENFORCE(!this->classStack.empty()); if (this->classStack.back() != this->targetClass) { return; } - auto &ident = ast::cast_tree_nonnull(tree); - if (ident.kind != this->queryKind) { return; } @@ -31,16 +29,14 @@ void FieldFinder::postTransformUnresolvedIdent(core::Context ctx, ast::Expressio this->result_.emplace_back(ident.name); } -void FieldFinder::preTransformClassDef(core::Context ctx, ast::ExpressionPtr &tree) { - auto &classDef = ast::cast_tree_nonnull(tree); - +void FieldFinder::preTransformClassDef(core::Context ctx, const ast::ClassDef &classDef) { ENFORCE(classDef.symbol.exists()); ENFORCE(classDef.symbol != core::Symbols::todo()); this->classStack.push_back(classDef.symbol); } -void FieldFinder::postTransformClassDef(core::Context ctx, ast::ExpressionPtr &tree) { +void FieldFinder::postTransformClassDef(core::Context ctx, const ast::ClassDef &classDef) { this->classStack.pop_back(); } diff --git a/main/lsp/FieldFinder.h b/main/lsp/FieldFinder.h index b2d3370278..22788f1d0e 100644 --- a/main/lsp/FieldFinder.h +++ b/main/lsp/FieldFinder.h @@ -18,9 +18,9 @@ class FieldFinder { public: FieldFinder(core::ClassOrModuleRef target, ast::UnresolvedIdent::Kind queryKind); - void postTransformUnresolvedIdent(core::Context ctx, ast::ExpressionPtr &ident); - void preTransformClassDef(core::Context ctx, ast::ExpressionPtr &classDef); - void postTransformClassDef(core::Context ctx, ast::ExpressionPtr &classDef); + void postTransformUnresolvedIdent(core::Context ctx, const ast::UnresolvedIdent &ident); + void preTransformClassDef(core::Context ctx, const ast::ClassDef &classDef); + void postTransformClassDef(core::Context ctx, const ast::ClassDef &classDef); const std::vector &result() const; }; diff --git a/main/lsp/LSPConfiguration.cc b/main/lsp/LSPConfiguration.cc index 0575004dc0..a157803108 100644 --- a/main/lsp/LSPConfiguration.cc +++ b/main/lsp/LSPConfiguration.cc @@ -54,6 +54,24 @@ void LSPConfiguration::assertHasClientConfig() const { } } +core::TrackUntyped LSPClientConfiguration::parseEnableHighlightUntyped(const SorbetInitializationOptions &options, + core::TrackUntyped defaultIfUnset) { + if (!options.highlightUntyped.has_value()) { + return defaultIfUnset; + } else if (auto *enabled = get_if(&options.highlightUntyped.value())) { + return *enabled ? core::TrackUntyped::Everywhere : core::TrackUntyped::Nowhere; + } else { + auto &highlightUntyped = get(options.highlightUntyped.value()); + if (highlightUntyped == "" || highlightUntyped == "everywhere") { + return core::TrackUntyped::Everywhere; + } else if (highlightUntyped == "everywhere-but-tests") { + return core::TrackUntyped::EverywhereButTests; + } else { + return core::TrackUntyped::Nowhere; + } + } +} + LSPClientConfiguration::LSPClientConfiguration(const InitializeParams ¶ms) { // Note: Default values for fields are set in class definition. if (auto rootUriString = get_if(¶ms.rootUri)) { @@ -103,7 +121,8 @@ LSPClientConfiguration::LSPClientConfiguration(const InitializeParams ¶ms) { enableOperationNotifications = initOptions->supportsOperationNotifications.value_or(false); enableTypecheckInfo = initOptions->enableTypecheckInfo.value_or(false); enableSorbetURIs = initOptions->supportsSorbetURIs.value_or(false); - enableHighlightUntyped = initOptions->highlightUntyped.value_or(false); + enableHighlightUntyped = parseEnableHighlightUntyped(*initOptions, core::TrackUntyped::Nowhere); + enableTypedFalseCompletionNudges = initOptions->enableTypedFalseCompletionNudges.value_or(true); } } diff --git a/main/lsp/LSPConfiguration.h b/main/lsp/LSPConfiguration.h index 683c0e84b6..5ac45c6674 100644 --- a/main/lsp/LSPConfiguration.h +++ b/main/lsp/LSPConfiguration.h @@ -9,6 +9,7 @@ namespace sorbet::realmain::lsp { class LSPOutput; class InitializeParams; +class SorbetInitializationOptions; class Position; class Location; @@ -34,8 +35,11 @@ class LSPClientConfiguration final { /** If true, then LSP sends metadata to the client every time it typechecks files. Used in tests. */ bool enableTypecheckInfo = false; - /** If true, then LSP outputs a warning for untyped values */ - bool enableHighlightUntyped = false; + /** Where LSP should output an information diagnostic for untyped values */ + core::TrackUntyped enableHighlightUntyped = core::TrackUntyped::Nowhere; + + /** If false, nudges in `typed: false` files are disabled */ + bool enableTypedFalseCompletionNudges = true; /** * Whether or not the active client has support for snippets in CompletionItems. @@ -53,6 +57,9 @@ class LSPClientConfiguration final { bool clientCodeActionDataSupport = false; LSPClientConfiguration(const InitializeParams &initializeParams); + + static core::TrackUntyped parseEnableHighlightUntyped(const SorbetInitializationOptions &options, + core::TrackUntyped defaultIfUnset); }; /** diff --git a/main/lsp/LSPIndexer.cc b/main/lsp/LSPIndexer.cc index 2cd6654f87..eb65e825ea 100644 --- a/main/lsp/LSPIndexer.cc +++ b/main/lsp/LSPIndexer.cc @@ -75,6 +75,9 @@ void LSPIndexer::computeFileHashes(const vector> &files) computeFileHashes(files, *emptyWorkers); } +// This function was previously called canTakeFastPath, but we changed it in ancitipation of adding +// incremental mode(s) that lied between the original fast and slow path. Leaving this comment here +// because old habits die hard and I still can only remember the name "canTakeFastPath" TypecheckingPath LSPIndexer::getTypecheckingPathInternal(const vector> &changedFiles, const UnorderedMap> &evictedFiles) const { @@ -304,8 +307,8 @@ LSPFileUpdates LSPIndexer::commitEdit(SorbetWorkspaceEditParams &edit, WorkerPoo // which one it will be. initialGS->errorQueue = make_shared( initialGS->errorQueue->logger, initialGS->errorQueue->tracer, make_shared()); - auto trees = hashing::Hashing::indexAndComputeFileHashes(initialGS, config->opts, *config->logger, frefs, - workers, kvstore); + auto trees = hashing::Hashing::indexAndComputeFileHashes(initialGS, config->opts, *config->logger, + absl::Span(frefs), workers, kvstore); update.updatedFileIndexes.resize(trees.size()); for (auto &ast : trees) { const int i = fileToPos[ast.file]; @@ -395,4 +398,9 @@ const core::File &LSPIndexer::getFile(core::FileRef fref) const { return fref.data(*initialGS); } +void LSPIndexer::updateGsFromOptions(const DidChangeConfigurationParams &options) const { + initialGS->trackUntyped = + LSPClientConfiguration::parseEnableHighlightUntyped(*options.settings, initialGS->trackUntyped); +} + } // namespace sorbet::realmain::lsp diff --git a/main/lsp/LSPIndexer.h b/main/lsp/LSPIndexer.h index c58c83baf8..4665e2ddb9 100644 --- a/main/lsp/LSPIndexer.h +++ b/main/lsp/LSPIndexer.h @@ -103,6 +103,8 @@ class LSPIndexer final { * out in a context that's not the InitializedTask's index function. */ void transferInitializeState(InitializedTask &task); + + void updateGsFromOptions(const DidChangeConfigurationParams &options) const; }; } // namespace sorbet::realmain::lsp diff --git a/main/lsp/LSPInput.h b/main/lsp/LSPInput.h index d578a25da0..6132c5f4be 100644 --- a/main/lsp/LSPInput.h +++ b/main/lsp/LSPInput.h @@ -58,8 +58,8 @@ class LSPFDInput final : public LSPInput { class LSPProgrammaticInput final : public LSPInput { absl::Mutex mtx; // Contains all available messages for processing. - std::deque> available GUARDED_BY(mtx); - bool closed GUARDED_BY(mtx) = false; + std::deque> available ABSL_GUARDED_BY(mtx); + bool closed ABSL_GUARDED_BY(mtx) = false; public: LSPProgrammaticInput() = default; diff --git a/main/lsp/LSPLoop.cc b/main/lsp/LSPLoop.cc index fd8685ca0c..7d210cfa1b 100644 --- a/main/lsp/LSPLoop.cc +++ b/main/lsp/LSPLoop.cc @@ -105,79 +105,6 @@ class NotifyNotificationOnDestruction { } }; -CounterState mergeCounters(CounterState counters) { - if (!counters.hasNullCounters()) { - counterConsume(move(counters)); - } - return getAndClearThreadCounters(); -} - -void tagNewRequest(spdlog::logger &logger, LSPMessage &msg) { - msg.latencyTimer = make_unique(logger, "task_latency", - initializer_list{50, 100, 250, 500, 1000, 1500, 2000, 2500, 5000, 10000, - 15000, 20000, 25000, 30000, 35000, 40000}); -} - -class LSPWatchmanProcess final : public watchman::WatchmanProcess { - MessageQueueState &messageQueue; - absl::Mutex &messageQueueMutex; - absl::Notification &initializedNotification; - const std::shared_ptr config; - - void enqueueNotification(std::unique_ptr notification) { - auto msg = make_unique(move(notification)); - // Don't start enqueueing requests until LSP is initialized. - initializedNotification.WaitForNotification(); - { - absl::MutexLock lck(&messageQueueMutex); - tagNewRequest(*logger, *msg); - messageQueue.counters = mergeCounters(move(messageQueue.counters)); - messageQueue.pendingRequests.push_back(move(msg)); - } - } - -public: - LSPWatchmanProcess(std::shared_ptr logger, std::string_view watchmanPath, - std::string_view workSpace, std::vector extensions, MessageQueueState &messageQueue, - absl::Mutex &messageQueueMutex, absl::Notification &initializedNotification, - std::shared_ptr config) - : WatchmanProcess(std::move(logger), watchmanPath, workSpace, std::move(extensions)), - messageQueue(messageQueue), messageQueueMutex(messageQueueMutex), - initializedNotification(initializedNotification), config(std::move(config)) {} - - virtual void processQueryResponse(std::unique_ptr response) { - auto notifMsg = make_unique("2.0", LSPMethod::SorbetWatchmanFileChange, move(response)); - enqueueNotification(move(notifMsg)); - } - - virtual void processStateEnter(std::unique_ptr stateEnter) { - auto notification = - make_unique("2.0", LSPMethod::SorbetWatchmanStateEnter, move(stateEnter)); - enqueueNotification(move(notification)); - } - - virtual void processStateLeave(std::unique_ptr stateLeave) { - auto notification = - make_unique("2.0", LSPMethod::SorbetWatchmanStateLeave, move(stateLeave)); - enqueueNotification(move(notification)); - } - - virtual void processExit(int watchmanExitCode, const std::optional &msg) { - { - absl::MutexLock lck(&messageQueueMutex); - if (!messageQueue.terminate) { - messageQueue.terminate = true; - messageQueue.errorCode = watchmanExitCode; - if (watchmanExitCode != 0 && msg.has_value()) { - auto params = make_unique(MessageType::Error, msg.value()); - config->output->write(make_unique( - make_unique("2.0", LSPMethod::WindowShowMessage, move(params)))); - } - } - logger->debug("Watchman terminating"); - } - } -}; } // namespace void LSPLoop::processRequest(const string &json) { @@ -292,9 +219,9 @@ optional> LSPLoop::runLSP(shared_ptr inp throw EarlyReturnWithCode(1); } - watchmanProcess = make_unique(logger, opts.watchmanPath, opts.rawInputDirNames.at(0), - vector({"rb", "rbi"}), messageQueue, - messageQueueMutex, initializedNotification, this->config); + watchmanProcess = make_unique( + logger, opts.watchmanPath, opts.rawInputDirNames.at(0), vector({"rb", "rbi"}), messageQueue, + messageQueueMutex, initializedNotification, this->config); } auto readerThread = @@ -313,7 +240,7 @@ optional> LSPLoop::runLSP(shared_ptr inp absl::MutexLock lck(&messageQueueMutex); // guards guardedState. auto &msg = readResult.message; if (msg) { - tagNewRequest(*logger, *msg); + msg->tagNewRequest(*logger); messageQueue.counters = mergeCounters(move(messageQueue.counters)); messageQueue.pendingRequests.push_back(move(msg)); // Reset span now that we've found a request. diff --git a/main/lsp/LSPLoop.h b/main/lsp/LSPLoop.h index 1e4640c070..0ad5821117 100644 --- a/main/lsp/LSPLoop.h +++ b/main/lsp/LSPLoop.h @@ -94,11 +94,7 @@ std::optional findDocumentation(std::string_view sourceCode, int be bool hideSymbol(const core::GlobalState &gs, core::SymbolRef sym); std::unique_ptr formatRubyMarkup(MarkupKind markupKind, std::string_view rubyMarkup, std::optional explanation); -std::string prettyTypeForMethod(const core::GlobalState &gs, core::MethodRef method, const core::TypePtr &receiver, - const core::TypePtr &retType, const core::TypeConstraint *constraint); std::string prettyTypeForConstant(const core::GlobalState &gs, core::SymbolRef constant); -core::TypePtr getResultType(const core::GlobalState &gs, const core::TypePtr &type, core::SymbolRef inWhat, - core::TypePtr receiver, const core::TypeConstraint *constr); SymbolKind symbolRef2SymbolKind(const core::GlobalState &gs, core::SymbolRef sym, bool isAttrBestEffortUIOnly); // Returns all subclasses of ClassOrModuleRef (including itself) @@ -115,5 +111,13 @@ SymbolKind symbolRef2SymbolKind(const core::GlobalState &gs, core::SymbolRef sym std::vector getSubclassesSlow(const core::GlobalState &gs, core::ClassOrModuleRef sym, bool includeSelf); +std::unique_ptr +skipLiteralIfMethodDef(std::vector> &queryResponses); + +// prop/const/attr setters are bogged down with a bunch of extra Ident and Literal query +// responses, which don't make sense to use find all references on. +std::unique_ptr +getQueryResponseForFindAllReferences(std::vector> &queryResponses); + } // namespace sorbet::realmain::lsp #endif // RUBY_TYPER_LSPLOOP_H diff --git a/main/lsp/LSPMessage.cc b/main/lsp/LSPMessage.cc index c2b34c1536..1acedcf031 100644 --- a/main/lsp/LSPMessage.cc +++ b/main/lsp/LSPMessage.cc @@ -129,6 +129,12 @@ LSPMessage::LSPMessage(std::string_view json) : LSPMessage::LSPMessage(fromJSON( LSPMessage::~LSPMessage() = default; +void LSPMessage::tagNewRequest(spdlog::logger &logger) { + this->latencyTimer = make_unique(logger, "task_latency", + initializer_list{50, 100, 250, 500, 1000, 1500, 2000, 2500, 5000, + 10000, 15000, 20000, 25000, 30000, 35000, 40000}); +} + optional LSPMessage::id() const { if (isRequest()) { return asRequest().id; diff --git a/main/lsp/LSPMessage.h b/main/lsp/LSPMessage.h index 26cbf4ad35..0363737729 100644 --- a/main/lsp/LSPMessage.h +++ b/main/lsp/LSPMessage.h @@ -66,6 +66,11 @@ class LSPMessage final { */ std::unique_ptr latencyTimer; + /** + * Initializes the `latencyTimer` of this message with a new Timer + */ + void tagNewRequest(spdlog::logger &logger); + /** * Returns an ID if the message has one. Otherwise, returns nullopt. */ diff --git a/main/lsp/LSPOutput.h b/main/lsp/LSPOutput.h index 54eb810dd6..8665522aa4 100644 --- a/main/lsp/LSPOutput.h +++ b/main/lsp/LSPOutput.h @@ -23,7 +23,7 @@ class LSPOutput { absl::Mutex mtx; // Implementation-specific write implementation. Will be called from multiple threads, but invocations will never // interleave as method is protected by a mutex. - virtual void rawWrite(std::unique_ptr msg) EXCLUSIVE_LOCKS_REQUIRED(mtx) = 0; + virtual void rawWrite(std::unique_ptr msg) ABSL_EXCLUSIVE_LOCKS_REQUIRED(mtx) = 0; public: LSPOutput() = default; @@ -44,7 +44,7 @@ class LSPStdout final : public LSPOutput { std::shared_ptr logger; protected: - void rawWrite(std::unique_ptr msg) override EXCLUSIVE_LOCKS_REQUIRED(mtx); + void rawWrite(std::unique_ptr msg) override ABSL_EXCLUSIVE_LOCKS_REQUIRED(mtx); public: LSPStdout(std::shared_ptr &logger); @@ -55,10 +55,10 @@ class LSPStdout final : public LSPOutput { * Used in LSPWrapper and in tests. */ class LSPOutputToVector final : public LSPOutput { - std::deque> output GUARDED_BY(mtx); + std::deque> output ABSL_GUARDED_BY(mtx); protected: - void rawWrite(std::unique_ptr msg) override EXCLUSIVE_LOCKS_REQUIRED(mtx); + void rawWrite(std::unique_ptr msg) override ABSL_EXCLUSIVE_LOCKS_REQUIRED(mtx); public: LSPOutputToVector() = default; diff --git a/main/lsp/LSPPreprocessor.cc b/main/lsp/LSPPreprocessor.cc index 080d93e320..7c86de3951 100644 --- a/main/lsp/LSPPreprocessor.cc +++ b/main/lsp/LSPPreprocessor.cc @@ -36,13 +36,6 @@ class TerminateOnDestruction final { } }; -CounterState mergeCounters(CounterState counters) { - if (!counters.hasNullCounters()) { - counterConsume(move(counters)); - } - return getAndClearThreadCounters(); -} - } // namespace bool TaskQueue::isTerminated() const { @@ -229,6 +222,11 @@ unique_ptr LSPPreprocessor::getTaskForMessage(LSPMessage &msg) { return make_unique(*config); case LSPMethod::SorbetError: return make_unique(*config, move(get>(rawParams))); + case LSPMethod::WorkspaceDidChangeConfiguration: { + return make_unique( + *config, move(get>(rawParams)), this->openFilePaths(), + nextVersion++); + } default: return make_unique( *config, make_unique( @@ -494,7 +492,7 @@ LSPPreprocessor::canonicalizeEdits(uint32_t v, unique_ptr for (auto &file : queryResponse->files) { // Don't append rootPath if it is empty. string localPath = !config->rootPath.empty() ? absl::StrCat(config->rootPath, "/", file) : file; - // Editor contents supercede file system updates. + // Editor contents supersede file system updates. if (!config->isFileIgnored(localPath) && !openFiles.contains(localPath)) { auto fileType = core::File::Type::Normal; auto fileContents = readFile(localPath, *config->opts.fs); @@ -503,5 +501,13 @@ LSPPreprocessor::canonicalizeEdits(uint32_t v, unique_ptr } return edit; } +std::vector LSPPreprocessor::openFilePaths() const { + std::vector paths; + paths.reserve(openFiles.size()); + for (auto const &[path, file] : openFiles) { + paths.emplace_back(path); + } + return paths; +} } // namespace sorbet::realmain::lsp diff --git a/main/lsp/LSPPreprocessor.h b/main/lsp/LSPPreprocessor.h index 08b099f46d..0d6bf47950 100644 --- a/main/lsp/LSPPreprocessor.h +++ b/main/lsp/LSPPreprocessor.h @@ -4,6 +4,7 @@ #include "absl/synchronization/mutex.h" #include "main/lsp/LSPConfiguration.h" #include "main/lsp/LSPMessage.h" +#include "main/lsp/MessageQueueState.h" #include namespace sorbet::realmain::lsp { @@ -16,36 +17,16 @@ class DidOpenTextDocumentParams; class WatchmanQueryResponse; class CancelParams; -struct MessageQueueState { - std::deque> pendingRequests; - bool terminate = false; - int errorCode = 0; - // Counters collected from other threads. - CounterState counters; - - class NotifyOnDestruction { - absl::Mutex &mutex; - bool &flag; - - public: - NotifyOnDestruction(MessageQueueState &state, absl::Mutex &mutex) : mutex(mutex), flag(state.terminate){}; - ~NotifyOnDestruction() { - absl::MutexLock lck(&mutex); - flag = true; - } - }; -}; - class TaskQueue final { absl::Mutex stateMutex; - std::deque> pendingTasks GUARDED_BY(stateMutex); - bool terminated GUARDED_BY(stateMutex) = false; - bool paused GUARDED_BY(stateMutex) = false; - int errorCode GUARDED_BY(stateMutex) = 0; + std::deque> pendingTasks ABSL_GUARDED_BY(stateMutex); + bool terminated ABSL_GUARDED_BY(stateMutex) = false; + bool paused ABSL_GUARDED_BY(stateMutex) = false; + int errorCode ABSL_GUARDED_BY(stateMutex) = 0; // Counters collected from preprocessor thread - CounterState counters GUARDED_BY(stateMutex); + CounterState counters ABSL_GUARDED_BY(stateMutex); public: TaskQueue() = default; @@ -105,7 +86,7 @@ class LSPPreprocessor final { * Example: (E = edit, D = delayable non-edit, M = arbitrary non-edit) * {[M1][E1][E2][D1][E3]} => {[M1][E1-3][D1]} */ - void mergeFileChanges() EXCLUSIVE_LOCKS_REQUIRED(taskQueue->getMutex()); + void mergeFileChanges() ABSL_EXCLUSIVE_LOCKS_REQUIRED(taskQueue->getMutex()); /* The following methods convert edits into SorbetWorkspaceEditParams. */ @@ -128,6 +109,8 @@ class LSPPreprocessor final { std::unique_ptr getTaskForMessage(LSPMessage &msg); + std::vector openFilePaths() const; + public: LSPPreprocessor(std::shared_ptr config, std::shared_ptr taskQueue, uint32_t initialVersion = 0); diff --git a/main/lsp/LSPQuery.cc b/main/lsp/LSPQuery.cc index 86cb4296c0..6071d0e575 100644 --- a/main/lsp/LSPQuery.cc +++ b/main/lsp/LSPQuery.cc @@ -53,7 +53,7 @@ LSPQuery::filterAndDedup(const core::GlobalState &gs, } LSPQueryResult LSPQuery::byLoc(const LSPConfiguration &config, LSPTypecheckerDelegate &typechecker, string_view uri, - const Position &pos, LSPMethod forMethod, bool errorIfFileIsUntyped) { + const Position &pos, LSPMethod forMethod, bool emptyResultIfFileIsUntyped) { Timer timeit(config.logger, "setupLSPQueryByLoc"); const core::GlobalState &gs = typechecker.state(); auto fref = config.uri2FileRef(gs, uri); @@ -72,7 +72,7 @@ LSPQueryResult LSPQuery::byLoc(const LSPConfiguration &config, LSPTypecheckerDel return LSPQueryResult{{}, move(error)}; } - if (errorIfFileIsUntyped && fref.data(gs).strictLevel < core::StrictLevel::True) { + if (emptyResultIfFileIsUntyped && fref.data(gs).strictLevel < core::StrictLevel::True) { config.logger->info("Ignoring request on untyped file `{}`", uri); // Act as if the query returned no results. return LSPQueryResult{{}, nullptr}; @@ -97,7 +97,7 @@ LSPQueryResult LSPQuery::LSPQuery::bySymbolInFiles(const LSPConfiguration &confi } LSPQueryResult LSPQuery::bySymbol(const LSPConfiguration &config, LSPTypecheckerDelegate &typechecker, - core::SymbolRef symbol, core::NameRef pkgName) { + core::SymbolRef symbol, core::packages::MangledName pkgName) { Timer timeit(config.logger, "setupLSPQueryBySymbol"); ENFORCE(symbol.exists()); vector frefs; diff --git a/main/lsp/LSPQuery.h b/main/lsp/LSPQuery.h index 2aa08425ac..303d89c3c5 100644 --- a/main/lsp/LSPQuery.h +++ b/main/lsp/LSPQuery.h @@ -13,11 +13,12 @@ class LSPQuery { static LSPQueryResult byLoc(const LSPConfiguration &config, LSPTypecheckerDelegate &typechecker, std::string_view uri, const Position &pos, LSPMethod forMethod, - bool errorIfFileIsUntyped = true); + bool emptyResultIfFileIsUntyped = true); static LSPQueryResult bySymbolInFiles(const LSPConfiguration &config, LSPTypecheckerDelegate &typechecker, core::SymbolRef symbol, std::vector frefs); static LSPQueryResult bySymbol(const LSPConfiguration &config, LSPTypecheckerDelegate &typechecker, - core::SymbolRef symbol, core::NameRef pkgName = core::NameRef::noName()); + core::SymbolRef symbol, + core::packages::MangledName pkgName = core::packages::MangledName()); }; } // namespace sorbet::realmain::lsp diff --git a/main/lsp/LSPTask.cc b/main/lsp/LSPTask.cc index 136b9c5606..5591009e52 100644 --- a/main/lsp/LSPTask.cc +++ b/main/lsp/LSPTask.cc @@ -198,6 +198,8 @@ ConstExprStr LSPTask::methodString() const { return "textDocument.typeDefinition"; case LSPMethod::WorkspaceSymbol: return "workspace.symbol"; + case LSPMethod::WorkspaceDidChangeConfiguration: + return "worksapce.didChangeConfiguration"; case LSPMethod::TextDocumentImplementation: return "textDocument.implementation"; case LSPMethod::CodeActionResolve: @@ -299,7 +301,7 @@ LSPTask::getReferencesToSymbol(LSPTypecheckerDelegate &typechecker, core::Symbol } vector> -LSPTask::getReferencesToSymbolInPackage(LSPTypecheckerDelegate &typechecker, core::NameRef packageName, +LSPTask::getReferencesToSymbolInPackage(LSPTypecheckerDelegate &typechecker, core::packages::MangledName packageName, core::SymbolRef symbol, vector> &&priorRefs) const { if (symbol.exists()) { @@ -451,10 +453,6 @@ AccessorInfo LSPTask::getAccessorInfo(const core::GlobalState &gs, core::SymbolR return info; } info.fieldSymbol = gs.lookupFieldSymbol(ownerCls, fieldName); - if (!info.fieldSymbol.exists()) { - // field symbol does not exist, so `symbol` must not be an accessor. - return info; - } } if (!info.readerSymbol.exists()) { diff --git a/main/lsp/LSPTask.h b/main/lsp/LSPTask.h index 498aec0f4e..005f13a2fd 100644 --- a/main/lsp/LSPTask.h +++ b/main/lsp/LSPTask.h @@ -1,15 +1,12 @@ #ifndef RUBY_TYPER_LSPTASK_H #define RUBY_TYPER_LSPTASK_H +#include "absl/synchronization/notification.h" #include "main/lsp/AbstractRewriter.h" #include "main/lsp/LSPMessage.h" #include "main/lsp/LSPTypechecker.h" #include "main/lsp/json_types.h" -namespace absl { -class Notification; -} - namespace sorbet::realmain::lsp { class LSPIndexer; class LSPPreprocessor; @@ -41,7 +38,7 @@ class LSPTask { std::vector> &&priorRefs = {}) const; std::vector> - getReferencesToSymbolInPackage(LSPTypecheckerDelegate &typechecker, core::NameRef packageName, + getReferencesToSymbolInPackage(LSPTypecheckerDelegate &typechecker, core::packages::MangledName packageName, core::SymbolRef symbol, std::vector> &&priorRefs = {}) const; @@ -111,7 +108,7 @@ class LSPTask { // indexer. The default implementation returns RUN. virtual Phase finalPhase() const; - // Some tasks, like request cancelations, need to interface with the preprocessor. The default implementation is + // Some tasks, like request cancellations, need to interface with the preprocessor. The default implementation is // a no-op. Is only ever invoked from the preprocessor thread. virtual void preprocess(LSPPreprocessor &preprocessor); diff --git a/main/lsp/LSPTypechecker.cc b/main/lsp/LSPTypechecker.cc index 6684e47149..8ae323a91f 100644 --- a/main/lsp/LSPTypechecker.cc +++ b/main/lsp/LSPTypechecker.cc @@ -90,8 +90,8 @@ void LSPTypechecker::initialize(TaskQueue &queue, std::unique_ptropts.inputFileNames); indexed.resize(initialGS->filesUsed()); - auto asts = hashing::Hashing::indexAndComputeFileHashes(initialGS, config->opts, *config->logger, - inputFiles, workers, ownedKvstore); + auto asts = hashing::Hashing::indexAndComputeFileHashes( + initialGS, config->opts, *config->logger, absl::Span(inputFiles), workers, ownedKvstore); // asts are in fref order, but we (currently) don't index and compute file hashes for payload files, so // vector index != FileRef ID. Fix that by slotting them into `indexed`. for (auto &ast : asts) { @@ -311,10 +311,10 @@ vector LSPTypechecker::runFastPath(LSPFileUpdates &updates, Worke } ENFORCE(gs->lspQuery.isEmpty()); - auto resolved = - shouldRunIncrementalNamer - ? pipeline::incrementalResolve(*gs, move(updatedIndexed), std::move(oldFoundHashesForFiles), config->opts) - : pipeline::incrementalResolve(*gs, move(updatedIndexed), nullopt, config->opts); + auto resolved = shouldRunIncrementalNamer + ? pipeline::incrementalResolve(*gs, move(updatedIndexed), std::move(oldFoundHashesForFiles), + config->opts, workers) + : pipeline::incrementalResolve(*gs, move(updatedIndexed), nullopt, config->opts, workers); auto sorted = sortParsedFiles(*gs, *errorReporter, move(resolved)); const auto presorted = true; const auto cancelable = false; @@ -386,7 +386,11 @@ bool LSPTypechecker::copyIndexed(WorkerPool &workers, const UnorderedSet &i } } } - return !epochManager.wasTypecheckingCanceled(); + if (epochManager.wasTypecheckingCanceled()) { + return true; + } + fast_sort(out, [](const auto &lhs, const auto &rhs) -> bool { return lhs.file < rhs.file; }); + return epochManager.wasTypecheckingCanceled(); } bool LSPTypechecker::runSlowPath(LSPFileUpdates updates, WorkerPool &workers, @@ -437,7 +441,7 @@ bool LSPTypechecker::runSlowPath(LSPFileUpdates updates, WorkerPool &workers, // We use `gs` rather than the moved `finalGS` from this point forward. // Copy the indexes of unchanged files. - if (!copyIndexed(workers, updatedFiles, indexedCopies)) { + if (copyIndexed(workers, updatedFiles, indexedCopies)) { // Canceled. return; } @@ -460,9 +464,22 @@ bool LSPTypechecker::runSlowPath(LSPFileUpdates updates, WorkerPool &workers, } } } + + pipeline::setPackagerOptions(*gs, config->opts); + // TODO(jez) Splitting this like how the pipeline intersperses this with indexing is going + // to take more work. Punting for now. + pipeline::package(*gs, absl::Span(indexedCopies), config->opts, workers); + // Only need to compute FoundDefHashes when running to compute a FileHash auto foundHashes = nullptr; - auto maybeResolved = pipeline::resolve(gs, move(indexedCopies), config->opts, workers, foundHashes); + auto canceled = + pipeline::name(*gs, absl::Span(indexedCopies), config->opts, workers, foundHashes); + if (canceled) { + ast::ParsedFilesOrCancelled::cancel(move(indexedCopies), workers); + return; + } + + auto maybeResolved = pipeline::resolve(gs, move(indexedCopies), config->opts, workers); if (!maybeResolved.hasResult()) { return; } @@ -555,10 +572,8 @@ void LSPTypechecker::commitFileUpdates(LSPFileUpdates &updates, bool couldBeCanc indexedFinalGS.clear(); } - int i = -1; ENFORCE(updates.updatedFileIndexes.size() == updates.updatedFiles.size()); for (auto &ast : updates.updatedFileIndexes) { - i++; const int id = ast.file.id(); if (id >= indexed.size()) { indexed.resize(id + 1); @@ -601,7 +616,7 @@ void tryApplyLocalVarSaver(const core::GlobalState &gs, vector signature = sig_finder::SigFinder::findSignature(ctx, t.tree, queryLoc); } LocalVarSaver localVarSaver(ctx.locAt(t.tree.loc()), move(signature)); - ast::TreeWalk::apply(ctx, localVarSaver, t.tree); + ast::ConstTreeWalk::apply(ctx, localVarSaver, t.tree); } } @@ -633,7 +648,7 @@ LSPQueryResult LSPTypechecker::query(const core::lsp::Query &q, const std::vecto ENFORCE(gs->errorQueue->isEmpty()); ENFORCE(gs->lspQuery.isEmpty()); gs->lspQuery = q; - auto resolved = getResolved(filesForQuery); + auto resolved = getResolved(filesForQuery, workers); tryApplyDefLocSaver(*gs, resolved); tryApplyLocalVarSaver(*gs, resolved); @@ -670,6 +685,10 @@ std::vector> LSPTypechecker::retypecheck(vectordrainErrors(); } +ast::ExpressionPtr LSPTypechecker::getDesugared(core::FileRef fref) const { + return pipeline::desugarOne(config->opts, *gs, fref); +} + const ast::ParsedFile &LSPTypechecker::getIndexed(core::FileRef fref) const { const auto id = fref.id(); auto treeFinalGS = indexedFinalGS.find(id); @@ -680,7 +699,7 @@ const ast::ParsedFile &LSPTypechecker::getIndexed(core::FileRef fref) const { return indexed[id]; } -vector LSPTypechecker::getResolved(const vector &frefs) const { +vector LSPTypechecker::getResolved(const vector &frefs, WorkerPool &workers) const { ENFORCE(this_thread::get_id() == typecheckerThreadId, "Typechecker can only be used from the typechecker thread."); vector updatedIndexed; @@ -696,7 +715,7 @@ vector LSPTypechecker::getResolved(const vector // In getResolved, we want the LSP query behavior, not the file update behavior, which we get by passing nullopt. auto foundHashesForFiles = nullopt; - return pipeline::incrementalResolve(*gs, move(updatedIndexed), move(foundHashesForFiles), config->opts); + return pipeline::incrementalResolve(*gs, move(updatedIndexed), move(foundHashesForFiles), config->opts, workers); } const core::GlobalState &LSPTypechecker::state() const { @@ -715,6 +734,23 @@ void LSPTypechecker::setSlowPathBlocked(bool blocked) { slowPathBlocked = blocked; } +void LSPTypechecker::updateGsFromOptions(const DidChangeConfigurationParams &options) const { + this->gs->trackUntyped = + LSPClientConfiguration::parseEnableHighlightUntyped(*options.settings, this->gs->trackUntyped); + + if (options.settings->enableTypecheckInfo.has_value() || + options.settings->enableTypedFalseCompletionNudges.has_value() || + options.settings->supportsOperationNotifications.has_value() || + options.settings->supportsSorbetURIs.has_value()) { + auto msg = + "Currently `highlightUntyped` is the only updateable setting using the workspace/didChangeConfiguration " + "notification"; + auto params = make_unique(MessageType::Warning, msg); + config->output->write(make_unique( + make_unique("2.0", LSPMethod::WindowShowMessage, move(params)))); + } +} + LSPTypecheckerDelegate::LSPTypecheckerDelegate(TaskQueue &queue, WorkerPool &workers, LSPTypechecker &typechecker) : typechecker(typechecker), queue{queue}, workers(workers) {} @@ -753,11 +789,22 @@ const ast::ParsedFile &LSPTypecheckerDelegate::getIndexed(core::FileRef fref) co } std::vector LSPTypecheckerDelegate::getResolved(const std::vector &frefs) const { - return typechecker.getResolved(frefs); + return typechecker.getResolved(frefs, workers); +} + +ast::ExpressionPtr LSPTypecheckerDelegate::getDesugared(core::FileRef fref) const { + return typechecker.getDesugared(fref); } const core::GlobalState &LSPTypecheckerDelegate::state() const { return typechecker.state(); } +void LSPTypecheckerDelegate::updateGsFromOptions(const DidChangeConfigurationParams &options) const { + typechecker.updateGsFromOptions(options); +} + +LSPFileUpdates LSPTypecheckerDelegate::getNoopUpdate(std::vector frefs) const { + return typechecker.getNoopUpdate(frefs); +} } // namespace sorbet::realmain::lsp diff --git a/main/lsp/LSPTypechecker.h b/main/lsp/LSPTypechecker.h index 262a3d6e73..264206d2a8 100644 --- a/main/lsp/LSPTypechecker.h +++ b/main/lsp/LSPTypechecker.h @@ -22,6 +22,7 @@ namespace sorbet::realmain::lsp { class ResponseError; class InitializedTask; class TaskQueue; +class DidChangeConfigurationParams; struct LSPQueryResult { std::vector> responses; @@ -73,12 +74,6 @@ class LSPTypechecker final { /** Commits the given file updates to LSPTypechecker. Does not send diagnostics. */ void commitFileUpdates(LSPFileUpdates &updates, bool couldBeCanceled); - /** - * Get an LSPFileUpdates containing the latest versions of the given files. It's a "no-op" file update because it - * doesn't actually change anything. - */ - LSPFileUpdates getNoopUpdate(std::vector frefs) const; - /** Deep copy all entries in `indexed` that contain ASTs, except for those with IDs in the ignore set. Returns true * on success, false if the operation was canceled. */ bool copyIndexed(WorkerPool &workers, const UnorderedSet &ignore, std::vector &out) const; @@ -113,6 +108,15 @@ class LSPTypechecker final { LSPQueryResult query(const core::lsp::Query &q, const std::vector &filesForQuery, WorkerPool &workers) const; + /** + * Returns the parsed file for the given file, up to the desugar pass. + * + * This is never cached, which means that the file will be re-parsed from scratch. + * This is slower than getting the indexed tree (everything before namer), so if + * You can use the indexed tree that will be more performant. Certain IDE actions + * need particularly fine-grained fidelity in the AST (precludes rewriter). + */ + ast::ExpressionPtr getDesugared(core::FileRef fref) const; /** * Returns the parsed file for the given file, up to the index passes (does not include resolver passes). */ @@ -121,7 +125,7 @@ class LSPTypechecker final { /** * Returns the parsed files for the given files, including resolver. */ - std::vector getResolved(const std::vector &frefs) const; + std::vector getResolved(const std::vector &frefs, WorkerPool &workers) const; /** * Returns the currently active GlobalState. @@ -143,6 +147,18 @@ class LSPTypechecker final { * this flag to `false` will immediately unblock any currently blocked slow paths. */ void setSlowPathBlocked(bool blocked); + + /** + * Exposes very limited mutability to typechecker's global state in order to support the client changing + * options (such as highlighting untyped code) without doing a full restart of Sorbet. + */ + void updateGsFromOptions(const DidChangeConfigurationParams &options) const; + + /** + * Get an LSPFileUpdates containing the latest versions of the given files. It's a "no-op" file update because it + * doesn't actually change anything. + */ + LSPFileUpdates getNoopUpdate(std::vector frefs) const; }; /** @@ -181,7 +197,11 @@ class LSPTypecheckerDelegate final { LSPQueryResult query(const core::lsp::Query &q, const std::vector &filesForQuery) const; const ast::ParsedFile &getIndexed(core::FileRef fref) const; std::vector getResolved(const std::vector &frefs) const; + ast::ExpressionPtr getDesugared(core::FileRef fref) const; const core::GlobalState &state() const; + + void updateGsFromOptions(const DidChangeConfigurationParams &options) const; + LSPFileUpdates getNoopUpdate(std::vector frefs) const; }; } // namespace sorbet::realmain::lsp #endif diff --git a/main/lsp/LocalVarFinder.cc b/main/lsp/LocalVarFinder.cc index eaac38010f..c3b71f2cfd 100644 --- a/main/lsp/LocalVarFinder.cc +++ b/main/lsp/LocalVarFinder.cc @@ -6,10 +6,8 @@ using namespace std; namespace sorbet::realmain::lsp { -void LocalVarFinder::preTransformBlock(core::Context ctx, ast::ExpressionPtr &tree) { +void LocalVarFinder::preTransformBlock(core::Context ctx, const ast::Block &block) { ENFORCE(!methodStack.empty()); - - auto &block = ast::cast_tree_nonnull(tree); auto loc = ctx.locAt(block.loc); if (methodStack.back() != this->targetMethod) { @@ -26,11 +24,9 @@ void LocalVarFinder::preTransformBlock(core::Context ctx, ast::ExpressionPtr &tr } } -void LocalVarFinder::postTransformAssign(core::Context ctx, ast::ExpressionPtr &tree) { +void LocalVarFinder::postTransformAssign(core::Context ctx, const ast::Assign &assign) { ENFORCE(!methodStack.empty()); - auto &assign = ast::cast_tree_nonnull(tree); - auto *local = ast::cast_tree(assign.lhs); if (local == nullptr) { return; @@ -41,9 +37,7 @@ void LocalVarFinder::postTransformAssign(core::Context ctx, ast::ExpressionPtr & } } -void LocalVarFinder::preTransformMethodDef(core::Context ctx, ast::ExpressionPtr &tree) { - auto &methodDef = ast::cast_tree_nonnull(tree); - +void LocalVarFinder::preTransformMethodDef(core::Context ctx, const ast::MethodDef &methodDef) { ENFORCE(methodDef.symbol.exists()); ENFORCE(methodDef.symbol != core::Symbols::todoMethod()); @@ -59,12 +53,11 @@ void LocalVarFinder::preTransformMethodDef(core::Context ctx, ast::ExpressionPtr this->methodStack.emplace_back(currentMethod); } -void LocalVarFinder::postTransformMethodDef(core::Context ctx, ast::ExpressionPtr &tree) { +void LocalVarFinder::postTransformMethodDef(core::Context ctx, const ast::MethodDef &tree) { this->methodStack.pop_back(); } -void LocalVarFinder::preTransformClassDef(core::Context ctx, ast::ExpressionPtr &tree) { - auto &classDef = ast::cast_tree_nonnull(tree); +void LocalVarFinder::preTransformClassDef(core::Context ctx, const ast::ClassDef &classDef) { ENFORCE(classDef.symbol.exists()); ENFORCE(classDef.symbol != core::Symbols::todo()); @@ -74,7 +67,7 @@ void LocalVarFinder::preTransformClassDef(core::Context ctx, ast::ExpressionPtr this->methodStack.emplace_back(currentMethod); } -void LocalVarFinder::postTransformClassDef(core::Context ctx, ast::ExpressionPtr &tree) { +void LocalVarFinder::postTransformClassDef(core::Context ctx, const ast::ClassDef &tree) { this->methodStack.pop_back(); } diff --git a/main/lsp/LocalVarFinder.h b/main/lsp/LocalVarFinder.h index 089c90a524..087d573628 100644 --- a/main/lsp/LocalVarFinder.h +++ b/main/lsp/LocalVarFinder.h @@ -20,12 +20,12 @@ class LocalVarFinder { public: LocalVarFinder(core::MethodRef targetMethod, core::Loc queryLoc) : targetMethod(targetMethod), queryLoc(queryLoc) {} - void postTransformAssign(core::Context ctx, ast::ExpressionPtr &assign); - void preTransformBlock(core::Context ctx, ast::ExpressionPtr &block); - void preTransformMethodDef(core::Context ctx, ast::ExpressionPtr &methodDef); - void postTransformMethodDef(core::Context ctx, ast::ExpressionPtr &methodDef); - void preTransformClassDef(core::Context ctx, ast::ExpressionPtr &classDef); - void postTransformClassDef(core::Context ctx, ast::ExpressionPtr &classDef); + void postTransformAssign(core::Context ctx, const ast::Assign &assign); + void preTransformBlock(core::Context ctx, const ast::Block &block); + void preTransformMethodDef(core::Context ctx, const ast::MethodDef &methodDef); + void postTransformMethodDef(core::Context ctx, const ast::MethodDef &methodDef); + void preTransformClassDef(core::Context ctx, const ast::ClassDef &classDef); + void postTransformClassDef(core::Context ctx, const ast::ClassDef &classDef); const std::vector &result() const; }; diff --git a/main/lsp/LocalVarSaver.cc b/main/lsp/LocalVarSaver.cc index 51a1d51ef6..95e69ca4b8 100644 --- a/main/lsp/LocalVarSaver.cc +++ b/main/lsp/LocalVarSaver.cc @@ -21,8 +21,7 @@ core::MethodRef enclosingMethod(core::Context ctx) { } } // namespace -void LocalVarSaver::postTransformBlock(core::Context ctx, ast::ExpressionPtr &tree) { - auto &block = ast::cast_tree_nonnull(tree); +void LocalVarSaver::postTransformBlock(core::Context ctx, const ast::Block &block) { auto method = enclosingMethod(ctx); for (auto &arg : block.args) { @@ -38,8 +37,7 @@ void LocalVarSaver::postTransformBlock(core::Context ctx, ast::ExpressionPtr &tr } } -void LocalVarSaver::postTransformLocal(core::Context ctx, ast::ExpressionPtr &tree) { - auto &local = ast::cast_tree_nonnull(tree); +void LocalVarSaver::postTransformLocal(core::Context ctx, const ast::Local &local) { auto method = enclosingMethod(ctx); bool lspQueryMatch = ctx.state.lspQuery.matchesVar(method, local.localVariable); @@ -53,13 +51,12 @@ void LocalVarSaver::postTransformLocal(core::Context ctx, ast::ExpressionPtr &tr } } -void LocalVarSaver::preTransformMethodDef(core::Context ctx, ast::ExpressionPtr &tree) { - this->enclosingMethodDefLoc.emplace_back(ctx.locAt(tree.loc())); +void LocalVarSaver::preTransformMethodDef(core::Context ctx, const ast::MethodDef &methodDef) { + this->enclosingMethodDefLoc.emplace_back(ctx.locAt(methodDef.loc)); } -void LocalVarSaver::postTransformMethodDef(core::Context ctx, ast::ExpressionPtr &tree) { +void LocalVarSaver::postTransformMethodDef(core::Context ctx, const ast::MethodDef &methodDef) { this->enclosingMethodDefLoc.pop_back(); - auto &methodDef = ast::cast_tree_nonnull(tree); // Check args. for (auto &arg : methodDef.args) { diff --git a/main/lsp/LocalVarSaver.h b/main/lsp/LocalVarSaver.h index 820cdee443..4650bd46e7 100644 --- a/main/lsp/LocalVarSaver.h +++ b/main/lsp/LocalVarSaver.h @@ -16,10 +16,10 @@ class LocalVarSaver { LocalVarSaver(core::Loc rootLoc, std::optional &&signature) : enclosingMethodDefLoc({rootLoc}), signature(move(signature)) {} - void postTransformBlock(core::Context ctx, ast::ExpressionPtr &local); - void postTransformLocal(core::Context ctx, ast::ExpressionPtr &local); - void preTransformMethodDef(core::Context ctx, ast::ExpressionPtr &methodDef); - void postTransformMethodDef(core::Context ctx, ast::ExpressionPtr &methodDef); + void postTransformBlock(core::Context ctx, const ast::Block &local); + void postTransformLocal(core::Context ctx, const ast::Local &local); + void preTransformMethodDef(core::Context ctx, const ast::MethodDef &methodDef); + void postTransformMethodDef(core::Context ctx, const ast::MethodDef &methodDef); }; }; // namespace sorbet::realmain::lsp diff --git a/main/lsp/MessageQueueState.h b/main/lsp/MessageQueueState.h new file mode 100644 index 0000000000..e8d7628bfb --- /dev/null +++ b/main/lsp/MessageQueueState.h @@ -0,0 +1,33 @@ +#ifndef SORBET_LSP_MESSAGE_QUEUE_STATE_H +#define SORBET_LSP_MESSAGE_QUEUE_STATE_H + +#include "common/counters/Counters.h" +#include +#include + +namespace sorbet::realmain::lsp { + +class LSPMessage; + +struct MessageQueueState { + std::deque> pendingRequests; + bool terminate = false; + int errorCode = 0; + // Counters collected from other threads. + CounterState counters; + + class NotifyOnDestruction { + absl::Mutex &mutex; + bool &flag; + + public: + NotifyOnDestruction(MessageQueueState &state, absl::Mutex &mutex) : mutex(mutex), flag(state.terminate){}; + ~NotifyOnDestruction() { + absl::MutexLock lck(&mutex); + flag = true; + } + }; +}; + +} // namespace sorbet::realmain::lsp +#endif diff --git a/main/lsp/NextMethodFinder.cc b/main/lsp/NextMethodFinder.cc index cd7a941824..7dc7b03a4e 100644 --- a/main/lsp/NextMethodFinder.cc +++ b/main/lsp/NextMethodFinder.cc @@ -5,10 +5,8 @@ using namespace std; namespace sorbet::realmain::lsp { -void NextMethodFinder::preTransformClassDef(core::Context ctx, ast::ExpressionPtr &tree) { - auto &classDef = ast::cast_tree_nonnull(tree); - - auto loc = ctx.locAt(tree.loc()); +void NextMethodFinder::preTransformClassDef(core::Context ctx, const ast::ClassDef &classDef) { + auto loc = ctx.locAt(classDef.loc); if (!this->narrowestClassDefRange.exists()) { // No narrowestClassDefRange yet, so take the loc @@ -28,13 +26,12 @@ void NextMethodFinder::preTransformClassDef(core::Context ctx, ast::ExpressionPt this->scopeContainsQueryLoc.emplace_back(loc.contains(this->queryLoc)); } -void NextMethodFinder::postTransformClassDef(core::Context ctx, ast::ExpressionPtr &tree) { +void NextMethodFinder::postTransformClassDef(core::Context ctx, const ast::ClassDef &tree) { ENFORCE(!this->scopeContainsQueryLoc.empty()); this->scopeContainsQueryLoc.pop_back(); } -void NextMethodFinder::preTransformMethodDef(core::Context ctx, ast::ExpressionPtr &tree) { - auto &methodDef = ast::cast_tree_nonnull(tree); +void NextMethodFinder::preTransformMethodDef(core::Context ctx, const ast::MethodDef &methodDef) { ENFORCE(methodDef.symbol.exists()); ENFORCE(methodDef.symbol != core::Symbols::todoMethod()); @@ -48,7 +45,7 @@ void NextMethodFinder::preTransformMethodDef(core::Context ctx, ast::ExpressionP auto currentMethod = methodDef.symbol; - auto currentLoc = ctx.locAt(tree.loc()); + auto currentLoc = ctx.locAt(methodDef.loc); if (!currentLoc.exists()) { // Defensive in case location information is disabled (e.g., certain fuzzer modes) return; diff --git a/main/lsp/NextMethodFinder.h b/main/lsp/NextMethodFinder.h index 9374d1de3c..3a6cbb775d 100644 --- a/main/lsp/NextMethodFinder.h +++ b/main/lsp/NextMethodFinder.h @@ -24,9 +24,9 @@ class NextMethodFinder { : queryLoc(queryLoc), narrowestClassDefRange(core::Loc::none()), scopeContainsQueryLoc(std::vector{}), result_(core::Loc::none(), core::Symbols::noMethod()) {} - void preTransformClassDef(core::Context ctx, ast::ExpressionPtr &tree); - void postTransformClassDef(core::Context ctx, ast::ExpressionPtr &tree); - void preTransformMethodDef(core::Context ctx, ast::ExpressionPtr &tree); + void preTransformClassDef(core::Context ctx, const ast::ClassDef &tree); + void postTransformClassDef(core::Context ctx, const ast::ClassDef &tree); + void preTransformMethodDef(core::Context ctx, const ast::MethodDef &tree); const core::MethodRef result() const; }; diff --git a/main/lsp/QueryCollector.cc b/main/lsp/QueryCollector.cc index 38b2387f74..5c780d43b1 100644 --- a/main/lsp/QueryCollector.cc +++ b/main/lsp/QueryCollector.cc @@ -11,7 +11,7 @@ uint16_t getQueryResponseTypeSpecificity(const core::lsp::QueryResponse &q) { return 8; } else if (q.isMethodDef()) { return 7; - } else if (auto send = q.isSend()) { + } else if (q.isSend()) { return 6; } else if (q.isField()) { return 5; diff --git a/main/lsp/lsp_helpers.cc b/main/lsp/lsp_helpers.cc index fd5ab70e03..a5c8c7a78e 100644 --- a/main/lsp/lsp_helpers.cc +++ b/main/lsp/lsp_helpers.cc @@ -31,6 +31,11 @@ bool hideSymbol(const core::GlobalState &gs, core::SymbolRef sym) { name == core::Names::unresolvedAncestors() || name == core::Names::Constants::AttachedClass()) { return true; } + // TODO(jez) We probably want to get rid of anything with angle brackets (like what + // completion.cc does) but that can be in another change. + if (name == core::Names::beforeAngles()) { + return true; + } // static-init for a file if (name.kind() == core::NameKind::UNIQUE && name.dataUnique(gs)->original == core::Names::staticInit()) { return true; @@ -58,163 +63,16 @@ unique_ptr formatRubyMarkup(MarkupKind markupKind, string_view ru return make_unique(markupKind, move(content)); } -// iff a sig has more than this many parameters, then print it as a multi-line sig. -constexpr int MAX_PRETTY_SIG_ARGS = 4; -// iff a `def` would be this wide or wider, expand it to be a multi-line def. -constexpr int MAX_PRETTY_WIDTH = 80; - -string prettySigForMethod(const core::GlobalState &gs, core::MethodRef method, const core::TypePtr &receiver, - core::TypePtr retType, const core::TypeConstraint *constraint) { - ENFORCE(method.exists()); - ENFORCE(method.data(gs)->dealiasMethod(gs) == method); - // handle this case anyways so that we don't crash in prod when this method is mis-used - if (!method.exists()) { - return ""; - } - - if (!retType) { - retType = getResultType(gs, method.data(gs)->resultType, method, receiver, constraint); - } - string methodReturnType = - (retType == core::Types::void_()) ? "void" : absl::StrCat("returns(", retType.show(gs), ")"); - vector typeAndArgNames; - - vector flags; - auto sym = method.data(gs); - string sigCall = "sig"; - if (sym->flags.isFinal) { - sigCall = "sig(:final)"; - } - if (sym->flags.isAbstract) { - flags.emplace_back("abstract"); - } - if (sym->flags.isOverridable) { - flags.emplace_back("overridable"); - } - if (sym->flags.isOverride) { - flags.emplace_back("override"); - } - for (auto &argSym : method.data(gs)->arguments) { - // Don't display synthetic arguments (like blk). - if (!argSym.isSyntheticBlockArgument()) { - typeAndArgNames.emplace_back(absl::StrCat( - argSym.argumentName(gs), ": ", getResultType(gs, argSym.type, method, receiver, constraint).show(gs))); - } - } - - string flagString = ""; - if (!flags.empty()) { - flagString = fmt::format("{}.", fmt::join(flags, ".")); - } - string paramsString = ""; - if (!typeAndArgNames.empty()) { - paramsString = fmt::format("params({}).", fmt::join(typeAndArgNames, ", ")); - } - - auto oneline = fmt::format("{} {{{}{}{}}}", sigCall, flagString, paramsString, methodReturnType); - if (oneline.size() <= MAX_PRETTY_WIDTH && typeAndArgNames.size() <= MAX_PRETTY_SIG_ARGS) { - return oneline; - } - - if (!flags.empty()) { - flagString = fmt::format("{}\n .", fmt::join(flags, "\n .")); - } - if (!typeAndArgNames.empty()) { - paramsString = fmt::format("params(\n {}\n )\n .", fmt::join(typeAndArgNames, ",\n ")); - } - return fmt::format("{} do\n {}{}{}\nend", sigCall, flagString, paramsString, methodReturnType); -} - -string prettyDefForMethod(const core::GlobalState &gs, core::MethodRef method) { - ENFORCE(method.exists()); - // handle this case anyways so that we don't crash in prod when this method is mis-used - if (!method.exists()) { - return ""; - } - auto methodData = method.data(gs); - - string visibility = ""; - if (methodData->flags.isPrivate) { - visibility = "private "; - } else if (methodData->flags.isProtected) { - visibility = "protected "; - } - - auto methodNameRef = methodData->name; - ENFORCE(methodNameRef.exists()); - string methodName = "???"; - if (methodNameRef.exists()) { - methodName = methodNameRef.toString(gs); - } - string methodNamePrefix = ""; - if (methodData->owner.exists() && methodData->owner.data(gs)->attachedClass(gs).exists()) { - methodNamePrefix = "self."; - } - vector prettyArgs; - const auto &arguments = methodData->dealiasMethod(gs).data(gs)->arguments; - ENFORCE(!arguments.empty(), "Should have at least a block arg"); - for (const auto &argSym : arguments) { - // Don't display synthetic arguments (like blk). - if (argSym.isSyntheticBlockArgument()) { - continue; - } - string prefix = ""; - string suffix = ""; - if (argSym.flags.isRepeated) { - if (argSym.flags.isKeyword) { - prefix = "**"; // variadic keyword args - } else { - prefix = "*"; // rest args - } - } else if (argSym.flags.isKeyword) { - if (argSym.flags.isDefault) { - suffix = ": …"; // optional keyword (has a default value) - } else { - suffix = ":"; // required keyword - } - } else if (argSym.flags.isBlock) { - prefix = "&"; - } else if (argSym.flags.isDefault) { - suffix = "=…"; - } - prettyArgs.emplace_back(fmt::format("{}{}{}", prefix, argSym.argumentName(gs), suffix)); - } - - string argListPrefix = ""; - string argListSeparator = ""; - string argListSuffix = ""; - if (prettyArgs.size() > 0) { - argListPrefix = "("; - argListSeparator = ", "; - argListSuffix = ")"; - } - - auto result = fmt::format("{}def {}{}{}{}{}; end", visibility, methodNamePrefix, methodName, argListPrefix, - fmt::join(prettyArgs, argListSeparator), argListSuffix); - if (prettyArgs.size() > 0 && result.length() >= MAX_PRETTY_WIDTH) { - argListPrefix = "(\n "; - argListSeparator = ",\n "; - argListSuffix = "\n)"; - result = fmt::format("{}def {}{}{}{}{}\nend", visibility, methodNamePrefix, methodName, argListPrefix, - fmt::join(prettyArgs, argListSeparator), argListSuffix); - } - return result; -} - -string prettyTypeForMethod(const core::GlobalState &gs, core::MethodRef method, const core::TypePtr &receiver, - const core::TypePtr &retType, const core::TypeConstraint *constraint) { - return fmt::format("{}\n{}", - prettySigForMethod(gs, method.data(gs)->dealiasMethod(gs), receiver, retType, constraint), - prettyDefForMethod(gs, method)); -} - string prettyTypeForConstant(const core::GlobalState &gs, core::SymbolRef constant) { - // Request that the constant already be dealiased, rather than dealias here to avoid defensively dealiasing. - // We should understand where dealias calls go. - ENFORCE(constant == constant.dealias(gs)); - if (constant == core::Symbols::StubModule()) { - return "This constant is not defined"; + return "(unable to resolve constant)"; + } + + if (constant.isClassAlias(gs)) { + auto dealiased = constant.dealias(gs); + auto dealiasedShow = + dealiased == core::Symbols::StubModule() ? "(unable to resolve constant)" : dealiased.show(gs); + return fmt::format("{} = {}", constant.name(gs).show(gs), dealiasedShow); } core::TypePtr result; @@ -236,29 +94,6 @@ string prettyTypeForConstant(const core::GlobalState &gs, core::SymbolRef consta } } -core::TypePtr getResultType(const core::GlobalState &gs, const core::TypePtr &type, core::SymbolRef inWhat, - core::TypePtr receiver, const core::TypeConstraint *constr) { - auto resultType = type; - if (core::is_proxy_type(receiver)) { - receiver = receiver.underlying(gs); - } - if (auto *applied = core::cast_type(receiver)) { - /* instantiate generic classes */ - resultType = core::Types::resultTypeAsSeenFrom(gs, resultType, inWhat.enclosingClass(gs), applied->klass, - applied->targs); - } - if (!resultType) { - resultType = core::Types::untypedUntracked(); - } - if (receiver) { - resultType = core::Types::replaceSelfType(gs, resultType, receiver); // instantiate self types - } - if (constr) { - resultType = core::Types::instantiate(gs, resultType, *constr); // instantiate generic methods - } - return resultType; -} - SymbolKind symbolRef2SymbolKind(const core::GlobalState &gs, core::SymbolRef symbol, bool isAttrBestEffortUIOnly) { if (symbol.isClassOrModule()) { auto klass = symbol.asClassOrModuleRef(); @@ -336,6 +171,52 @@ vector getSubclassesSlow(const core::GlobalState &gs, co return subclasses; } +unique_ptr +skipLiteralIfMethodDef(vector> &queryResponses) { + for (auto &r : queryResponses) { + if (r->isMethodDef()) { + return move(r); + } else if (!r->isLiteral()) { + break; + } + } + + return move(queryResponses[0]); +} + +unique_ptr +getQueryResponseForFindAllReferences(vector> &queryResponses) { + // Find all references might show an Ident last if its a `prop`, and the Ident will be the + // synthetic local variable name of the method argument. + auto firstResp = queryResponses[0]->isIdent(); + if (firstResp == nullptr) { + return skipLiteralIfMethodDef(queryResponses); + } + + for (auto resp = queryResponses.begin() + 1; resp != queryResponses.end(); ++resp) { + // If this query response has the same location as the first ident response, keep skipping + // up. Seeing a query response for the exact same loc suggests this was synthesized in + // rewriter. + if ((*resp)->getLoc() == firstResp->termLoc) { + continue; + } + + // It's always okay to skip literals for Find All References + auto lit = (*resp)->isLiteral(); + if (lit != nullptr) { + continue; + } + + if ((*resp)->isMethodDef()) { + return move(*resp); + } else { + return skipLiteralIfMethodDef(queryResponses); + } + } + + return skipLiteralIfMethodDef(queryResponses); +} + /** * Retrieves the documentation above a symbol. * - Returned documentation has one trailing newline (if it exists) diff --git a/main/lsp/notifications/did_change_configuration.cc b/main/lsp/notifications/did_change_configuration.cc new file mode 100644 index 0000000000..757d2d2abf --- /dev/null +++ b/main/lsp/notifications/did_change_configuration.cc @@ -0,0 +1,32 @@ +#include "main/lsp/notifications/did_change_configuration.h" +#include "main/lsp/LSPIndexer.h" +#include "main/lsp/LSPPreprocessor.h" + +namespace sorbet::realmain::lsp { +DidChangeConfigurationTask::DidChangeConfigurationTask(const LSPConfiguration &config, + std::unique_ptr params, + std::vector &&openFiles, const uint32_t epoch) + : LSPTask(config, LSPMethod::WorkspaceDidChangeConfiguration), params(move(params)), openFilePaths(move(openFiles)), + epoch(epoch) {} + +LSPTask::Phase DidChangeConfigurationTask::finalPhase() const { + // We want this to run all the way so that the changes to + // Global State get propagated through. + return LSPTask::Phase::RUN; +} + +void DidChangeConfigurationTask::index(LSPIndexer &indexer) { + indexer.updateGsFromOptions(*params); +} + +void DidChangeConfigurationTask::run(LSPTypecheckerDelegate &tc) { + tc.updateGsFromOptions(*params); + std::vector openFileRefs; + for (auto const &path : openFilePaths) { + openFileRefs.push_back(tc.state().findFileByPath(path)); + } + auto updates = tc.getNoopUpdate(openFileRefs); + updates.epoch = epoch; + tc.typecheckOnFastPath(std::move(updates), {}); +} +} // namespace sorbet::realmain::lsp diff --git a/main/lsp/notifications/did_change_configuration.h b/main/lsp/notifications/did_change_configuration.h new file mode 100644 index 0000000000..6bf0809b12 --- /dev/null +++ b/main/lsp/notifications/did_change_configuration.h @@ -0,0 +1,25 @@ +#ifndef RUBY_TYPER_LSP_NOTIFICATIONS_DID_CHANGE_CONFIGURATION_H +#define RUBY_TYPER_LSP_NOTIFICATIONS_DID_CHANGE_CONFIGURATION_H + +#include "main/lsp/LSPTask.h" + +namespace sorbet::realmain::lsp { +class DidChangeConfigurationParams; +class DidChangeConfigurationTask final : public LSPTask { + std::unique_ptr params; + const std::vector openFilePaths; + const uint32_t epoch; + +public: + DidChangeConfigurationTask(const LSPConfiguration &config, std::unique_ptr params, + std::vector &&openFiles, const uint32_t epoch); + + LSPTask::Phase finalPhase() const override; + + void index(LSPIndexer &indexer) override; + + void run(LSPTypecheckerDelegate &tc) override; +}; +} // namespace sorbet::realmain::lsp + +#endif diff --git a/main/lsp/notifications/notifications.h b/main/lsp/notifications/notifications.h index 422d35c8ba..a9243c3393 100644 --- a/main/lsp/notifications/notifications.h +++ b/main/lsp/notifications/notifications.h @@ -2,6 +2,7 @@ #define RUBY_TYPER_LSP_NOTIFICATIONS_NOTIFICATIONS_H #include "main/lsp/notifications/cancel_request.h" +#include "main/lsp/notifications/did_change_configuration.h" #include "main/lsp/notifications/exit.h" #include "main/lsp/notifications/initialized.h" #include "main/lsp/notifications/sorbet_fence.h" diff --git a/main/lsp/requests/code_action.cc b/main/lsp/requests/code_action.cc index 7c6cc39a53..d02fee2a44 100644 --- a/main/lsp/requests/code_action.cc +++ b/main/lsp/requests/code_action.cc @@ -4,6 +4,7 @@ #include "common/sort/sort.h" #include "core/lsp/QueryResponse.h" #include "main/lsp/ConvertToSingletonClassMethod.h" +#include "main/lsp/ExtractVariable.h" #include "main/lsp/LSPLoop.h" #include "main/lsp/LSPQuery.h" #include "main/lsp/MoveMethod.h" @@ -14,6 +15,7 @@ using namespace std; namespace sorbet::realmain::lsp { namespace { + const UnorderedSet OPERATORS = {"+", "−", "*", "/", "%", "**", "==", "!=", ">", "<", ">=", "<=", "<=>", "===", ".eql?", "equal?", "=", "+=", "-=", "*=", "/=", "%=", "**=", "&", "|", "^", "~", @@ -25,20 +27,19 @@ bool isOperator(string_view name) { vector> getQuickfixEdits(const LSPConfiguration &config, const core::GlobalState &gs, const vector &edits) { - UnorderedMap>> editsByFile; + UnorderedMap>> editsByFile; for (auto &edit : edits) { auto range = Range::fromLoc(gs, edit.loc); if (range != nullptr) { - editsByFile[config.fileRef2Uri(gs, edit.loc.file())].emplace_back( - make_unique(move(range), edit.replacement)); + editsByFile[edit.loc.file()].emplace_back(make_unique(move(range), edit.replacement)); } } vector> documentEdits; - for (auto &it : editsByFile) { + for (auto &[file, edits] : editsByFile) { // TODO: Document version documentEdits.emplace_back(make_unique( - make_unique(it.first, JSONNullObject()), move(it.second))); + make_unique(config.fileRef2Uri(gs, file), JSONNullObject()), move(edits))); } return documentEdits; } @@ -72,8 +73,8 @@ hasLoneMethodResponse(const core::GlobalState &gs, const vector> &responses) { +const core::lsp::SendResponse *isTUnsafeOrMustResponse(const core::GlobalState &gs, + const vector> &responses) { if (responses.empty()) { return nullptr; } @@ -89,7 +90,8 @@ const core::lsp::SendResponse *isTUnsafeResponse(const core::GlobalState &gs, } auto data = method.data(gs); - if (data->owner != core::Symbols::TSingleton() || data->name != core::Names::unsafe()) { + if (data->owner != core::Symbols::TSingleton() || + (data->name != core::Names::unsafe() && data->name != core::Names::must())) { return nullptr; } @@ -154,6 +156,9 @@ unique_ptr CodeActionTask::runRequest(LSPTypecheckerDelegate &t action->kind = CodeActionKind::Quickfix; auto workspaceEdit = make_unique(); workspaceEdit->documentChanges = getQuickfixEdits(config, gs, autocorrect.edits); + if (absl::c_any_of(autocorrect.edits, [&](auto edit) { return edit.loc.file().isPackage(gs); })) { + action->command = make_unique("Save package files", "sorbet.savePackageFiles"); + } action->edit = move(workspaceEdit); result.emplace_back(move(action)); } @@ -187,76 +192,104 @@ unique_ptr CodeActionTask::runRequest(LSPTypecheckerDelegate &t } } - auto queryResult = LSPQuery::byLoc(config, typechecker, params->textDocument->uri, *params->range->start, - LSPMethod::TextDocumentCodeAction, false); + if (loc.beginPos() == loc.endPos()) { + // No selection + auto queryResult = LSPQuery::byLoc(config, typechecker, params->textDocument->uri, *params->range->start, + LSPMethod::TextDocumentCodeAction, false); - // Generate "Move method" code actions only for class method definitions - if (queryResult.error == nullptr) { - if (auto *def = hasLoneMethodResponse(gs, queryResult.responses)) { - unique_ptr action; - bool canResolveLazily = config.getClientConfig().clientCodeActionResolveEditSupport && - config.getClientConfig().clientCodeActionDataSupport; + // Generate "Move method" code actions only for class method definitions + if (queryResult.error == nullptr) { + if (auto *def = hasLoneMethodResponse(gs, queryResult.responses)) { + unique_ptr action; + bool canResolveLazily = config.getClientConfig().clientCodeActionResolveEditSupport && + config.getClientConfig().clientCodeActionDataSupport; - if (def->symbol.data(gs)->owner.data(gs)->isSingletonClass(gs)) { - auto action = make_unique("Move method to a new module"); - action->kind = CodeActionKind::RefactorExtract; - - if (canResolveLazily) { - action->data = move(params); - } else { - auto workspaceEdit = make_unique(); - auto edits = getMoveMethodEdits(typechecker, config, *def); - workspaceEdit->documentChanges = move(edits); - action->edit = move(workspaceEdit); - } - - result.emplace_back(move(action)); - } else { - auto action = make_unique("Convert to singleton class method (best effort)"); - action->kind = CodeActionKind::RefactorRewrite; + if (def->symbol.data(gs)->owner.data(gs)->isSingletonClass(gs)) { + auto action = make_unique("Move method to a new module"); + action->kind = CodeActionKind::RefactorExtract; - if (canResolveLazily) { - const auto &maybeSource = def->termLoc.source(gs); - if (maybeSource.has_value() && absl::StartsWith(maybeSource.value(), "def ")) { + if (canResolveLazily) { action->data = move(params); - result.emplace_back(move(action)); } else { - // Maybe this is an attr_reader or a prop or something. Abort. - // (Only have to do this logic in the lazy case, because the eager case does - // it already.) - } - } else { - auto workspaceEdit = make_unique(); - auto edits = convertToSingletonClassMethod(typechecker, config, *def); - if (!edits.empty()) { - // "empty" means an error in convertToSingletonClassMethod. - // Don't prevent other code actions from being reported due to this one error. - // Instead, merely skip this code action. + auto workspaceEdit = make_unique(); + auto edits = getMoveMethodEdits(typechecker, config, *def); workspaceEdit->documentChanges = move(edits); action->edit = move(workspaceEdit); - result.emplace_back(move(action)); + } + + result.emplace_back(move(action)); + } else { + auto action = make_unique("Convert to singleton class method (best effort)"); + action->kind = CodeActionKind::RefactorRewrite; + + if (canResolveLazily) { + const auto &maybeSource = def->termLoc.source(gs); + if (maybeSource.has_value() && absl::StartsWith(maybeSource.value(), "def ")) { + action->data = move(params); + result.emplace_back(move(action)); + } else { + // Maybe this is an attr_reader or a prop or something. Abort. + // (Only have to do this logic in the lazy case, because the eager case does + // it already.) + } + } else { + auto workspaceEdit = make_unique(); + auto edits = convertToSingletonClassMethod(typechecker, config, *def); + if (!edits.empty()) { + // "empty" means an error in convertToSingletonClassMethod. + // Don't prevent other code actions from being reported due to this one error. + // Instead, merely skip this code action. + workspaceEdit->documentChanges = move(edits); + action->edit = move(workspaceEdit); + result.emplace_back(move(action)); + } } } - } - } else if (auto *resp = isTUnsafeResponse(gs, queryResult.responses)) { - auto tdi = make_unique(move(params->textDocument->uri), JSONNullObject()); - auto replaceRange = Range::fromLoc(gs, resp->termLoc()); - auto arg0Loc = core::Loc(file, resp->argLocOffsets[0]); - auto newContents = arg0Loc.source(gs).value(); + } else if (auto *resp = isTUnsafeOrMustResponse(gs, queryResult.responses)) { + auto tdi = + make_unique(move(params->textDocument->uri), JSONNullObject()); + auto replaceRange = Range::fromLoc(gs, resp->termLoc()); + auto arg0Loc = core::Loc(file, resp->argLocOffsets[0]); + auto newContents = arg0Loc.source(gs).value(); - vector> edits; - edits.emplace_back(make_unique(move(replaceRange), string(newContents))); + vector> edits; + edits.emplace_back(make_unique(move(replaceRange), string(newContents))); - vector> documentEdits; - documentEdits.emplace_back(make_unique(move(tdi), move(edits))); + vector> documentEdits; + documentEdits.emplace_back(make_unique(move(tdi), move(edits))); - auto workspaceEdit = make_unique(); - workspaceEdit->documentChanges = move(documentEdits); + auto workspaceEdit = make_unique(); + workspaceEdit->documentChanges = move(documentEdits); - auto action = make_unique("Delete T.unsafe"); - action->kind = CodeActionKind::RefactorRewrite; - action->edit = move(workspaceEdit); - result.emplace_back(move(action)); + auto action = make_unique(fmt::format("Delete T.{}", resp->callerSideName.show(gs))); + action->kind = CodeActionKind::RefactorRewrite; + action->edit = move(workspaceEdit); + result.emplace_back(move(action)); + } + } + } else { + // Selection + if (config.opts.lspExtractToVariableEnabled) { + // For move method to new module we use canResolveLazily to defer the computation + // until the user has actually selected the action. We can't do that here because + // we need to do the core computation to know if extract the current selection is + // valid in the first place, to decide if we can show the code action or not. + Timer timeit(gs.tracer(), "Extract to Variable"); + + auto documentEdits = VariableExtractor::getEdits(typechecker, config, loc); + if (!documentEdits.empty()) { + auto action = make_unique("Extract Variable"); + action->kind = CodeActionKind::RefactorExtract; + + auto workspaceEdit = make_unique(); + workspaceEdit->documentChanges = move(documentEdits); + + action->edit = move(workspaceEdit); + result.emplace_back(move(action)); + + // TODO(neil): trigger a rename for newVariable + // TODO(neil): replace other occurrences of this expression with newVariable + } } } diff --git a/main/lsp/requests/completion.cc b/main/lsp/requests/completion.cc index 49ab3b2ed2..64231f5b1c 100644 --- a/main/lsp/requests/completion.cc +++ b/main/lsp/requests/completion.cc @@ -8,6 +8,7 @@ #include "common/strings/formatting.h" #include "common/typecase.h" #include "core/lsp/QueryResponse.h" +#include "core/source_generator/source_generator.h" #include "main/lsp/FieldFinder.h" #include "main/lsp/LSPLoop.h" #include "main/lsp/LSPQuery.h" @@ -297,11 +298,12 @@ vector allSimilarLocalNames(const core::GlobalState &gs, const ve return result; } -string methodSnippet(const core::GlobalState &gs, core::DispatchResult &dispatchResult, core::MethodRef method, - const core::TypePtr &receiverType, const core::TypeConstraint *constraint, uint16_t totalArgs) { +string methodSnippet(const core::GlobalState &gs, core::DispatchResult &dispatchResult, core::MethodRef maybeAlias, + const core::TypePtr &receiverType, const core::TypeConstraint *constraint, uint16_t totalArgs, + core::Loc queryLoc) { fmt::memory_buffer result; - auto shortName = method.data(gs)->name.shortName(gs); - auto isSetter = method.data(gs)->name.isSetter(gs); + auto shortName = maybeAlias.data(gs)->name.shortName(gs); + auto isSetter = maybeAlias.data(gs)->name.isSetter(gs); if (isSetter) { fmt::format_to(std::back_inserter(result), "{}", string_view(shortName.data(), shortName.size() - 1)); } else { @@ -314,8 +316,18 @@ string methodSnippet(const core::GlobalState &gs, core::DispatchResult &dispatch * since the rest is likely useless */ if (totalArgs > 0 || dispatchResult.main.blockReturnType != nullptr) { - fmt::format_to(std::back_inserter(result), "${{0}}"); - return to_string(result); + auto maybeNewline = queryLoc.adjustLen(gs, 0, 1); + // ... but carve out an exception if the query location is at the end of the line, because + // then likely it only looks like there are "arguments" to this method call because of how + // Ruby allows the `x.` being split from the method name on the next line + // + // (This isn't a great solution, but I think that we're likely going to revisit generating + // snippets here in the future with a bit of an overhaul of how completion works, so I'm + // fine with it in the mean time.) + if (!maybeNewline.exists() || maybeNewline.source(gs) != "\n") { + fmt::format_to(std::back_inserter(result), "${{0}}"); + return to_string(result); + } } if (isSetter) { @@ -323,6 +335,7 @@ string methodSnippet(const core::GlobalState &gs, core::DispatchResult &dispatch return to_string(result); } + auto method = maybeAlias.data(gs)->dealiasMethod(gs); vector typeAndArgNames; for (auto &argSym : method.data(gs)->arguments) { fmt::memory_buffer argBuf; @@ -340,7 +353,8 @@ string methodSnippet(const core::GlobalState &gs, core::DispatchResult &dispatch fmt::format_to(std::back_inserter(argBuf), "{}: ", argSym.name.shortName(gs)); } if (argSym.type) { - auto resultType = getResultType(gs, argSym.type, method, receiverType, constraint).show(gs); + auto resultType = + core::source_generator::getResultType(gs, argSym.type, method, receiverType, constraint).show(gs); fmt::format_to(std::back_inserter(argBuf), "${{{}:{}}}", nextTabstop++, resultType); } else { fmt::format_to(std::back_inserter(argBuf), "${{{}}}", nextTabstop++); @@ -365,7 +379,8 @@ string methodSnippet(const core::GlobalState &gs, core::DispatchResult &dispatch auto targs_it = appliedType->targs.begin(); targs_it++; blkArgs = fmt::format(" |{}|", fmt::map_join(targs_it, appliedType->targs.end(), ", ", [&](auto targ) { - auto resultType = getResultType(gs, targ, method, receiverType, constraint); + auto resultType = core::source_generator::getResultType( + gs, targ, method, receiverType, constraint); return fmt::format("${{{}:{}}}", nextTabstop++, resultType.show(gs)); })); } @@ -595,7 +610,7 @@ vector allSimilarFieldsForClass(LSPTypecheckerDelegate &typecheck FieldFinder fieldFinder(klass, kind); for (auto &t : resolved) { auto ctx = core::Context(gs, core::Symbols::root(), t.file); - ast::TreeWalk::apply(ctx, fieldFinder, t.tree); + ast::ConstTreeWalk::apply(ctx, fieldFinder, t.tree); } auto fields = fieldFinder.result(); @@ -634,7 +649,7 @@ vector localNamesForMethod(LSPTypecheckerDelegate &typechecker, c LocalVarFinder localVarFinder(method, queryLoc); for (auto &t : resolved) { auto ctx = core::Context(gs, core::Symbols::root(), t.file); - ast::TreeWalk::apply(ctx, localVarFinder, t.tree); + ast::ConstTreeWalk::apply(ctx, localVarFinder, t.tree); } auto result = localVarFinder.result(); @@ -661,7 +676,7 @@ core::MethodRef firstMethodAfterQuery(LSPTypecheckerDelegate &typechecker, const NextMethodFinder nextMethodFinder(queryLoc); for (auto &t : resolved) { auto ctx = core::Context(gs, core::Symbols::root(), t.file); - ast::TreeWalk::apply(ctx, nextMethodFinder, t.tree); + ast::ConstTreeWalk::apply(ctx, nextMethodFinder, t.tree); } return nextMethodFinder.result(); @@ -883,7 +898,7 @@ vector> allSimilarConstantItems(const core::GlobalSta vector> items; if (scopes.size() == 1 && !scopes[0].exists()) { - // This happens when there was a contant literal like C::D but `C` itself was stubbed, + // This happens when there was a constant literal like C::D but `C` itself was stubbed, // so we have no idea what `D` is or what its resolution scope is. return items; } @@ -1040,7 +1055,7 @@ CompletionTask::getCompletionItemForMethod(LSPTypecheckerDelegate &typechecker, string replacementText; if (supportsSnippets) { item->insertTextFormat = InsertTextFormat::Snippet; - replacementText = methodSnippet(gs, dispatchResult, what, receiverType, constraint, totalArgs); + replacementText = methodSnippet(gs, dispatchResult, maybeAlias, receiverType, constraint, totalArgs, queryLoc); } else { item->insertTextFormat = InsertTextFormat::PlainText; replacementText = label; @@ -1058,7 +1073,8 @@ CompletionTask::getCompletionItemForMethod(LSPTypecheckerDelegate &typechecker, documentation = findDocumentation(whatFile.data(gs).source(), what.data(gs)->loc().beginPos()); } - auto prettyType = prettyTypeForMethod(gs, maybeAlias, receiverType, nullptr, constraint); + auto prettyType = core::source_generator::prettyTypeForMethod(gs, maybeAlias, receiverType, nullptr, constraint, + core::ShowOptions().withUseValidSyntax()); item->documentation = formatRubyMarkup(markupKind, prettyType, documentation); if (documentation != nullopt && documentation->find("@deprecated") != documentation->npos) { @@ -1235,7 +1251,7 @@ unique_ptr CompletionTask::runRequest(LSPTypecheckerDelegate &t } auto queryLoc = maybeQueryLoc.value(); - auto result = LSPQuery::byLoc(config, typechecker, uri, pos, LSPMethod::TextDocumentCompletion); + auto result = LSPQuery::byLoc(config, typechecker, uri, pos, LSPMethod::TextDocumentCompletion, false); if (result.error) { // An error happened while setting up the query. @@ -1256,12 +1272,16 @@ unique_ptr CompletionTask::runRequest(LSPTypecheckerDelegate &t } } - ENFORCE(fref.exists()); - auto level = fref.data(gs).strictLevel; - if (!fref.data(gs).hasParseErrors() && level < core::StrictLevel::True) { - items.emplace_back(getCompletionItemForUntyped(gs, queryLoc, 0, "(file is not `# typed: true` or higher)")); - response->result = make_unique(false, move(items)); - return response; + auto enableTypedFalseCompletionNudges = config.getClientConfig().enableTypedFalseCompletionNudges; + if (enableTypedFalseCompletionNudges) { + ENFORCE(fref.exists()); + auto level = fref.data(gs).strictLevel; + if (!fref.data(gs).hasParseErrors() && level < core::StrictLevel::True) { + items.emplace_back( + getCompletionItemForUntyped(gs, queryLoc, 0, "(file is not `# typed: true` or higher)")); + response->result = make_unique(false, move(items)); + return response; + } } response->result = std::move(emptyResult); diff --git a/main/lsp/requests/definition.cc b/main/lsp/requests/definition.cc index 5e5c807701..f9914f1f08 100644 --- a/main/lsp/requests/definition.cc +++ b/main/lsp/requests/definition.cc @@ -1,11 +1,52 @@ #include "main/lsp/requests/definition.h" +#include "ast/treemap/treemap.h" #include "core/lsp/QueryResponse.h" +#include "main/lsp/LSPLoop.h" #include "main/lsp/LSPQuery.h" +#include "main/lsp/NextMethodFinder.h" #include "main/lsp/json_types.h" using namespace std; namespace sorbet::realmain::lsp { + +namespace { + +core::MethodRef firstMethodAfterQuery(LSPTypecheckerDelegate &typechecker, const core::Loc queryLoc) { + const auto &gs = typechecker.state(); + auto files = vector{queryLoc.file()}; + auto resolved = typechecker.getResolved(files); + + NextMethodFinder nextMethodFinder(queryLoc); + for (auto &t : resolved) { + auto ctx = core::Context(gs, core::Symbols::root(), t.file); + ast::ConstTreeWalk::apply(ctx, nextMethodFinder, t.tree); + } + + return nextMethodFinder.result(); +} + +// TODO(jez) Can replace this with findMemberTransitiveAncestors once 7212 lands. +core::MethodRef findParentMethod(const core::GlobalState &gs, core::MethodRef childMethod) { + const auto &klassData = childMethod.data(gs)->owner.data(gs); + auto name = childMethod.data(gs)->name; + + for (const auto &mixin : klassData->mixins()) { + auto superMethod = mixin.data(gs)->findMethod(gs, name); + if (superMethod.exists()) { + return superMethod; + } + } + + if (klassData->superClass().exists()) { + return klassData->superClass().data(gs)->findMethodTransitive(gs, name); + } + + return core::Symbols::noMethod(); +} + +} // namespace + DefinitionTask::DefinitionTask(const LSPConfiguration &config, MessageId id, unique_ptr params) : LSPRequestTask(config, move(id), LSPMethod::TextDocumentDefinition), params(move(params)) {} @@ -26,11 +67,11 @@ unique_ptr DefinitionTask::runRequest(LSPTypecheckerDelegate &t if (!queryResponses.empty()) { const bool fileIsTyped = config.uri2FileRef(gs, params->textDocument->uri).data(gs).strictLevel >= core::StrictLevel::True; - auto resp = move(queryResponses[0]); + auto resp = skipLiteralIfMethodDef(queryResponses); // Only support go-to-definition on constants and fields in untyped files. if (auto c = resp->isConstant()) { - auto sym = c->symbol; + auto sym = c->symbolBeforeDealias; vector>> locMapping; for (auto loc : sym.locs(gs)) { locMapping.emplace_back(loc, config.loc2Location(gs, loc)); @@ -51,7 +92,7 @@ unique_ptr DefinitionTask::runRequest(LSPTypecheckerDelegate &t } std::transform(locMapping.begin(), locMapping.end(), std::back_inserter(locations), [](auto &p) { return std::move(p.second); }); - } else if (resp->isField() || (fileIsTyped && (resp->isIdent() || resp->isLiteral()))) { + } else if (resp->isField() || (fileIsTyped && resp->isIdent())) { const auto &retType = resp->getTypeAndOrigins(); for (auto &originLoc : retType.origins) { addLocIfExists(gs, locations, originLoc); @@ -66,7 +107,20 @@ unique_ptr DefinitionTask::runRequest(LSPTypecheckerDelegate &t auto start = sendResp->dispatchResult.get(); while (start != nullptr) { if (start->main.method.exists() && !start->main.receiver.isUntyped()) { - addLocIfExists(gs, locations, start->main.method.data(gs)->loc()); + auto loc = start->main.method.data(gs)->loc(); + if (start->main.method == core::Symbols::T_Private_Methods_DeclBuilder_override()) { + auto nextMethod = firstMethodAfterQuery(typechecker, sendResp->termLoc()); + if (nextMethod.exists()) { + auto parentMethod = findParentMethod(gs, nextMethod); + if (parentMethod.exists()) { + // actually, jump to the definition of the abstract method, instead of + // the definition of `override` in builder.rbi + loc = parentMethod.data(gs)->loc(); + } + } + } + + addLocIfExists(gs, locations, loc); } start = start->secondary.get(); } diff --git a/main/lsp/requests/document_formatting.cc b/main/lsp/requests/document_formatting.cc index b0cad00612..e78cdfeb41 100644 --- a/main/lsp/requests/document_formatting.cc +++ b/main/lsp/requests/document_formatting.cc @@ -30,12 +30,12 @@ enum RubyfmtStatus { // rubyfmt and you should report a bug with the file that crashed rubyfmt RIPPER_PARSE_FAILURE = 2, - // an error occured during IO within the function, should be impossible + // an error occurred during IO within the function, should be impossible // and most likely indicates a programming error within rubyfmt, please // file a bug IO_ERROR = 3, - // some unknown ruby error occured during execution fo Rubyfmt. This indicates + // some unknown ruby error occurred during execution of Rubyfmt. This indicates // a programming error. Please file a bug report and terminate the process // and restart. OTHER_RUBY_ERROR = 4, diff --git a/main/lsp/requests/document_highlight.cc b/main/lsp/requests/document_highlight.cc index 071caefd7c..62d1e79461 100644 --- a/main/lsp/requests/document_highlight.cc +++ b/main/lsp/requests/document_highlight.cc @@ -1,6 +1,7 @@ #include "main/lsp/requests/document_highlight.h" #include "absl/strings/match.h" #include "core/lsp/QueryResponse.h" +#include "main/lsp/LSPLoop.h" #include "main/lsp/LSPQuery.h" #include "main/lsp/json_types.h" @@ -59,12 +60,13 @@ unique_ptr DocumentHighlightTask::runRequest(LSPTypecheckerDele return response; } const bool fileIsTyped = file.data(gs).strictLevel >= core::StrictLevel::True; - auto resp = move(queryResponses[0]); - // N.B.: Ignores literals. + + auto resp = getQueryResponseForFindAllReferences(queryResponses); + // If file is untyped, only supports find reference requests from constants and class definitions. if (auto constResp = resp->isConstant()) { - response->result = - getHighlights(typechecker, getReferencesToSymbolInFile(typechecker, fref, constResp->symbol)); + response->result = getHighlights( + typechecker, getReferencesToSymbolInFile(typechecker, fref, constResp->symbolBeforeDealias)); } else if (auto fieldResp = resp->isField()) { // This could be a `prop` or `attr_*`, which have multiple associated symbols. response->result = getHighlights( diff --git a/main/lsp/requests/document_symbol.cc b/main/lsp/requests/document_symbol.cc index b9afaec2a0..3b986b4f6e 100644 --- a/main/lsp/requests/document_symbol.cc +++ b/main/lsp/requests/document_symbol.cc @@ -193,12 +193,6 @@ bool DocumentSymbolTask::isDelayable() const { unique_ptr DocumentSymbolTask::runRequest(LSPTypecheckerDelegate &typechecker) { auto response = make_unique("2.0", id, LSPMethod::TextDocumentDocumentSymbol); - if (!config.opts.lspDocumentSymbolEnabled) { - response->error = - make_unique((int)LSPErrorCodes::InvalidRequest, - "The `Document Symbol` LSP feature is experimental and disabled by default."); - return response; - } const core::GlobalState &gs = typechecker.state(); vector> result; diff --git a/main/lsp/requests/hover.cc b/main/lsp/requests/hover.cc index 1f898ccbe4..2405278c2e 100644 --- a/main/lsp/requests/hover.cc +++ b/main/lsp/requests/hover.cc @@ -3,6 +3,7 @@ #include "absl/strings/str_join.h" #include "common/sort/sort.h" #include "core/lsp/QueryResponse.h" +#include "core/source_generator/source_generator.h" #include "main/lsp/LSPLoop.h" #include "main/lsp/LSPQuery.h" #include "main/lsp/json_types.h" @@ -12,8 +13,8 @@ using namespace std; namespace sorbet::realmain::lsp { string methodInfoString(const core::GlobalState &gs, const core::TypePtr &retType, - const core::DispatchResult &dispatchResult, - const unique_ptr &constraint) { + const core::DispatchResult &dispatchResult, const unique_ptr &constraint, + const core::ShowOptions options) { string contents; auto start = &dispatchResult; ; @@ -23,8 +24,9 @@ string methodInfoString(const core::GlobalState &gs, const core::TypePtr &retTyp if (!contents.empty()) { contents += "\n"; } - contents = absl::StrCat( - contents, prettyTypeForMethod(gs, component.method, component.receiver, retType, constraint.get())); + contents = absl::StrCat(contents, core::source_generator::prettyTypeForMethod(gs, component.method, + component.receiver, retType, + constraint.get(), options)); } start = start->secondary.get(); } @@ -40,7 +42,7 @@ unique_ptr HoverTask::runRequest(LSPTypecheckerDelegate &typech const core::GlobalState &gs = typechecker.state(); auto result = LSPQuery::byLoc(config, typechecker, params->textDocument->uri, *params->position, - LSPMethod::TextDocumentHover); + LSPMethod::TextDocumentHover, false); if (result.error) { // An error happened while setting up the query. response->error = move(result.error); @@ -54,9 +56,11 @@ unique_ptr HoverTask::runRequest(LSPTypecheckerDelegate &typech ENFORCE(fref.exists()); auto level = fref.data(gs).strictLevel; if (level < core::StrictLevel::True) { - auto text = fmt::format("This file is `# typed: {}`.\n" - "Hover, Go To Definition, and other features are disabled in this file.", - level == core::StrictLevel::Ignore ? "ignore" : "false"); + auto text = level == core::StrictLevel::Ignore + ? "This file is `# typed: ignore`.\n" + "No Sorbet IDE features will work in this file." + : "This file is `# typed: false`.\n" + "Most Hover results will not appear until the file is `# typed: true` or higher."; response->result = make_unique(make_unique(clientHoverMarkupKind, text)); } else { // Note: Need to specifically specify the variant type here so the null gets placed into the proper slot. @@ -66,6 +70,7 @@ unique_ptr HoverTask::runRequest(LSPTypecheckerDelegate &typech } auto resp = move(queryResponses[0]); + auto options = core::ShowOptions(); vector documentationLocations; string typeString; @@ -78,11 +83,19 @@ unique_ptr HoverTask::runRequest(LSPTypecheckerDelegate &typech } } } else if (auto c = resp->isConstant()) { - for (auto loc : c->symbol.locs(gs)) { + for (auto loc : c->symbolBeforeDealias.locs(gs)) { if (loc.exists()) { documentationLocations.emplace_back(loc); } } + auto dealiased = c->symbolBeforeDealias.dealias(gs); + if (dealiased != c->symbolBeforeDealias) { + for (auto loc : dealiased.locs(gs)) { + if (loc.exists()) { + documentationLocations.emplace_back(loc); + } + } + } } else if (auto d = resp->isMethodDef()) { for (auto loc : d->symbol.data(gs)->locs()) { if (loc.exists()) { @@ -110,12 +123,13 @@ unique_ptr HoverTask::runRequest(LSPTypecheckerDelegate &typech // the result type. typeString = retType.showWithMoreInfo(gs); } else { - typeString = methodInfoString(gs, retType, *sendResp->dispatchResult, constraint); + typeString = methodInfoString(gs, retType, *sendResp->dispatchResult, constraint, options); } } else if (auto defResp = resp->isMethodDef()) { - typeString = prettyTypeForMethod(gs, defResp->symbol, nullptr, defResp->retType.type, nullptr); + typeString = core::source_generator::prettyTypeForMethod(gs, defResp->symbol, nullptr, defResp->retType.type, + nullptr, options); } else if (auto constResp = resp->isConstant()) { - typeString = prettyTypeForConstant(gs, constResp->symbol); + typeString = prettyTypeForConstant(gs, constResp->symbolBeforeDealias); } else { core::TypePtr retType = resp->getRetType(); // Some untyped arguments have null types. diff --git a/main/lsp/requests/implementation.cc b/main/lsp/requests/implementation.cc index dfc39d1880..85dcca4c83 100644 --- a/main/lsp/requests/implementation.cc +++ b/main/lsp/requests/implementation.cc @@ -27,7 +27,8 @@ unique_ptr makeInvalidRequestError(core::SymbolRef symbol, const const MethodImplementationResults findMethodImplementations(const core::GlobalState &gs, core::MethodRef method) { MethodImplementationResults res; - if (!method.data(gs)->flags.isAbstract) { + auto flags = method.data(gs)->flags; + if (!flags.isAbstract && !flags.isOverridable) { res.error = makeInvalidRequestError(method, gs); return res; } @@ -47,7 +48,7 @@ const MethodImplementationResults findMethodImplementations(const core::GlobalSt return res; } -core::MethodRef findOverridedMethod(const core::GlobalState &gs, const core::MethodRef method) { +core::MethodRef findOverriddenMethod(const core::GlobalState &gs, const core::MethodRef method) { auto ownerClass = method.data(gs)->owner; for (auto mixin : ownerClass.data(gs)->mixins()) { @@ -82,18 +83,12 @@ unique_ptr ImplementationTask::runRequest(LSPTypecheckerDelegat auto queryResponse = move(queryResult.responses[0]); if (auto def = queryResponse->isMethodDef()) { // User called "Go to Implementation" from the abstract function definition - core::SymbolRef maybeMethod = def->symbol; - if (!maybeMethod.isMethod()) { - response->error = makeInvalidRequestError(maybeMethod, gs); - return response; - } - - auto method = maybeMethod.asMethodRef(); - core::MethodRef overridedMethod = method; + auto method = def->symbol; + core::MethodRef overriddenMethod = method; if (method.data(gs)->flags.isOverride) { - overridedMethod = findOverridedMethod(gs, method); + overriddenMethod = findOverriddenMethod(gs, method); } - auto locationsOrError = findMethodImplementations(gs, overridedMethod); + auto locationsOrError = findMethodImplementations(gs, overriddenMethod); if (locationsOrError.error != nullptr) { response->error = move(locationsOrError.error); @@ -105,7 +100,7 @@ unique_ptr ImplementationTask::runRequest(LSPTypecheckerDelegat } } else if (auto constant = queryResponse->isConstant()) { // User called "Go to Implementation" from the abstract class reference - auto classSymbol = constant->symbol.asClassOrModuleRef(); + auto classSymbol = constant->symbolBeforeDealias.dealias(gs).asClassOrModuleRef(); if (!classSymbol.data(gs)->flags.isAbstract) { response->error = makeInvalidRequestError(classSymbol, gs); @@ -130,12 +125,12 @@ unique_ptr ImplementationTask::runRequest(LSPTypecheckerDelegat } auto calledMethod = mainResponse.method; - auto overridedMethod = calledMethod; + auto overriddenMethod = calledMethod; if (calledMethod.data(gs)->flags.isOverride) { - overridedMethod = findOverridedMethod(gs, overridedMethod); + overriddenMethod = findOverriddenMethod(gs, overriddenMethod); } - auto locationsOrError = findMethodImplementations(gs, overridedMethod); + auto locationsOrError = findMethodImplementations(gs, overriddenMethod); if (locationsOrError.error != nullptr) { response->error = move(locationsOrError.error); diff --git a/main/lsp/requests/initialize.cc b/main/lsp/requests/initialize.cc index 2c85bb1f06..4daed68fdc 100644 --- a/main/lsp/requests/initialize.cc +++ b/main/lsp/requests/initialize.cc @@ -25,7 +25,7 @@ unique_ptr InitializeTask::runRequest(LSPTypecheckerDelegate &t serverCap->textDocumentSync = TextDocumentSyncKind::Full; serverCap->definitionProvider = true; serverCap->typeDefinitionProvider = true; - serverCap->documentSymbolProvider = opts.lspDocumentSymbolEnabled; + serverCap->documentSymbolProvider = true; serverCap->workspaceSymbolProvider = true; serverCap->documentHighlightProvider = opts.lspDocumentHighlightEnabled; serverCap->hoverProvider = true; diff --git a/main/lsp/requests/references.cc b/main/lsp/requests/references.cc index 64893cf1e1..3c209c4b2f 100644 --- a/main/lsp/requests/references.cc +++ b/main/lsp/requests/references.cc @@ -1,5 +1,6 @@ #include "main/lsp/requests/references.h" #include "core/lsp/QueryResponse.h" +#include "main/lsp/LSPLoop.h" #include "main/lsp/LSPOutput.h" #include "main/lsp/LSPQuery.h" #include "main/lsp/ShowOperation.h" @@ -19,7 +20,7 @@ bool ReferencesTask::needsMultithreading(const LSPIndexer &indexer) const { vector ReferencesTask::getSymsToCheckWithinPackage(const core::GlobalState &gs, core::SymbolRef symInPackage, - core::NameRef packageName) { + core::packages::MangledName packageName) { std::vector fullName; auto sym = symInPackage; @@ -30,8 +31,10 @@ vector ReferencesTask::getSymsToCheckWithinPackage(const core:: reverse(fullName.begin(), fullName.end()); vector result; - vector namespacesToCheck = {core::Symbols::root(), - core::Symbols::root().data(gs)->findMember(gs, packager::TEST_NAME)}; + vector namespacesToCheck = { + core::Symbols::root(), + core::Symbols::root().data(gs)->findMember(gs, core::packages::PackageDB::TEST_NAMESPACE), + }; for (auto &namespaceToCheck : namespacesToCheck) { if (!namespaceToCheck.exists()) { @@ -86,8 +89,8 @@ unique_ptr ReferencesTask::runRequest(LSPTypecheckerDelegate &t fileIsTyped = fref.data(gs).strictLevel >= core::StrictLevel::True; } if (!queryResponses.empty()) { - auto resp = move(queryResponses[0]); - // N.B.: Ignores literals. + auto resp = getQueryResponseForFindAllReferences(queryResponses); + // If file is untyped, only supports find reference requests from constants and class definitions. if (auto constResp = resp->isConstant()) { if (fref.data(gs).isPackage()) { @@ -117,7 +120,7 @@ unique_ptr ReferencesTask::runRequest(LSPTypecheckerDelegate &t // Returns all global usages of Foo::A auto packageName = gs.packageDB().getPackageNameForFile(fref); - auto symsToCheck = getSymsToCheckWithinPackage(gs, constResp->symbol, packageName); + auto symsToCheck = getSymsToCheckWithinPackage(gs, constResp->symbolBeforeDealias, packageName); if (!symsToCheck.empty()) { std::vector> locations; @@ -134,13 +137,13 @@ unique_ptr ReferencesTask::runRequest(LSPTypecheckerDelegate &t } else { // Fall back to normal case when we are not querying for an external symbol, e.g. class Foo < // PackageSpec declarations, or export statements. - response->result = - extractLocations(typechecker.state(), getReferencesToSymbol(typechecker, constResp->symbol)); + response->result = extractLocations( + typechecker.state(), getReferencesToSymbol(typechecker, constResp->symbolBeforeDealias)); } } else { // Normal handling for non-package files - response->result = - extractLocations(typechecker.state(), getReferencesToSymbol(typechecker, constResp->symbol)); + response->result = extractLocations(typechecker.state(), + getReferencesToSymbol(typechecker, constResp->symbolBeforeDealias)); } } else if (auto fieldResp = resp->isField()) { // This could be a `prop` or `attr_*`, which have multiple associated symbols. diff --git a/main/lsp/requests/references.h b/main/lsp/requests/references.h index 3fbb5a14e3..c281085c16 100644 --- a/main/lsp/requests/references.h +++ b/main/lsp/requests/references.h @@ -8,7 +8,7 @@ class ReferenceParams; class ReferencesTask final : public LSPRequestTask { std::unique_ptr params; std::vector getSymsToCheckWithinPackage(const core::GlobalState &gs, core::SymbolRef symInPackage, - core::NameRef packageName); + core::packages::MangledName packageName); core::SymbolRef findSym(const core::GlobalState &gs, const std::vector &fullName, core::SymbolRef underNamespace); diff --git a/main/lsp/requests/rename.cc b/main/lsp/requests/rename.cc index c183fcf8f0..c9fd6ffd57 100644 --- a/main/lsp/requests/rename.cc +++ b/main/lsp/requests/rename.cc @@ -116,7 +116,7 @@ class MethodRenamer : public AbstractRewriter { string newsrc; if (auto sendResp = response->isSend()) { newsrc = replaceMethodNameInSend(string(source.value()), sendResp); - } else if (auto defResp = response->isMethodDef()) { + } else if (response->isMethodDef()) { newsrc = replaceMethodNameInDef(string(source.value())); } else { ENFORCE(0, "Unexpected query response type while renaming method"); diff --git a/main/lsp/requests/signature_help.cc b/main/lsp/requests/signature_help.cc index 288d3e9b6f..51d8212200 100644 --- a/main/lsp/requests/signature_help.cc +++ b/main/lsp/requests/signature_help.cc @@ -1,5 +1,6 @@ #include "main/lsp/requests/signature_help.h" #include "core/lsp/QueryResponse.h" +#include "core/source_generator/source_generator.h" #include "main/lsp/LSPLoop.h" #include "main/lsp/LSPQuery.h" #include "main/lsp/json_types.h" @@ -15,7 +16,7 @@ void addSignatureHelpItem(const core::GlobalState &gs, core::MethodRef method, return; } // Label is mandatory, so method name (i.e B#add) is shown for now. Might want to add markup highlighting - // wtih respect to activeParameter here. + // with respect to activeParameter here. auto sig = make_unique(method.show(gs)); vector> parameters; @@ -37,9 +38,10 @@ void addSignatureHelpItem(const core::GlobalState &gs, core::MethodRef method, if (i != args.size() - 1) { methodDocumentation += ", "; } - parameter->documentation = getResultType(gs, arg.type, method, resp.dispatchResult->main.receiver, - resp.dispatchResult->main.constr.get()) - .show(gs); + parameter->documentation = + core::source_generator::getResultType(gs, arg.type, method, resp.dispatchResult->main.receiver, + resp.dispatchResult->main.constr.get()) + .show(gs); parameters.push_back(move(parameter)); i += 1; } diff --git a/main/lsp/requests/sorbet_show_symbol.cc b/main/lsp/requests/sorbet_show_symbol.cc index 2a291e8935..ecb7c9cad7 100644 --- a/main/lsp/requests/sorbet_show_symbol.cc +++ b/main/lsp/requests/sorbet_show_symbol.cc @@ -17,9 +17,9 @@ unique_ptr SorbetShowSymbolTask::runRequest(LSPTypecheckerDeleg const core::GlobalState &gs = typechecker.state(); // To match the behavior of Go To Definition, we don't error in an untyped file, but instead // be okay with returning an empty result for certain queries. - auto errorIfFileIsUntyped = false; + auto emptyResultIfFileIsUntyped = false; auto result = LSPQuery::byLoc(config, typechecker, params->textDocument->uri, *params->position, - LSPMethod::SorbetShowSymbol, errorIfFileIsUntyped); + LSPMethod::SorbetShowSymbol, emptyResultIfFileIsUntyped); if (result.error) { // An error happened while setting up the query. response->error = move(result.error); diff --git a/main/lsp/requests/workspace_symbols.cc b/main/lsp/requests/workspace_symbols.cc index 7c47b09c40..088dc2bceb 100644 --- a/main/lsp/requests/workspace_symbols.cc +++ b/main/lsp/requests/workspace_symbols.cc @@ -34,7 +34,7 @@ class SymbolMatcher final { vector> doQuery(string_view query, size_t maxResults = MAX_RESULTS); private: - vector> symbolRef2SymbolInformations(core::SymbolRef symRef, size_t maxLocations); + vector> symbolRef2SymbolInformation(core::SymbolRef symRef, size_t maxLocations); const LSPConfiguration &config; const core::GlobalState &gs; @@ -71,8 +71,8 @@ SymbolMatcher::SymbolMatcher(const LSPConfiguration &config, const core::GlobalS /** * Converts a symbol into any (supported) SymbolInformation objects. */ -vector> SymbolMatcher::symbolRef2SymbolInformations(core::SymbolRef symRef, - size_t maxLocations) { +vector> SymbolMatcher::symbolRef2SymbolInformation(core::SymbolRef symRef, + size_t maxLocations) { vector> results; for (auto loc : symRef.locs(gs)) { if (results.size() >= maxLocations) { @@ -315,7 +315,7 @@ vector> SymbolMatcher::doQuery(string_view query_v for (auto &candidate : candidates) { auto ref = candidate.first; auto maxLocations = min(MAX_LOCATIONS_PER_SYMBOL, maxResults - results.size()); - for (auto &symbolInformation : symbolRef2SymbolInformations(ref, maxLocations)) { + for (auto &symbolInformation : symbolRef2SymbolInformation(ref, maxLocations)) { results.emplace_back(move(symbolInformation)); } if (results.size() >= maxResults) { diff --git a/main/lsp/tools/generate_lsp_messages.cc b/main/lsp/tools/generate_lsp_messages.cc index 759c5006ab..3fe369feaf 100644 --- a/main/lsp/tools/generate_lsp_messages.cc +++ b/main/lsp/tools/generate_lsp_messages.cc @@ -41,6 +41,9 @@ int main(int argc, char **argv) { std::vector enumClassFileBuffer(enumSource.size()); for (auto &buffer : enumClassFileBuffer) { + fmt::format_to(std::back_inserter(buffer), "#pragma GCC diagnostic push\n"); + fmt::format_to(std::back_inserter(buffer), + "#pragma GCC diagnostic ignored \"-Wunused-but-set-variable\"\n"); fmt::format_to(std::back_inserter(buffer), "#include \"main/lsp/json_types.h\"\n"); fmt::format_to(std::back_inserter(buffer), "#include \"main/lsp/lsp_messages_gen_helpers.h\"\n"); fmt::format_to(std::back_inserter(buffer), "namespace sorbet::realmain::lsp {{\n"); @@ -55,6 +58,7 @@ int main(int argc, char **argv) { for (auto &buffer : enumClassFileBuffer) { fmt::format_to(std::back_inserter(buffer), "}}\n"); + fmt::format_to(std::back_inserter(buffer), "#pragma GCC diagnostic pop\n"); } if (!writeFile(enumHeader, enumHeaderBuffer)) { @@ -76,6 +80,9 @@ int main(int argc, char **argv) { std::vector classFileBuffer(msgSources.size()); for (auto &buffer : classFileBuffer) { + fmt::format_to(std::back_inserter(buffer), "#pragma GCC diagnostic push\n"); + fmt::format_to(std::back_inserter(buffer), + "#pragma GCC diagnostic ignored \"-Wunused-but-set-variable\"\n"); fmt::format_to(std::back_inserter(buffer), "#include \"main/lsp/json_types.h\"\n"); fmt::format_to(std::back_inserter(buffer), "#include \"main/lsp/lsp_messages_gen_helpers.h\"\n"); fmt::format_to(std::back_inserter(buffer), "namespace sorbet::realmain::lsp {{\n"); @@ -89,6 +96,7 @@ int main(int argc, char **argv) { for (auto &buffer : classFileBuffer) { fmt::format_to(std::back_inserter(buffer), "}}\n"); + fmt::format_to(std::back_inserter(buffer), "#pragma GCC diagnostic pop\n"); } // Output buffers to files. diff --git a/main/lsp/tools/generate_lsp_messages.h b/main/lsp/tools/generate_lsp_messages.h index 4a3dc9545b..d2307206c9 100644 --- a/main/lsp/tools/generate_lsp_messages.h +++ b/main/lsp/tools/generate_lsp_messages.h @@ -393,7 +393,7 @@ class JSONArrayType final : public JSONType { // context. fmt::format_to(std::back_inserter(out), "{{\n"); fmt::format_to(std::back_inserter(out), "rapidjson::Value {}(rapidjson::kArrayType);\n", arrayVar); - fmt::format_to(std::back_inserter(out), "for (auto &element : {}) {{\n", from); + fmt::format_to(std::back_inserter(out), "for (const auto &element : {}) {{\n", from); componentType->emitToJSONValue(out, "element", AssignSerializedElementValue, fieldName); fmt::format_to(std::back_inserter(out), "}}\n"); assign(out, arrayVar); diff --git a/main/lsp/tools/make_lsp_types.cc b/main/lsp/tools/make_lsp_types.cc index f0fb0abec4..4eeee198e3 100644 --- a/main/lsp/tools/make_lsp_types.cc +++ b/main/lsp/tools/make_lsp_types.cc @@ -175,16 +175,15 @@ void makeLSPTypes(vector> &enumTypes, vector> &enumTypes, vector> &enumTypes, vector> &enumTypes, vector> &enumTypes, vector> &enumTypes, vector logger, string_view watchmanPath, string_view workSpace, - vector extensions) +WatchmanProcess::WatchmanProcess(std::shared_ptr logger, std::string_view watchmanPath, + std::string_view workSpace, std::vector extensions, + MessageQueueState &messageQueue, absl::Mutex &messageQueueMutex, + absl::Notification &initializedNotification, + std::shared_ptr config) : logger(std::move(logger)), watchmanPath(string(watchmanPath)), workSpace(string(workSpace)), extensions(std::move(extensions)), - thread(runInAThread("watchmanReader", std::bind(&WatchmanProcess::start, this))) {} + thread(runInAThread("watchmanReader", std::bind(&WatchmanProcess::start, this))), messageQueue(messageQueue), + messageQueueMutex(messageQueueMutex), initializedNotification(initializedNotification), + config(std::move(config)) {} WatchmanProcess::~WatchmanProcess() { exitWithCode(0, ""); @@ -154,4 +162,47 @@ void WatchmanProcess::exitWithCode(int code, const std::optional &m } } +void WatchmanProcess::enqueueNotification(std::unique_ptr notification) { + auto msg = make_unique(move(notification)); + // Don't start enqueueing requests until LSP is initialized. + initializedNotification.WaitForNotification(); + { + absl::MutexLock lck(&messageQueueMutex); + msg->tagNewRequest(*logger); + messageQueue.counters = mergeCounters(move(messageQueue.counters)); + messageQueue.pendingRequests.push_back(move(msg)); + } +} + +void WatchmanProcess::processQueryResponse(std::unique_ptr response) { + auto notifMsg = make_unique("2.0", LSPMethod::SorbetWatchmanFileChange, move(response)); + enqueueNotification(move(notifMsg)); +} + +void WatchmanProcess::processStateEnter(std::unique_ptr stateEnter) { + auto notification = make_unique("2.0", LSPMethod::SorbetWatchmanStateEnter, move(stateEnter)); + enqueueNotification(move(notification)); +} + +void WatchmanProcess::processStateLeave(std::unique_ptr stateLeave) { + auto notification = make_unique("2.0", LSPMethod::SorbetWatchmanStateLeave, move(stateLeave)); + enqueueNotification(move(notification)); +} + +void WatchmanProcess::processExit(int watchmanExitCode, const std::optional &msg) { + { + absl::MutexLock lck(&messageQueueMutex); + if (!messageQueue.terminate) { + messageQueue.terminate = true; + messageQueue.errorCode = watchmanExitCode; + if (watchmanExitCode != 0 && msg.has_value()) { + auto params = make_unique(MessageType::Error, msg.value()); + config->output->write(make_unique( + make_unique("2.0", LSPMethod::WindowShowMessage, move(params)))); + } + } + logger->debug("Watchman terminating"); + } +} + } // namespace sorbet::realmain::lsp::watchman diff --git a/main/lsp/watchman/WatchmanProcess.h b/main/lsp/watchman/WatchmanProcess.h index fbc013fecc..dbd9d6f108 100644 --- a/main/lsp/watchman/WatchmanProcess.h +++ b/main/lsp/watchman/WatchmanProcess.h @@ -2,14 +2,18 @@ #define RUBY_TYPER_LSP_WATCHMAN_WATCHMANPROCESS_H #include "absl/synchronization/mutex.h" +#include "absl/synchronization/notification.h" #include "common/common.h" #include "core/core.h" +#include "main/lsp/MessageQueueState.h" #include "spdlog/spdlog.h" namespace sorbet::realmain::lsp { class WatchmanQueryResponse; class WatchmanStateEnter; class WatchmanStateLeave; +class LSPConfiguration; +class NotificationMessage; } // namespace sorbet::realmain::lsp namespace sorbet::realmain::lsp::watchman { @@ -27,6 +31,11 @@ class WatchmanProcess { // If true, the process has been stopped. bool stopped = false; + MessageQueueState &messageQueue; + absl::Mutex &messageQueueMutex; + absl::Notification &initializedNotification; + const std::shared_ptr config; + /** * Starts up a Watchman subprocess and begins processing file changes. Runs in a dedicated thread. */ @@ -36,14 +45,15 @@ class WatchmanProcess { bool isStopped(); -protected: - virtual void processQueryResponse(std::unique_ptr) = 0; + void enqueueNotification(std::unique_ptr notification); + + void processQueryResponse(std::unique_ptr); - virtual void processStateEnter(std::unique_ptr) = 0; + void processStateEnter(std::unique_ptr); - virtual void processStateLeave(std::unique_ptr) = 0; + void processStateLeave(std::unique_ptr); - virtual void processExit(int core, const std::optional &) = 0; + void processExit(int core, const std::optional &); public: /** @@ -51,9 +61,11 @@ class WatchmanProcess { * workspace folder. Passes file updates to `processUpdate` function. */ WatchmanProcess(std::shared_ptr logger, std::string_view watchmanPath, std::string_view workSpace, - std::vector extensions); + std::vector extensions, MessageQueueState &messageQueue, + absl::Mutex &messageQueueMutex, absl::Notification &initializedNotification, + std::shared_ptr config); - virtual ~WatchmanProcess(); + ~WatchmanProcess(); WatchmanProcess(const WatchmanProcess &&) = delete; WatchmanProcess(WatchmanProcess &) = delete; diff --git a/main/lsp/wrapper.cc b/main/lsp/wrapper.cc index 88a6fa60c7..2f468c06f0 100644 --- a/main/lsp/wrapper.cc +++ b/main/lsp/wrapper.cc @@ -30,6 +30,8 @@ void setRequiredLSPOptions(core::GlobalState &gs, options::Options &options) { gs.requiresAncestorEnabled = options.requiresAncestorEnabled; gs.ruby3KeywordArgs = options.ruby3KeywordArgs; + gs.typedSuper = options.typedSuper; + gs.suppressPayloadSuperclassRedefinitionFor = options.suppressPayloadSuperclassRedefinitionFor; // Ensure LSP is enabled. options.runLSP = true; @@ -165,7 +167,6 @@ unique_ptr MultiThreadedLSPWrapper::read(int timeoutMs) { void LSPWrapper::enableAllExperimentalFeatures() { opts->lspDocumentHighlightEnabled = true; - opts->lspDocumentSymbolEnabled = true; opts->lspSignatureHelpEnabled = true; opts->lspDocumentFormatRubyfmtEnabled = true; } diff --git a/main/main.cc b/main/main.cc index 47e3b1d4af..61c3373e5a 100644 --- a/main/main.cc +++ b/main/main.cc @@ -6,8 +6,5 @@ int main(int argc, char *argv[]) { return sorbet::realmain::realmain(argc, argv); } catch (sorbet::EarlyReturnWithCode &c) { return c.returnCode; - } catch (sorbet::SorbetException &e) { - fprintf(stderr, "caught %s: %s\n", typeid(e).name(), e.what()); - return 1; } }; diff --git a/main/minimize/minimize.cc b/main/minimize/minimize.cc index a1ce7d780e..7921827a14 100644 --- a/main/minimize/minimize.cc +++ b/main/minimize/minimize.cc @@ -52,10 +52,10 @@ OutputCategory outputCategoryFromClassName(string_view fullName) { } else if (absl::StrContains(fullName, "::Mutator")) { return OutputCategory::Mutator; } else if (absl::StrContains(fullName, "::Model")) { - if (absl::StartsWith(fullName, "Plaid") || absl::StartsWith(fullName, "Algolia")) { - return OutputCategory::External; - } else { + if (absl::StartsWith(fullName, "Opus") || absl::StartsWith(fullName, "Chalk")) { return OutputCategory::Model; + } else { + return OutputCategory::External; } } else if (absl::StrContains(fullName, "::Autogen::Proto")) { return OutputCategory::AutogenProto; @@ -219,7 +219,8 @@ void serializeMethods(const core::GlobalState &sourceGS, const core::GlobalState // probably update this pass to also serialize any new type information. auto isSingleton = rbiClass.data(rbiGS)->isSingletonClass(rbiGS); - outfile.fmt(" def {}{}(", isSingleton ? "self." : "", rbiEntryShortName); + auto isPrivate = rbiEntry.data(rbiGS)->flags.isPrivate; + outfile.fmt(" {}def {}{}(", isPrivate ? "private " : "", isSingleton ? "self." : "", rbiEntryShortName); auto &rbiParameters = rbiEntry.data(rbiGS)->arguments; if (rbiParameters.size() == 3 && rbiParameters[1].name == core::Names::fwdKwargs()) { @@ -413,14 +414,19 @@ void Minimize::indexAndResolveForMinimize(unique_ptr &sourceG // I'm ignoring everything relating to caching here, because missing methods is likely // to run on a new _unknown.rbi file every time and I didn't want to think about it. // If this phase gets slow, we can consider whether caching would speed things up. - auto rbiIndexed = pipeline::index(*rbiGS, rbiInputFiles, opts, workers, nullptr); + auto rbiIndexed = pipeline::index(*rbiGS, absl::Span(rbiInputFiles), opts, workers, nullptr); if (rbiGS->hadCriticalError()) { rbiGS->errorQueue->flushAllErrors(*rbiGS); } + pipeline::setPackagerOptions(*rbiGS, opts); + pipeline::package(*rbiGS, absl::Span(rbiIndexed), opts, workers); // Only need to compute FoundDefHashes when running to compute a FileHash auto foundHashes = nullptr; - rbiIndexed = move(pipeline::resolve(rbiGS, move(rbiIndexed), opts, workers, foundHashes).result()); + auto canceled = pipeline::name(*rbiGS, absl::Span(rbiIndexed), opts, workers, foundHashes); + ENFORCE(!canceled, "Can only cancel in LSP mode"); + + rbiIndexed = move(pipeline::resolve(rbiGS, move(rbiIndexed), opts, workers).result()); if (rbiGS->hadCriticalError()) { rbiGS->errorQueue->flushAllErrors(*rbiGS); } diff --git a/main/options/options.cc b/main/options/options.cc index be228e5803..e6d4db6727 100644 --- a/main/options/options.cc +++ b/main/options/options.cc @@ -177,6 +177,21 @@ const vector stop_after_options({ {"inferencer", Phase::INFERENCER}, }); +core::TrackUntyped text2TrackUntyped(string_view key, spdlog::logger &logger) { + if (key == "") { + return core::TrackUntyped::Everywhere; + } else if (key == "nowhere") { + return core::TrackUntyped::Nowhere; + } else if (key == "everywhere-but-tests") { + return core::TrackUntyped::EverywhereButTests; + } else if (key == "everywhere") { + return core::TrackUntyped::Everywhere; + } else { + logger.error("Unknown --track-untyped option: `{}`", key); + throw EarlyReturnWithCode(1); + } +} + core::StrictLevel text2StrictLevel(string_view key, shared_ptr logger) { if (key == "ignore") { return core::StrictLevel::Ignore; @@ -277,10 +292,10 @@ buildOptions(const vector auto { return pr.option; })); + print_options, ", ", [](const auto &pr) -> auto{ return pr.option; })); fmt::format_to(std::back_inserter(all_stop_after), "Stop After: [{}]", fmt::map_join( - stop_after_options, ", ", [](const auto &pr) -> auto { return pr.option; })); + stop_after_options, ", ", [](const auto &pr) -> auto{ return pr.option; })); // Advanced options options.add_options("advanced")("dir", "Input directory", cxxopts::value>()); @@ -354,6 +369,9 @@ buildOptions(const vector>(), "string"); options.add_options("advanced")( @@ -394,14 +412,9 @@ buildOptions(const vector>(), "string"); - options.add_options("dev")( - "secondary-test-package-namespaces", - "Secondary top-level namespaces which contain test code (in addition to Test, which is primary). " - "This option must be used in conjunction with --stripe-packages", - cxxopts::value>(), "string"); - options.add_options("dev")("skip-package-import-visibility-check-for", - "Packages for which the visible_to check does not apply. They can import any package " - "regardless of visible_to annotations." + options.add_options("dev")("allow-relaxed-packager-checks-for", + "Packages which are allowed to ignore the restrictions set by `visible_to` " + "and `export` directives." "This option must be used in conjunction with --stripe-packages", cxxopts::value>(), "string"); buildAutogenCacheOptions(options); @@ -412,9 +425,17 @@ buildOptions(const vector()->default_value(empty.errorUrlBase), "url-base"); options.add_options("advanced")("experimental-ruby3-keyword-args", "Enforce use of new (Ruby 3.0-style) keyword arguments", cxxopts::value()); + options.add_options("advanced")("typed-super", "Enable typechecking of `super` calls when possible", + cxxopts::value()->default_value("true")); options.add_options("advanced")("check-out-of-order-constant-references", "Enable out-of-order constant reference checks (error 5027)"); - options.add_options("advanced")("track-untyped", "Track untyped usage statistics in the file-table output"); + options.add_options("advanced")("track-untyped", "Track untyped usage statistics in the file-table output", + cxxopts::value()->implicit_value("everywhere"), + "{[nowhere],everywhere,everywhere-but-tests}"); + options.add_options("advanced")("suppress-payload-superclass-redefinition-for", + "Explicitly suppress the superclass redefinition error for the specified class " + "defined in Sorbet's payload. May be repeated.", + cxxopts::value>(), "Fully::Qualified::ClassName"); // Developer options options.add_options("dev")("p,print", to_string(all_prints), cxxopts::value>(), "type"); @@ -431,11 +452,14 @@ buildOptions(const vector>(), "string"); + options.add_options("dev")("autogen-msgpack-skip-reference-metadata", + "Skip serializing extra metadata on references when printing msgpack in autogen", + cxxopts::value()); options.add_options("dev")("stop-after", to_string(all_stop_after), cxxopts::value()->default_value("inferencer"), "phase"); options.add_options("dev")("no-stdlib", "Do not load included rbi files for stdlib"); options.add_options("dev")("minimize-to-rbi", - "[experimental] Output a minimal RBI contining the diff between Sorbet's view of a " + "[experimental] Output a minimal RBI containing the diff between Sorbet's view of a " "codebase and the definitions present in this file", cxxopts::value()->default_value(""), ""); options.add_options("dev")("wait-for-dbg", "Wait for debugger on start"); @@ -502,15 +526,12 @@ buildOptions(const vector()->default_value(empty.statsdHost), "host"); options.add_options("dev")("counters", "Print all internal counters"); - if (sorbet::debug_mode) { - options.add_options("dev")("suggest-sig", "Report typing candidates. Only supported in debug builds"); - } options.add_options("dev")("suggest-typed", "Suggest which typed: sigils to add or upgrade"); options.add_options("dev")("suggest-unsafe", "In as many errors as possible, suggest autocorrects to wrap problem code with " ". Omit the = to default to wrapping with T.unsafe. " - "This supercedes certain autocorrects, especially T.must.", + "This supersedes certain autocorrects, especially T.must.", cxxopts::value()->implicit_value("T.unsafe"), ""); options.add_options("dev")("statsd-prefix", "StatsD prefix", cxxopts::value()->default_value(empty.statsdPrefix), "prefix"); @@ -670,6 +691,10 @@ void readOptions(Options &opts, if (raw["simulate-crash"].as()) { Exception::raise("simulated crash"); } + opts.waitForDebugger = raw["wait-for-dbg"].as(); + while (opts.waitForDebugger && !stopInDebugger()) { + // spin + } if (raw.count("allowed-extension") > 0) { auto exts = raw["allowed-extension"].as>(); @@ -728,17 +753,23 @@ void readOptions(Options &opts, bool enableAllLSPFeatures = raw["enable-all-experimental-lsp-features"].as(); opts.lspAllBetaFeaturesEnabled = enableAllLSPFeatures || raw["enable-all-beta-lsp-features"].as(); - opts.lspDocumentSymbolEnabled = - opts.lspAllBetaFeaturesEnabled || raw["enable-experimental-lsp-document-symbol"].as(); opts.lspDocumentHighlightEnabled = enableAllLSPFeatures || raw["enable-experimental-lsp-document-highlight"].as(); opts.lspSignatureHelpEnabled = enableAllLSPFeatures || raw["enable-experimental-lsp-signature-help"].as(); + opts.lspExtractToVariableEnabled = + enableAllLSPFeatures || raw["enable-experimental-lsp-extract-to-variable"].as(); opts.rubyfmtPath = raw["rubyfmt-path"].as(); - opts.lspDocumentFormatRubyfmtEnabled = - FileOps::exists(opts.rubyfmtPath) && - (enableAllLSPFeatures || raw["enable-experimental-lsp-document-formatting-rubyfmt"].as()); + if (enableAllLSPFeatures || raw["enable-experimental-lsp-document-formatting-rubyfmt"].as()) { + if (!FileOps::exists(opts.rubyfmtPath)) { + logger->error("`{}` does not exist, LSP rubyfmt integration will not be enabled", opts.rubyfmtPath); + } else { + opts.lspDocumentFormatRubyfmtEnabled = true; + } + } opts.outOfOrderReferenceChecksEnabled = raw["check-out-of-order-constant-references"].as(); - opts.trackUntyped = raw["track-untyped"].as(); + if (raw.count("track-untyped") > 0) { + opts.trackUntyped = text2TrackUntyped(raw["track-untyped"].as(), *logger); + } if (raw.count("lsp-directories-missing-from-client") > 0) { auto lspDirsMissingFromClient = raw["lsp-directories-missing-from-client"].as>(); @@ -795,6 +826,14 @@ void readOptions(Options &opts, throw EarlyReturnWithCode(1); } + if (raw.count("autogen-version") > 0) { + if (!opts.print.AutogenMsgPack.enabled) { + logger->error("`{}` must also include `{}`", "--autogen-version", "-p autogen-msgpack"); + throw EarlyReturnWithCode(1); + } + opts.autogenVersion = raw["autogen-version"].as(); + } + if (raw.count("autogen-subclasses-parent")) { if (!opts.print.AutogenSubclasses.enabled) { logger->error("autogen-subclasses-parent must be used with -p autogen-subclasses"); @@ -827,7 +866,21 @@ void readOptions(Options &opts, raw["autogen-behavior-allowed-in-rbi-files-paths"].as>(); } - if (opts.print.UntypedBlame.enabled && !opts.trackUntyped) { + opts.autogenMsgpackSkipReferenceMetadata = raw["autogen-msgpack-skip-reference-metadata"].as(); + if (opts.autogenMsgpackSkipReferenceMetadata) { + if (!opts.print.AutogenMsgPack.enabled) { + logger->error("autogen-skip-reference-metadata can only be used with -p autogen-msgpack"); + throw EarlyReturnWithCode(1); + } + + if (opts.autogenVersion < 6) { + logger->error( + "autogen-skip-reference-metadata can only be used with autogen msgpack version 6 or above"); + throw EarlyReturnWithCode(1); + } + } + + if (opts.print.UntypedBlame.enabled && opts.trackUntyped == core::TrackUntyped::Nowhere) { logger->error("-p untyped-blame: must also include --track-untyped"); throw EarlyReturnWithCode(1); } @@ -879,7 +932,6 @@ void readOptions(Options &opts, if (raw.count("suggest-unsafe") > 0) { opts.suggestUnsafe = raw["suggest-unsafe"].as(); } - opts.waitForDebugger = raw["wait-for-dbg"].as(); opts.traceLexer = raw["trace-lexer"].as(); opts.traceParser = raw["trace-parser"].as(); opts.stressIncrementalResolver = raw["stress-incremental-resolver"].as(); @@ -916,13 +968,6 @@ void readOptions(Options &opts, opts.reserveFieldTableCapacity = raw["reserve-field-table-capacity"].as(); opts.reserveTypeArgumentTableCapacity = raw["reserve-type-argument-table-capacity"].as(); opts.reserveTypeMemberTableCapacity = raw["reserve-type-member-table-capacity"].as(); - if (raw.count("autogen-version") > 0) { - if (!opts.print.AutogenMsgPack.enabled) { - logger->error("`{}` must also include `{}`", "--autogen-version", "-p autogen-msgpack"); - throw EarlyReturnWithCode(1); - } - opts.autogenVersion = raw["autogen-version"].as(); - } opts.stripeMode = raw["stripe-mode"].as(); opts.stripePackages = raw["stripe-packages"].as(); @@ -948,37 +993,19 @@ void readOptions(Options &opts, } } - if (raw.count("secondary-test-package-namespaces")) { + if (raw.count("allow-relaxed-packager-checks-for")) { if (!opts.stripePackages) { - logger->error("--secondary-test-package-namespaces can only be specified in --stripe-packages mode"); - throw EarlyReturnWithCode(1); - } - std::regex nsValid("[A-Z][a-zA-Z0-9]+"); - for (const string &ns : raw["secondary-test-package-namespaces"].as>()) { - if (!std::regex_match(ns, nsValid)) { - logger->error("--secondary-test-package-namespaces must contain items that start with a capital " - "letter and are alphanumeric."); - throw EarlyReturnWithCode(1); - } - opts.secondaryTestPackageNamespaces.emplace_back(ns); - } - } - - if (raw.count("skip-package-import-visibility-check-for")) { - if (!opts.stripePackages) { - logger->error( - "--skip-package-import-visibility-check-for can only be specified in --stripe-packages mode"); + logger->error("--allow-relaxed-packager-checks-for can only be specified in --stripe-packages mode"); throw EarlyReturnWithCode(1); } std::regex nsValid("[A-Z][a-zA-Z0-9:]+"); - for (const string &ns : raw["skip-package-import-visibility-check-for"].as>()) { + for (const string &ns : raw["allow-relaxed-packager-checks-for"].as>()) { if (!std::regex_match(ns, nsValid)) { - logger->error( - "--skip-package-import-visibility-check-for must contain items that start with a capital " - "letter and are alphanumeric."); + logger->error("--allow-relaxed-packager-checks-for must contain items that start with a capital " + "letter and are alphanumeric."); throw EarlyReturnWithCode(1); } - opts.skipPackageImportVisibilityCheckFor.emplace_back(ns); + opts.allowRelaxedPackagerChecksFor.emplace_back(ns); } } @@ -1039,6 +1066,14 @@ void readOptions(Options &opts, opts.errorUrlBase = raw["error-url-base"].as(); opts.noErrorSections = raw["no-error-sections"].as(); opts.ruby3KeywordArgs = raw["experimental-ruby3-keyword-args"].as(); + opts.typedSuper = raw["typed-super"].as(); + + if (raw.count("suppress-payload-superclass-redefinition-for") > 0) { + for (auto childClassName : raw["suppress-payload-superclass-redefinition-for"].as>()) { + opts.suppressPayloadSuperclassRedefinitionFor.emplace_back(childClassName); + } + } + if (raw.count("error-white-list") > 0) { logger->error("`{}` is deprecated; please use `{}` instead", "--error-white-list", "--isolate-error-code"); auto rawList = raw["error-white-list"].as>(); @@ -1064,10 +1099,6 @@ void readOptions(Options &opts, throw EarlyReturnWithCode(1); } - if (sorbet::debug_mode) { - opts.suggestSig = raw["suggest-sig"].as(); - } - if (raw.count("e") == 0 && opts.inputFileNames.empty() && !raw["version"].as() && !opts.runLSP && opts.storeState.empty() && !opts.print.PayloadSources.enabled) { logger->error("You must pass either `{}` or at least one folder or ruby file.\n\n{}", "-e", diff --git a/main/options/options.h b/main/options/options.h index 8612f6523e..5edaa6f12a 100644 --- a/main/options/options.h +++ b/main/options/options.h @@ -5,6 +5,7 @@ #include "common/common.h" #include "common/strings/ConstExprStr.h" #include "core/StrictLevel.h" +#include "core/TrackUntyped.h" #include "main/pipeline/semantic_extension/SemanticExtension.h" #include "spdlog/spdlog.h" #include @@ -143,7 +144,6 @@ struct Options { bool unsilenceErrors = false; bool logRecordedFilepaths = false; bool silenceDevMessage = false; - bool suggestSig = false; bool suppressNonCriticalErrors = false; bool runLSP = false; bool disableWatchman = false; @@ -167,8 +167,7 @@ struct Options { std::string stripePackagesHint = ""; std::vector extraPackageFilesDirectoryUnderscorePrefixes; std::vector extraPackageFilesDirectorySlashPrefixes; - std::vector secondaryTestPackageNamespaces; - std::vector skipPackageImportVisibilityCheckFor; + std::vector allowRelaxedPackagerChecksFor; std::string typedSource = ""; std::string cacheDir = ""; // This configured both maximum filesystem db size and max virtual memory usage @@ -179,6 +178,8 @@ struct Options { bool enableCounters = false; std::string errorUrlBase = "https://srb.help/"; bool ruby3KeywordArgs = false; + bool typedSuper = true; + std::vector suppressPayloadSuperclassRedefinitionFor; std::set isolateErrorCode; std::set suppressErrorCode; bool noErrorSections = false; @@ -238,6 +239,9 @@ struct Options { std::vector autogenSubclassesRelativeIgnorePatterns; // Allow RBI files to define behavior if they are in one of these paths. std::vector autogenBehaviorAllowedInRBIFilesPaths; + // When set, msgpack serialization of references skips extra metadata like inheritance information and expression + // ranges. + bool autogenMsgpackSkipReferenceMetadata; AutogenConstCacheConfig autogenConstantCacheConfig; // List of directories not available editor-side. References to files in these directories should be sent via @@ -249,12 +253,12 @@ struct Options { bool lspAllBetaFeaturesEnabled = false; // Booleans enabling various experimental LSP features. Each will be removed once corresponding feature stabilizes. bool lspDocumentHighlightEnabled = false; - bool lspDocumentSymbolEnabled = false; bool lspDocumentFormatRubyfmtEnabled = false; bool lspSignatureHelpEnabled = false; + bool lspExtractToVariableEnabled = false; // Enables out-of-order reference checking bool outOfOrderReferenceChecksEnabled = false; - bool trackUntyped = false; + core::TrackUntyped trackUntyped = core::TrackUntyped::Nowhere; // Experimental feature `requires_ancestor` bool requiresAncestorEnabled = false; @@ -266,7 +270,7 @@ struct Options { // Path to an RBI whose contents should be minimized by subtracting parts that Sorbet already // has a record of in its own GlobalState. // - // Used primarily by missing_methods.rb / `srb rbi hiddden-definitions` to minimize an RBI file + // Used primarily by missing_methods.rb / `srb rbi hidden-definitions` to minimize an RBI file // generated by requiring a codebase and inspecting it via reflection. std::string minimizeRBI; diff --git a/main/options/test/options_test.cc b/main/options/test/options_test.cc index 4b72b0ab8a..fac7a5da18 100644 --- a/main/options/test/options_test.cc +++ b/main/options/test/options_test.cc @@ -28,7 +28,6 @@ TEST_CASE("DefaultConstructorMatchesReadOptions") { CHECK_EQ(empty.unsilenceErrors, opts.unsilenceErrors); CHECK_EQ(empty.logRecordedFilepaths, opts.logRecordedFilepaths); CHECK_EQ(empty.silenceDevMessage, opts.silenceDevMessage); - CHECK_EQ(empty.suggestSig, opts.suggestSig); CHECK_EQ(empty.suppressNonCriticalErrors, opts.suppressNonCriticalErrors); CHECK_EQ(empty.runLSP, opts.runLSP); CHECK_EQ(empty.disableWatchman, opts.disableWatchman); @@ -70,7 +69,6 @@ TEST_CASE("DefaultConstructorMatchesReadOptions") { CHECK_EQ(empty.absoluteIgnorePatterns.size(), opts.absoluteIgnorePatterns.size()); CHECK_EQ(empty.relativeIgnorePatterns.size(), opts.relativeIgnorePatterns.size()); CHECK_EQ(empty.inputFileNames.size(), opts.inputFileNames.size()); - CHECK_EQ(empty.lspDocumentSymbolEnabled, opts.lspDocumentSymbolEnabled); CHECK_EQ(empty.lspDocumentHighlightEnabled, opts.lspDocumentHighlightEnabled); CHECK_EQ(empty.lspSignatureHelpEnabled, opts.lspSignatureHelpEnabled); CHECK_EQ(empty.lspDocumentFormatRubyfmtEnabled, opts.lspDocumentFormatRubyfmtEnabled); diff --git a/main/pipeline/pipeline.cc b/main/pipeline/pipeline.cc index 73bc29cb1b..4572ad711a 100644 --- a/main/pipeline/pipeline.cc +++ b/main/pipeline/pipeline.cc @@ -55,14 +55,19 @@ class CFGCollectorAndTyper { public: CFGCollectorAndTyper(const options::Options &opts) : opts(opts){}; + void postTransformClassDef(core::Context ctx, ast::ExpressionPtr &tree) { + auto &c = ast::cast_tree_nonnull(tree); + for (auto &extension : ctx.state.semanticExtensions) { + extension->typecheckClass(ctx, ctx.file, c); + } + } + void preTransformMethodDef(core::Context ctx, ast::ExpressionPtr &tree) { auto &m = ast::cast_tree_nonnull(tree); - // With scip-ruby, we might as well try making progress with untyped code. - auto minStrictLevel = ctx.state.isSCIPRuby ? core::StrictLevel::False : core::StrictLevel::True; - if (ctx.file.data(ctx).strictLevel < minStrictLevel || m.symbol.data(ctx)->flags.isOverloaded || - (m.symbol.data(ctx)->flags.isAbstract && ctx.file.data(ctx).compiledLevel != core::CompiledLevel::True)) { + if (!infer::Inference::willRun(ctx, m.declLoc, m.symbol)) { return; } + auto &print = opts.print; auto cfg = cfg::CFGBuilder::buildFor(ctx.withOwner(m.symbol), m); @@ -100,17 +105,24 @@ string fileKey(const core::File &file) { ast::ExpressionPtr fetchTreeFromCache(core::GlobalState &gs, core::FileRef fref, core::File &file, const unique_ptr &kvstore) { - if (kvstore && fref.id() < gs.filesUsed()) { - string fileHashKey = fileKey(file); - auto maybeCached = kvstore->read(fileHashKey); - if (maybeCached.data != nullptr) { - prodCounterInc("types.input.files.kvstore.hit"); - return core::serialize::Serializer::loadTree(gs, file, maybeCached.data); - } else { - prodCounterInc("types.input.files.kvstore.miss"); - } + if (kvstore == nullptr) { + return nullptr; + } + + if (fref.id() >= gs.filesUsed()) { + prodCounterInc("types.input.files.kvstore.unindexed"); + return nullptr; + } + + string fileHashKey = fileKey(file); + auto maybeCached = kvstore->read(fileHashKey); + if (maybeCached.data == nullptr) { + prodCounterInc("types.input.files.kvstore.miss"); + return nullptr; } - return nullptr; + + prodCounterInc("types.input.files.kvstore.hit"); + return core::serialize::Serializer::loadTree(gs, file, maybeCached.data); } unique_ptr runParser(core::GlobalState &gs, core::FileRef file, const options::Printers &print, @@ -173,10 +185,29 @@ ast::ParsedFile emptyParsedFile(core::FileRef file) { return {ast::MK::EmptyTree(), file}; } +ast::ExpressionPtr desugarOne(const options::Options &opts, core::GlobalState &gs, core::FileRef file) { + auto &print = opts.print; + + Timer timeit(gs.tracer(), "desugarOne", {{"file", string(file.data(gs).path())}}); + try { + if (file.data(gs).strictLevel == core::StrictLevel::Ignore) { + return ast::MK::EmptyTree(); + } + auto parseTree = runParser(gs, file, print, opts.traceLexer, opts.traceParser); + return runDesugar(gs, file, move(parseTree), print); + } catch (SorbetException &) { + Exception::failInFuzzer(); + if (auto e = gs.beginError(sorbet::core::Loc::none(file), core::errors::Internal::InternalError)) { + e.setHeader("Exception desugaring file: `{}` (backtrace is above)", file.data(gs).path()); + } + return ast::MK::EmptyTree(); + } +} + ast::ParsedFile indexOne(const options::Options &opts, core::GlobalState &lgs, core::FileRef file, ast::ExpressionPtr tree) { auto &print = opts.print; - ast::ParsedFile rewriten{nullptr, file}; + ast::ParsedFile rewritten{nullptr, file}; Timer timeit(lgs.tracer(), "indexOne", {{"file", string(file.data(lgs).path())}}); try { @@ -215,8 +246,8 @@ ast::ParsedFile indexOne(const options::Options &opts, core::GlobalState &lgs, c return emptyParsedFile(file); } - rewriten.tree = move(tree); - return rewriten; + rewritten.tree = move(tree); + return rewritten; } catch (SorbetException &) { Exception::failInFuzzer(); if (auto e = lgs.beginError(sorbet::core::Loc::none(file), core::errors::Internal::InternalError)) { @@ -229,11 +260,20 @@ ast::ParsedFile indexOne(const options::Options &opts, core::GlobalState &lgs, c vector incrementalResolve(core::GlobalState &gs, vector what, optional> &&foundHashesForFiles, - const options::Options &opts) { + const options::Options &opts, WorkerPool &workers) { try { #ifndef SORBET_REALMAIN_MIN if (opts.stripePackages) { Timer timeit(gs.tracer(), "incremental_packager"); + // For simplicity, we still call Packager::runIncremental here, even though + // pipeline::nameAndResolve no longer calls Packager::run. + // + // TODO(jez) We may want to revisit this. At the moment, the only thing that + // runIncremental does is validate that files have the right package prefix. We could + // split `pipeline::package` into something like "populate the package DB" and "verify + // the package prefixes" with the later living in `pipeline::nameAndResolve` once again + // (thus restoring the symmetry). + // TODO(jez) Parallelize this what = packager::Packager::runIncremental(gs, move(what)); } #endif @@ -242,16 +282,14 @@ incrementalResolve(core::GlobalState &gs, vector what, Timer timeit(gs.tracer(), "incremental_naming"); core::UnfreezeSymbolTable symbolTable(gs); core::UnfreezeNameTable nameTable(gs); - auto emptyWorkers = WorkerPool::create(0, gs.tracer()); - auto result = runIncrementalNamer - ? sorbet::namer::Namer::runIncremental( - gs, move(what), std::move(foundHashesForFiles.value()), *emptyWorkers) - : sorbet::namer::Namer::run(gs, move(what), *emptyWorkers, nullptr); + auto canceled = runIncrementalNamer + ? sorbet::namer::Namer::runIncremental(gs, absl::Span(what), + std::move(foundHashesForFiles.value()), workers) + : sorbet::namer::Namer::run(gs, absl::Span(what), workers, nullptr); // Cancellation cannot occur during incremental namer. - ENFORCE(result.hasResult()); - what = move(result.result()); + ENFORCE(!canceled); // Required for autogen tests, which need to control which phase to stop after. if (opts.stopAfterPhase == options::Phase::NAMER) { @@ -265,7 +303,7 @@ incrementalResolve(core::GlobalState &gs, vector what, core::UnfreezeSymbolTable symbolTable(gs); core::UnfreezeNameTable nameTable(gs); - auto result = sorbet::resolver::Resolver::runIncremental(gs, move(what), runIncrementalNamer); + auto result = sorbet::resolver::Resolver::runIncremental(gs, move(what), runIncrementalNamer, workers); // incrementalResolve is not cancelable. ENFORCE(result.hasResult()); what = move(result.result()); @@ -278,8 +316,7 @@ incrementalResolve(core::GlobalState &gs, vector what, #ifndef SORBET_REALMAIN_MIN if (opts.stripePackages) { - auto emptyWorkers = WorkerPool::create(0, gs.tracer()); - what = packager::VisibilityChecker::run(gs, *emptyWorkers, std::move(what)); + what = packager::VisibilityChecker::run(gs, workers, std::move(what)); } #endif @@ -534,7 +571,7 @@ vector mergeIndexResults(core::GlobalState &cgs, const options: return ret; } -vector indexSuppliedFiles(core::GlobalState &baseGs, vector &files, +vector indexSuppliedFiles(core::GlobalState &baseGs, absl::Span files, const options::Options &opts, WorkerPool &workers, const unique_ptr &kvstore) { auto resultq = make_shared>(files.size()); @@ -577,7 +614,7 @@ vector indexSuppliedFiles(core::GlobalState &baseGs, vector index(core::GlobalState &gs, vector files, const options::Options &opts, +vector index(core::GlobalState &gs, absl::Span files, const options::Options &opts, WorkerPool &workers, const unique_ptr &kvstore) { Timer timeit(gs.tracer(), "index"); vector ret; @@ -601,6 +638,7 @@ vector index(core::GlobalState &gs, vector files ret = indexSuppliedFiles(gs, files, opts, workers, kvstore); } + // TODO(jez) Do we want this fast_sort here? Is it redundant? fast_sort(ret, [](ast::ParsedFile const &a, ast::ParsedFile const &b) { return a.file < b.file; }); return ret; } @@ -634,7 +672,12 @@ void typecheckOne(core::Context ctx, ast::ParsedFile resolved, const options::Op } return; } - if (f.data(ctx).isRBI()) { + if (f.data(ctx).isRBI() && ctx.state.lspQuery.isEmpty()) { + // If this is an RBI file but isEmpty is not set, we want to run inference just so that we + // can get hover, completion, and definition requests. + // + // There may be type errors in the file (e.g., you don't need `extend T::Sig` to write `sig` + // in an RBI file), but we already ignore errors produced in service of an LSPQuery. if (intentionallyLeakASTs) { intentionallyLeakMemory(resolved.tree.release()); } @@ -655,6 +698,9 @@ void typecheckOne(core::Context ctx, ast::ParsedFile resolved, const options::Op CFGCollectorAndTyper collector(opts); { ast::ShallowWalk::apply(ctx, collector, resolved.tree); + if (f.data(ctx).isRBI()) { + return; + } for (auto &extension : ctx.state.semanticExtensions) { extension->finishTypecheckFile(ctx, f); } @@ -679,50 +725,126 @@ void typecheckOne(core::Context ctx, ast::ParsedFile resolved, const options::Op } } // namespace -vector package(core::GlobalState &gs, vector what, const options::Options &opts, - WorkerPool &workers) { +size_t partitionPackageFiles(const core::GlobalState &gs, absl::Span inputFiles) { + // c_partition does not maintain relative ordering of the elements, which means that + // the sort order of the file paths is not preserved. + // + // index doesn't depend on this order, because it is already indexes files in + // parallel and sorts the resulting parsed files at the end. For that reason, I've + // chosen not to use stable_partition here. + auto packageFilesEnd = absl::c_partition(inputFiles, [&](auto f) { return f.isPackage(gs); }); + auto numPackageFiles = distance(inputFiles.begin(), packageFilesEnd); + return numPackageFiles; +} + +void unpartitionPackageFiles(vector &packageFiles, vector &&nonPackageFiles) { + if (packageFiles.empty()) { + // Performance optimization--if it's already empty, no need to move one-by-one + packageFiles = move(nonPackageFiles); + } else { + // In this case, all the __package.rb files will have been sorted before non-__package.rb files, + // and within each subsequence, the parsed files will be sorted (pipeline::index sorts its result) + packageFiles.reserve(packageFiles.size() + nonPackageFiles.size()); + absl::c_move(nonPackageFiles, back_inserter(packageFiles)); + } +} + +void setPackagerOptions(core::GlobalState &gs, const options::Options &opts) { #ifndef SORBET_REALMAIN_MIN - if (opts.stripePackages) { - { - core::UnfreezeNameTable unfreezeToEnterPackagerOptionsGS(gs); - core::packages::UnfreezePackages unfreezeToEnterPackagerOptionsPackageDB = gs.unfreezePackages(); - gs.setPackagerOptions( - opts.secondaryTestPackageNamespaces, opts.extraPackageFilesDirectoryUnderscorePrefixes, - opts.extraPackageFilesDirectorySlashPrefixes, opts.packageSkipRBIExportEnforcementDirs, - opts.skipPackageImportVisibilityCheckFor, opts.stripePackagesHint); - } - what = packager::Packager::run(gs, workers, move(what)); + if (!opts.stripePackages) { + return; + } + + { + core::UnfreezeNameTable unfreezeToEnterPackagerOptionsGS(gs); + core::packages::UnfreezePackages unfreezeToEnterPackagerOptionsPackageDB = gs.unfreezePackages(); + gs.setPackagerOptions(opts.extraPackageFilesDirectoryUnderscorePrefixes, + opts.extraPackageFilesDirectorySlashPrefixes, opts.packageSkipRBIExportEnforcementDirs, + opts.allowRelaxedPackagerChecksFor, opts.stripePackagesHint); + } +#endif +} + +// packager intentionally runs outside of rewriter so that its output does not get cached. +// TODO(jez) How much of this still needs to be outside of rewriter? +void package(core::GlobalState &gs, absl::Span what, const options::Options &opts, + WorkerPool &workers) { +#ifndef SORBET_REALMAIN_MIN + if (!opts.stripePackages) { + return; + } + + try { + packager::Packager::run(gs, workers, what); if (opts.print.Packager.enabled) { for (auto &f : what) { opts.print.Packager.fmt("# -- {} --\n", f.file.data(gs).path()); opts.print.Packager.fmt("{}\n", f.tree.toStringWithTabs(gs, 0)); } } + } catch (SorbetException &) { + Exception::failInFuzzer(); + if (auto e = gs.beginError(sorbet::core::Loc::none(), core::errors::Internal::InternalError)) { + e.setHeader("Exception packaging (backtrace is above)"); + } } #endif - return what; } -ast::ParsedFilesOrCancelled name(core::GlobalState &gs, vector what, const options::Options &opts, - WorkerPool &workers, core::FoundDefHashes *foundHashes) { +[[nodiscard]] bool name(core::GlobalState &gs, absl::Span what, const options::Options &opts, + WorkerPool &workers, core::FoundDefHashes *foundHashes) { Timer timeit(gs.tracer(), "name"); core::UnfreezeNameTable nameTableAccess(gs); // creates singletons and class names core::UnfreezeSymbolTable symbolTableAccess(gs); // enters symbols - auto result = namer::Namer::run(gs, move(what), workers, foundHashes); + bool canceled = false; + try { + canceled = namer::Namer::run(gs, what, workers, foundHashes); + } catch (SorbetException &) { + Exception::failInFuzzer(); + if (auto e = gs.beginError(sorbet::core::Loc::none(), core::errors::Internal::InternalError)) { + e.setHeader("Exception naming (backtrace is above)"); + } + } + + if (!canceled) { + for (auto &named : what) { + if (opts.print.NameTree.enabled) { + opts.print.NameTree.fmt("{}\n", named.tree.toStringWithTabs(gs, 0)); + } + if (opts.print.NameTreeRaw.enabled) { + opts.print.NameTreeRaw.fmt("{}\n", named.tree.showRaw(gs)); + } + } + } - return result; + return canceled; } + class GatherUnresolvedConstantsWalk { public: vector unresolvedConstants; - void postTransformConstantLit(core::MutableContext ctx, ast::ExpressionPtr &tree) { - auto unresolvedPath = ast::cast_tree_nonnull(tree).fullUnresolvedPath(ctx); + + void postTransformConstantLit(core::MutableContext ctx, const ast::ConstantLit &tree) { + auto unresolvedPath = tree.fullUnresolvedPath(ctx); if (unresolvedPath.has_value()) { unresolvedConstants.emplace_back(fmt::format( "{}::{}", unresolvedPath->first != core::Symbols::root() ? unresolvedPath->first.show(ctx) : "", fmt::map_join(unresolvedPath->second, "::", [&](const auto &el) -> string { return el.show(ctx); }))); } } + + void preTransformClassDef(core::Context ctx, const ast::ClassDef &classDef) { + if (classDef.kind == ast::ClassDef::Kind::Class && !classDef.ancestors.empty()) { + auto *lit = ast::cast_tree(classDef.ancestors.front()); + auto unresolvedPath = lit->fullUnresolvedPath(ctx); + if (unresolvedPath.has_value()) { + unresolvedConstants.emplace_back(fmt::format( + "{}::{}", unresolvedPath->first != core::Symbols::root() ? unresolvedPath->first.show(ctx) : "", + fmt::map_join(unresolvedPath->second, + "::", [&](const auto &el) -> string { return el.show(ctx); }))); + } + } + } }; vector printMissingConstants(core::GlobalState &gs, const options::Options &opts, @@ -731,7 +853,7 @@ vector printMissingConstants(core::GlobalState &gs, const optio GatherUnresolvedConstantsWalk walk; for (auto &resolved : what) { core::MutableContext ctx(gs, core::Symbols::root(), resolved.file); - ast::TreeWalk::apply(ctx, walk, resolved.tree); + ast::ConstTreeWalk::apply(ctx, walk, resolved.tree); } auto &missing = walk.unresolvedConstants; fast_sort(missing); @@ -773,11 +895,11 @@ class DefinitionLinesDenylistEnforcer { ENFORCE(file.exists()); }; - void preTransformClassDef(core::Context ctx, ast::ExpressionPtr &tree) { - checkSym(ctx, ast::cast_tree_nonnull(tree).symbol); + void preTransformClassDef(core::Context ctx, const ast::ClassDef &tree) { + checkSym(ctx, tree.symbol); } - void preTransformMethodDef(core::Context ctx, ast::ExpressionPtr &tree) { - checkSym(ctx, ast::cast_tree_nonnull(tree).symbol); + void preTransformMethodDef(core::Context ctx, const ast::MethodDef &tree) { + checkSym(ctx, tree.symbol); } }; @@ -788,28 +910,20 @@ ast::ParsedFile checkNoDefinitionsInsideProhibitedLines(core::GlobalState &gs, a return what; } -ast::ParsedFilesOrCancelled resolve(unique_ptr &gs, vector what, - const options::Options &opts, WorkerPool &workers, - core::FoundDefHashes *foundHashes) { - try { - // packager intentionally runs outside of rewriter so that its output does not get cached. - what = package(*gs, move(what), opts, workers); - - auto result = name(*gs, move(what), opts, workers, foundHashes); - if (!result.hasResult()) { - return result; - } - what = move(result.result()); +ast::ParsedFilesOrCancelled nameAndResolve(unique_ptr &gs, vector what, + const options::Options &opts, WorkerPool &workers, + core::FoundDefHashes *foundHashes) { + auto canceled = name(*gs, absl::Span(what), opts, workers, foundHashes); + if (canceled) { + return ast::ParsedFilesOrCancelled::cancel(move(what), workers); + } - for (auto &named : what) { - if (opts.print.NameTree.enabled) { - opts.print.NameTree.fmt("{}\n", named.tree.toStringWithTabs(*gs, 0)); - } - if (opts.print.NameTreeRaw.enabled) { - opts.print.NameTreeRaw.fmt("{}\n", named.tree.showRaw(*gs)); - } - } + return resolve(gs, move(what), opts, workers); +} +ast::ParsedFilesOrCancelled resolve(unique_ptr &gs, vector what, + const options::Options &opts, WorkerPool &workers) { + try { if (opts.stopAfterPhase != options::Phase::NAMER) { ProgressIndicator namingProgress(opts.showProgress, "Resolving", 1); { @@ -847,7 +961,7 @@ ast::ParsedFilesOrCancelled resolve(unique_ptr &gs, vector what, const core::FileRef file = job.file; try { core::Context ctx(gs, core::Symbols::root(), file); - auto file = job.file; typecheckOne(ctx, move(job), opts, intentionallyLeakASTs); } catch (SorbetException &) { Exception::failInFuzzer(); @@ -1164,7 +1277,7 @@ void printFileTable(unique_ptr &gs, const options::Options &o #endif } -bool cacheTreesAndFiles(const core::GlobalState &gs, WorkerPool &workers, const vector &parsedFiles, +bool cacheTreesAndFiles(const core::GlobalState &gs, WorkerPool &workers, absl::Span parsedFiles, const unique_ptr &kvstore) { if (kvstore == nullptr) { return false; @@ -1301,8 +1414,8 @@ void printUntypedBlames(const core::GlobalState &gs, const UnorderedMap"); } else { writer.String(pkg.show(gs)); diff --git a/main/pipeline/pipeline.h b/main/pipeline/pipeline.h index 098d40f339..570cd657ef 100644 --- a/main/pipeline/pipeline.h +++ b/main/pipeline/pipeline.h @@ -16,18 +16,25 @@ namespace sorbet::realmain::pipeline { ast::ParsedFile indexOne(const options::Options &opts, core::GlobalState &lgs, core::FileRef file, ast::ExpressionPtr cachedTree = nullptr); +// Primarily exposed for LSP—outside of LSP, you probably want `indexOne`. +ast::ExpressionPtr desugarOne(const options::Options &opts, core::GlobalState &gs, core::FileRef file); + std::vector reserveFiles(std::unique_ptr &gs, const std::vector &files); -std::vector index(core::GlobalState &gs, std::vector files, - const options::Options &opts, WorkerPool &workers, - const std::unique_ptr &kvstore); +std::vector index(core::GlobalState &gs, absl::Span files, const options::Options &opts, + WorkerPool &workers, const std::unique_ptr &kvstore); -std::vector package(core::GlobalState &gs, std::vector what, - const options::Options &opts, WorkerPool &workers); +size_t partitionPackageFiles(const core::GlobalState &gs, absl::Span files); +void unpartitionPackageFiles(std::vector &packageFiles, + std::vector &&nonPackageFiles); -ast::ParsedFilesOrCancelled resolve(std::unique_ptr &gs, std::vector what, - const options::Options &opts, WorkerPool &workers, - core::FoundDefHashes *foundHashes); +void setPackagerOptions(core::GlobalState &gs, const options::Options &opts); +void package(core::GlobalState &gs, absl::Span what, const options::Options &opts, + WorkerPool &workers); + +ast::ParsedFilesOrCancelled nameAndResolve(std::unique_ptr &gs, std::vector what, + const options::Options &opts, WorkerPool &workers, + core::FoundDefHashes *foundHashes); // If `foundMethodHashesForFiles` is non-nullopt, incrementalResolve invokes Namer in runIncremental mode. // @@ -38,10 +45,13 @@ ast::ParsedFilesOrCancelled resolve(std::unique_ptr &gs, std: std::vector incrementalResolve(core::GlobalState &gs, std::vector what, std::optional> &&foundHashesForFiles, - const options::Options &opts); + const options::Options &opts, WorkerPool &workers); -ast::ParsedFilesOrCancelled name(core::GlobalState &gs, std::vector what, const options::Options &opts, - WorkerPool &workers, core::FoundDefHashes *foundHashes); +[[nodiscard]] bool name(core::GlobalState &gs, absl::Span what, const options::Options &opts, + WorkerPool &workers, core::FoundDefHashes *foundHashes); + +ast::ParsedFilesOrCancelled resolve(std::unique_ptr &gs, std::vector what, + const options::Options &opts, WorkerPool &workers); std::vector autogenWriteCacheFile(const core::GlobalState &gs, const std::string &cachePath, std::vector what, WorkerPool &workers); @@ -61,8 +71,7 @@ core::StrictLevel decideStrictLevel(const core::GlobalState &gs, const core::Fil const options::Options &opts); // Caches any uncached trees and files. Returns true if it modifies kvstore. -bool cacheTreesAndFiles(const core::GlobalState &gs, WorkerPool &workers, - const std::vector &parsedFiles, +bool cacheTreesAndFiles(const core::GlobalState &gs, WorkerPool &workers, absl::Span parsedFiles, const std::unique_ptr &kvstore); // Exported for tests only. diff --git a/main/pipeline/semantic_extension/SemanticExtension.h b/main/pipeline/semantic_extension/SemanticExtension.h index 900234adf0..9e2c21f409 100644 --- a/main/pipeline/semantic_extension/SemanticExtension.h +++ b/main/pipeline/semantic_extension/SemanticExtension.h @@ -31,6 +31,8 @@ class SemanticExtension { virtual void finishTypecheckFile(const core::GlobalState &, const core::FileRef &) const = 0; virtual void finishTypecheck(const core::GlobalState &) const = 0; virtual void typecheck(const core::GlobalState &, core::FileRef file, cfg::CFG &, ast::MethodDef &) const = 0; + // HACK: This is for allowing scip-ruby access to ClassDef values. + virtual void typecheckClass(const core::GlobalState &, core::FileRef file, ast::ClassDef &) const = 0; virtual void run(core::MutableContext &, ast::ClassDef *) const = 0; virtual ~SemanticExtension() = default; virtual std::unique_ptr deepCopy(const core::GlobalState &from, core::GlobalState &to) = 0; diff --git a/main/realmain.cc b/main/realmain.cc index 70558bf5b1..3c9ad02109 100644 --- a/main/realmain.cc +++ b/main/realmain.cc @@ -116,7 +116,7 @@ core::StrictLevel levelMinusOne(core::StrictLevel level) { core::StrictLevel levelToRecommendation(core::StrictLevel level) { switch (level) { case core::StrictLevel::Ignore: - // We don't suggest `# typed: ignore` because it is too common for some probem in a + // We don't suggest `# typed: ignore` because it is too common for some problems in a // generated RBI file to cause a problem that actually does need to be fixed, and not by // ignoring the RBI file. Ignoring the file has bad consequences, like introducing more // errors in other files, causing those files to be ignored, etc. @@ -211,58 +211,59 @@ void runAutogen(const core::GlobalState &gs, options::Options &opts, const autog fileq->push(i, 1); } auto crcBuilder = autogen::CRCBuilder::create(); + int autogenVersion = opts.autogenVersion == 0 ? autogen::AutogenVersion::MAX_VERSION : opts.autogenVersion; - workers.multiplexJob("runAutogen", [&gs, &opts, &indexed, &autogenCfg, crcBuilder, fileq, resultq]() { - AutogenResult out; - int n = 0; - int autogenVersion = opts.autogenVersion == 0 ? autogen::AutogenVersion::MAX_VERSION : opts.autogenVersion; - { - Timer timeit(logger, "autogenWorker"); - int idx = 0; - - for (auto result = fileq->try_pop(idx); !result.done(); result = fileq->try_pop(idx)) { - ++n; - auto &tree = indexed[idx]; - if (tree.file.data(gs).isPackage()) { - continue; - } - if (autogenVersion < autogen::AutogenVersion::VERSION_INCLUDE_RBI && tree.file.data(gs).isRBI()) { - continue; - } + workers.multiplexJob( + "runAutogen", [&gs, &autogenVersion, &opts, &indexed, &autogenCfg, crcBuilder, fileq, resultq]() { + AutogenResult out; + int n = 0; + { + Timer timeit(logger, "autogenWorker"); + int idx = 0; + + for (auto result = fileq->try_pop(idx); !result.done(); result = fileq->try_pop(idx)) { + ++n; + auto &tree = indexed[idx]; + if (tree.file.data(gs).isPackage()) { + continue; + } + if (autogenVersion < autogen::AutogenVersion::VERSION_INCLUDE_RBI && tree.file.data(gs).isRBI()) { + continue; + } - core::Context ctx(gs, core::Symbols::root(), tree.file); - auto pf = autogen::Autogen::generate(ctx, move(tree), autogenCfg, *crcBuilder); - tree = move(pf.tree); + core::Context ctx(gs, core::Symbols::root(), tree.file); + auto pf = autogen::Autogen::generate(ctx, move(tree), autogenCfg, *crcBuilder); + tree = move(pf.tree); - AutogenResult::Serialized serialized; + AutogenResult::Serialized serialized; - if (opts.print.Autogen.enabled) { - Timer timeit(logger, "autogenToString"); - serialized.strval = pf.toString(ctx, autogenVersion); - } - if (opts.print.AutogenMsgPack.enabled) { - Timer timeit(logger, "autogenToMsgpack"); - serialized.msgpack = pf.toMsgpack(ctx, autogenVersion, autogenCfg); - } + if (opts.print.Autogen.enabled) { + Timer timeit(logger, "autogenToString"); + serialized.strval = pf.toString(ctx, autogenVersion); + } + if (opts.print.AutogenMsgPack.enabled) { + Timer timeit(logger, "autogenToMsgpack"); + serialized.msgpack = pf.toMsgpack(ctx, autogenVersion, autogenCfg); + } - if (!tree.file.data(gs).isRBI()) { - // Exclude RBI files because they are not loadable and should not appear in - // auto-loader related output. - if (opts.print.AutogenSubclasses.enabled) { - Timer timeit(logger, "autogenSubclasses"); - serialized.subclasses = autogen::Subclasses::listAllSubclasses( - ctx, pf, opts.autogenSubclassesAbsoluteIgnorePatterns, - opts.autogenSubclassesRelativeIgnorePatterns); + if (!tree.file.data(gs).isRBI()) { + // Exclude RBI files because they are not loadable and should not appear in + // auto-loader related output. + if (opts.print.AutogenSubclasses.enabled) { + Timer timeit(logger, "autogenSubclasses"); + serialized.subclasses = autogen::Subclasses::listAllSubclasses( + ctx, pf, opts.autogenSubclassesAbsoluteIgnorePatterns, + opts.autogenSubclassesRelativeIgnorePatterns); + } } - } - out.prints.emplace_back(idx, move(serialized)); + out.prints.emplace_back(idx, move(serialized)); + } } - } - out.counters = getAndClearThreadCounters(); - resultq->push(move(out), n); - }); + out.counters = getAndClearThreadCounters(); + resultq->push(move(out), n); + }); AutogenResult out; for (auto res = resultq->wait_pop_timed(out, WorkerPool::BLOCK_INTERVAL(), *logger); !res.done(); @@ -279,6 +280,10 @@ void runAutogen(const core::GlobalState &gs, options::Options &opts, const autog if (opts.print.Autogen.enabled || opts.print.AutogenMsgPack.enabled) { { Timer timeit(logger, "autogenDependencyDBPrint"); + if (opts.print.AutogenMsgPack.enabled) { + opts.print.AutogenMsgPack.print( + autogen::ParsedFile::msgpackGlobalHeader(autogenVersion, merged.size(), autogenCfg)); + } for (auto &elem : merged) { if (opts.print.Autogen.enabled) { opts.print.Autogen.print(elem.strval); @@ -301,17 +306,23 @@ void runAutogen(const core::GlobalState &gs, options::Options &opts, const autog continue; } - for (const auto &[parentName, children] : *el.subclasses) { - if (!parentName.empty()) { - auto &childEntry = childMap[parentName]; - childEntry.entries.insert(children.entries.begin(), children.entries.end()); - childEntry.classKind = children.classKind; - } + for (const auto &[parentRef, children] : *el.subclasses) { + auto &childEntry = childMap[parentRef]; + childEntry.entries.insert(children.entries.begin(), children.entries.end()); + childEntry.classKind = children.classKind; } } + auto autogenSubclassesParentsRefs = vector(); + for (auto &parent : opts.autogenSubclassesParents) { + auto parentRef = autogen::Subclasses::getConstantRef(gs, parent); + if (!parentRef.exists()) + continue; + autogenSubclassesParentsRefs.emplace_back(parentRef); + } + vector serializedDescendantsMap = - autogen::Subclasses::genDescendantsMap(childMap, opts.autogenSubclassesParents); + autogen::Subclasses::genDescendantsMap(gs, childMap, autogenSubclassesParentsRefs); opts.print.AutogenSubclasses.fmt( "{}\n", fmt::join(serializedDescendantsMap.begin(), serializedDescendantsMap.end(), "\n")); @@ -379,9 +390,6 @@ int realmain(int argc, char *argv[]) { vector> extensions; options::Options opts; options::readOptions(opts, extensions, argc, argv, extensionProviders, logger); - while (opts.waitForDebugger && !stopInDebugger()) { - // spin - } if (opts.stdoutHUPHack) { startHUPMonitor(); } @@ -493,6 +501,8 @@ int realmain(int argc, char *argv[]) { gs->includeErrorSections = false; } gs->ruby3KeywordArgs = opts.ruby3KeywordArgs; + gs->typedSuper = opts.typedSuper; + gs->suppressPayloadSuperclassRedefinitionFor = opts.suppressPayloadSuperclassRedefinitionFor; if (!opts.stripeMode) { // Definitions in multiple locations interact poorly with autoloader this error is enforced in Stripe code. if (opts.isolateErrorCode.empty()) { @@ -520,6 +530,14 @@ int realmain(int argc, char *argv[]) { } gs->suggestUnsafe = opts.suggestUnsafe; + if (gs->runningUnderAutogen) { + gs->suppressErrorClass(core::errors::Namer::RedefinitionOfMethod.code); + gs->suppressErrorClass(core::errors::Namer::ModuleKindRedefinition.code); + gs->suppressErrorClass(core::errors::Namer::ConstantKindRedefinition.code); + gs->suppressErrorClass(core::errors::Resolver::StubConstant.code); + gs->suppressErrorClass(core::errors::Resolver::RecursiveTypeAlias.code); + } + logger->trace("done building initial global state"); if (opts.print.PayloadSources.enabled) { @@ -573,7 +591,7 @@ int realmain(int argc, char *argv[]) { "Talk ‘\\r\\n’-separated JSON-RPC to me. " "More details at https://microsoft.github.io/language-server-protocol/specification." "If you're developing an LSP extension to some editor, make sure to run sorbet with `-v` flag," - "it will enable outputing the LSP session to stderr(`Write: ` and `Read: ` log lines)", + "it will enable outputting the LSP session to stderr(`Write: ` and `Read: ` log lines)", sorbet_full_version_string); auto output = make_shared(logger); @@ -591,7 +609,7 @@ int realmain(int argc, char *argv[]) { hashing::Hashing::computeFileHashes(gs->getFiles(), *logger, *workers, opts); } - { inputFiles = pipeline::reserveFiles(gs, opts.inputFileNames); } + inputFiles = pipeline::reserveFiles(gs, opts.inputFileNames); if (opts.packageRBIGeneration) { #ifdef SORBET_REALMAIN_MIN @@ -643,18 +661,17 @@ int realmain(int argc, char *argv[]) { // only the package files that we know we need to load, it would cut down command-line rbi generation by // seconds. auto packageFileRefs = pipeline::reserveFiles(gs, packageFiles); - auto packages = pipeline::index(*gs, packageFileRefs, opts, *workers, nullptr); + auto packages = pipeline::index(*gs, absl::Span(packageFileRefs), opts, *workers, nullptr); { core::UnfreezeNameTable unfreezeToEnterPackagerOptionsGS(*gs); core::packages::UnfreezePackages unfreezeToEnterPackagerOptionsPackageDB = gs->unfreezePackages(); - gs->setPackagerOptions( - opts.secondaryTestPackageNamespaces, opts.extraPackageFilesDirectoryUnderscorePrefixes, - opts.extraPackageFilesDirectorySlashPrefixes, opts.packageSkipRBIExportEnforcementDirs, - opts.skipPackageImportVisibilityCheckFor, opts.stripePackagesHint); + gs->setPackagerOptions(opts.extraPackageFilesDirectoryUnderscorePrefixes, + opts.extraPackageFilesDirectorySlashPrefixes, + opts.packageSkipRBIExportEnforcementDirs, opts.allowRelaxedPackagerChecksFor, + opts.stripePackagesHint); } - packages = packager::Packager::findPackages(*gs, *workers, move(packages)); - + packager::Packager::findPackages(*gs, absl::Span(packages)); packager::Packager::setPackageNameOnFiles(*gs, packages); packager::Packager::setPackageNameOnFiles(*gs, inputFiles); @@ -700,41 +717,78 @@ int realmain(int argc, char *argv[]) { } { - if (!opts.storeState.empty() || opts.forceHashing) { - // Calculate file hashes alongside indexing when --store-state is specified for LSP mode - indexed = hashing::Hashing::indexAndComputeFileHashes(gs, opts, *logger, inputFiles, *workers, kvstore); - } else { - indexed = pipeline::index(*gs, inputFiles, opts, *workers, kvstore); + // ----- index ----- + + auto inputFilesSpan = absl::Span(inputFiles); + if (opts.stripePackages) { + auto numPackageFiles = pipeline::partitionPackageFiles(*gs, inputFilesSpan); + auto inputPackageFiles = inputFilesSpan.first(numPackageFiles); + inputFilesSpan = inputFilesSpan.subspan(numPackageFiles); + + if (!opts.storeState.empty() || opts.forceHashing) { + indexed = hashing::Hashing::indexAndComputeFileHashes(gs, opts, *logger, inputPackageFiles, + *workers, kvstore); + } else { + indexed = pipeline::index(*gs, inputPackageFiles, opts, *workers, kvstore); + } + + // Cache these before any pipeline::package rewrites, so that the cache is still + // usable regardless of whether `--stripe-packages` was passed. + cache::maybeCacheGlobalStateAndFiles(OwnedKeyValueStore::abort(move(kvstore)), opts, *gs, *workers, + indexed); + + // First run: only the __package.rb files. This populates the packageDB + pipeline::setPackagerOptions(*gs, opts); + pipeline::package(*gs, absl::Span(indexed), opts, *workers); + // Only need to compute hashes when running to compute a FileHash + auto foundHashes = nullptr; + auto canceled = pipeline::name(*gs, absl::Span(indexed), opts, *workers, foundHashes); + ENFORCE(!canceled, "There's no cancellation in batch mode"); } + + auto nonPackageIndexed = + (!opts.storeState.empty() || opts.forceHashing) + // Calculate file hashes alongside indexing when --store-state is specified for LSP mode + ? hashing::Hashing::indexAndComputeFileHashes(gs, opts, *logger, inputFilesSpan, *workers, kvstore) + : pipeline::index(*gs, inputFilesSpan, opts, *workers, kvstore); + + // Cache these before any pipeline::package rewrites, so that the cache is still usable + // regardless of whether `--stripe-packages` was passed. + cache::maybeCacheGlobalStateAndFiles(OwnedKeyValueStore::abort(move(kvstore)), opts, *gs, *workers, + nonPackageIndexed); + + // Second run: all the other files (the packageDB shouldn't change) + pipeline::package(*gs, absl::Span(nonPackageIndexed), opts, *workers); + + // Only need to compute hashes when running to compute a FileHash + auto foundHashes = nullptr; + auto canceled = + pipeline::name(*gs, absl::Span(nonPackageIndexed), opts, *workers, foundHashes); + ENFORCE(!canceled, "There's no cancellation in batch mode"); + + pipeline::unpartitionPackageFiles(indexed, move(nonPackageIndexed)); + // TODO(jez) At this point, it's not correct to call it `indexed` anymore: we've run namer too + if (gs->hadCriticalError()) { gs->errorQueue->flushAllErrors(*gs); } } - cache::maybeCacheGlobalStateAndFiles(OwnedKeyValueStore::abort(move(kvstore)), opts, *gs, *workers, indexed); if (gs->runningUnderAutogen) { #ifdef SORBET_REALMAIN_MIN logger->warn("Autogen is disabled in sorbet-orig for faster builds"); return 1; #else - + // TODO(jez) Make sure that it's still okay to run this phase after namer, otherwise + // you'll have to adjust the non-autogen pipeline code. At first read, it seems like it + // unwraps ConstantLit to UnresolvedConstantLit and proceeds as normal, so I think it + // should be fine. if (!opts.autogenConstantCacheConfig.cacheFile.empty()) { // we should regenerate the constant cache here indexed = pipeline::autogenWriteCacheFile(*gs, opts.autogenConstantCacheConfig.cacheFile, move(indexed), *workers); } - gs->suppressErrorClass(core::errors::Namer::RedefinitionOfMethod.code); - gs->suppressErrorClass(core::errors::Namer::InvalidClassOwner.code); - gs->suppressErrorClass(core::errors::Namer::ModuleKindRedefinition.code); - gs->suppressErrorClass(core::errors::Namer::ConstantKindRedefinition.code); - gs->suppressErrorClass(core::errors::Resolver::StubConstant.code); - gs->suppressErrorClass(core::errors::Resolver::RecursiveTypeAlias.code); - - // Only need to compute FoundMethodHashes when running to compute a FileHash - auto foundMethodHashes = nullptr; - indexed = move(pipeline::name(*gs, move(indexed), opts, *workers, foundMethodHashes).result()); - { core::UnfreezeNameTable nameTableAccess(*gs); core::UnfreezeSymbolTable symbolAccess(*gs); @@ -742,15 +796,14 @@ int realmain(int argc, char *argv[]) { indexed = resolver::Resolver::runConstantResolution(*gs, move(indexed), *workers); } - autogen::AutogenConfig autogenCfg = {.behaviorAllowedInRBIsPaths = - std::move(opts.autogenBehaviorAllowedInRBIFilesPaths)}; + autogen::AutogenConfig autogenCfg = { + .behaviorAllowedInRBIsPaths = std::move(opts.autogenBehaviorAllowedInRBIFilesPaths), + .msgpackSkipReferenceMetadata = std::move(opts.autogenMsgpackSkipReferenceMetadata)}; runAutogen(*gs, opts, autogenCfg, *workers, indexed, opts.autogenConstantCacheConfig.changedFiles); #endif } else { - // Only need to compute hashes when running to compute a FileHash - auto foundHashes = nullptr; - indexed = move(pipeline::resolve(gs, move(indexed), opts, *workers, foundHashes).result()); + indexed = move(pipeline::resolve(gs, move(indexed), opts, *workers).result()); if (gs->hadCriticalError()) { gs->errorQueue->flushAllErrors(*gs); } @@ -814,7 +867,7 @@ int realmain(int argc, char *argv[]) { } minErrorLevel = levelToRecommendation(minErrorLevel); if (file.data(*gs).originalSigil == minErrorLevel) { - // if the file could be strong, but is only marked strict, ensure that we don't reccomend that it be + // if the file could be strong, but is only marked strict, ensure that we don't recommend that it be // marked strict. continue; } diff --git a/namer/namer.cc b/namer/namer.cc index 3e9293de71..2398fea920 100644 --- a/namer/namer.cc +++ b/namer/namer.cc @@ -26,15 +26,32 @@ namespace sorbet::namer { namespace { +struct ParsedFileWithIdx { + ast::ParsedFile parsedFile; + size_t idx; + + ParsedFileWithIdx() = default; + ParsedFileWithIdx(ast::ParsedFile &&parsedFile, size_t idx) : parsedFile(move(parsedFile)), idx(idx) {} + ParsedFileWithIdx(const ParsedFileWithIdx &job) = delete; + ParsedFileWithIdx &operator=(const ParsedFileWithIdx &job) = delete; + ParsedFileWithIdx(ParsedFileWithIdx &&job) = default; + ParsedFileWithIdx &operator=(ParsedFileWithIdx &&job) = default; +}; + struct SymbolFinderResult { ast::ParsedFile tree; unique_ptr names; + + SymbolFinderResult() = default; + SymbolFinderResult(ast::ParsedFile &&tree, unique_ptr names) + : tree(move(tree)), names(move(names)) {} + SymbolFinderResult(const SymbolFinderResult &result) = delete; + SymbolFinderResult &operator=(const SymbolFinderResult &result) = delete; + SymbolFinderResult(SymbolFinderResult &&result) = default; + SymbolFinderResult &operator=(SymbolFinderResult &&result) = default; }; -void swap(SymbolFinderResult &a, SymbolFinderResult &b) { - a.tree.swap(b.tree); - a.names.swap(b.names); -} +using AllFoundDefinitions = vector>>; core::ClassOrModuleRef methodOwner(core::Context ctx, core::SymbolRef owner, bool isSelfMethod) { ENFORCE(owner.exists() && owner != core::Symbols::todo()); @@ -77,7 +94,6 @@ bool isMangleRenameUniqueName(core::GlobalState &gs, core::NameRef name) { /** * Used with TreeWalk to locate all of the class, method, static field, and type member symbols defined in the tree. * Does not mutate GlobalState, which allows us to parallelize this process. - * Does not report any errors, which lets us cache its output. * Produces a vector of symbols to insert, and a vector of modifiers to those symbols. */ class SymbolFinder { @@ -89,7 +105,7 @@ class SymbolFinder { // This tracks those as they appear. vector> methodVisiStack = {nullopt}; - void findClassModifiers(core::Context ctx, core::FoundDefinitionRef klass, ast::ExpressionPtr &line) { + void findClassModifiers(core::Context ctx, core::FoundDefinitionRef klass, const ast::ExpressionPtr &line) { auto *send = ast::cast_tree(line); if (send == nullptr) { return; @@ -169,7 +185,7 @@ class SymbolFinder { return rv; } - void preTransformClassDef(core::Context ctx, ast::ExpressionPtr &tree) { + void preTransformClassDef(core::Context ctx, const ast::ExpressionPtr &tree) { auto &klass = ast::cast_tree_nonnull(tree); core::FoundClass found; @@ -200,6 +216,7 @@ class SymbolFinder { methodVisiStack.emplace_back(nullopt); } + // `tree` is not `const` because this populates the `ancestors` field of the ClassDef void postTransformClassDef(core::Context ctx, ast::ExpressionPtr &tree) { auto &klass = ast::cast_tree_nonnull(tree); @@ -232,7 +249,7 @@ class SymbolFinder { } } - void addAncestor(core::Context ctx, ast::ClassDef &klass, ast::ExpressionPtr &node) { + void addAncestor(core::Context ctx, ast::ClassDef &klass, const ast::ExpressionPtr &node) { auto send = ast::cast_tree(node); if (send == nullptr) { ENFORCE(node.get() != nullptr); @@ -254,7 +271,7 @@ class SymbolFinder { const auto numPosArgs = send->numPosArgs(); if (numPosArgs == 0) { - if (auto e = ctx.beginError(send->loc, core::errors::Namer::IncludeMutipleParam)) { + if (auto e = ctx.beginError(send->loc, core::errors::Namer::IncludeMultipleParam)) { e.setHeader("`{}` requires at least one argument", send->fun.show(ctx)); } return; @@ -287,7 +304,7 @@ class SymbolFinder { } } - bool isValidAncestor(ast::ExpressionPtr &exp) { + bool isValidAncestor(const ast::ExpressionPtr &exp) { if (ast::isa_tree(exp) || exp.isSelfReference() || ast::isa_tree(exp)) { return true; } @@ -297,15 +314,15 @@ class SymbolFinder { return false; } - void preTransformBlock(core::Context ctx, ast::ExpressionPtr &block) { + void preTransformBlock(core::Context ctx, const ast::ExpressionPtr &block) { methodVisiStack.emplace_back(nullopt); } - void postTransformBlock(core::Context ctx, ast::ExpressionPtr &block) { + void postTransformBlock(core::Context ctx, const ast::ExpressionPtr &block) { methodVisiStack.pop_back(); } - void preTransformMethodDef(core::Context ctx, ast::ExpressionPtr &tree) { + void preTransformMethodDef(core::Context ctx, const ast::ExpressionPtr &tree) { auto &method = ast::cast_tree_nonnull(tree); core::FoundMethod foundMethod; foundMethod.owner = getOwner(); @@ -325,7 +342,7 @@ class SymbolFinder { methodVisiStack.emplace_back(nullopt); } - void postTransformMethodDef(core::Context ctx, ast::ExpressionPtr &tree) { + void postTransformMethodDef(core::Context ctx, const ast::ExpressionPtr &tree) { methodVisiStack.pop_back(); ownerStack.pop_back(); } @@ -350,7 +367,7 @@ class SymbolFinder { } } - void postTransformSend(core::Context ctx, ast::ExpressionPtr &tree) { + void postTransformSend(core::Context ctx, const ast::ExpressionPtr &tree) { auto &original = ast::cast_tree_nonnull(tree); auto ownerIsMethod = getOwnerRaw().kind() == core::FoundDefinitionRef::Kind::Method; @@ -373,6 +390,11 @@ class SymbolFinder { } if (!original.hasPosArgs()) { ENFORCE(!methodVisiStack.empty()); + + if (!original.recv.isSelfReference()) { + break; + } + methodVisiStack.back() = optional{core::FoundModifier{ core::FoundModifier::Kind::Method, getOwner(), @@ -426,7 +448,7 @@ class SymbolFinder { } } - void postTransformRuntimeMethodDefinition(core::Context, ast::ExpressionPtr &tree) { + void postTransformRuntimeMethodDefinition(core::Context, const ast::ExpressionPtr &tree) { auto &original = ast::cast_tree_nonnull(tree); // visibility toggle doesn't look at `self.*` methods, only instance methods @@ -614,17 +636,28 @@ class SymbolFinder { return foundDefs->addTypeMember(move(found)); } + bool sendRecvIsT(const ast::Send &s) { + bool result = false; + + typecase( + s.recv, [&](const ast::UnresolvedConstantLit &c) { result = c.cnst == core::Names::Constants::T(); }, + [&](const ast::ConstantLit &c) { result = c.symbol == core::Symbols::T(); }, + [&](const ast::ExpressionPtr &_default) { result = false; }); + + return result; + } + core::FoundDefinitionRef handleAssignment(core::Context ctx, const ast::Assign &asgn) { auto &send = ast::cast_tree_nonnull(asgn.rhs); auto foundRef = fillAssign(ctx, asgn); ENFORCE(foundRef.kind() == core::FoundDefinitionRef::Kind::StaticField); auto &staticField = foundRef.staticField(*foundDefs); - staticField.isTypeAlias = send.fun == core::Names::typeAlias(); + staticField.isTypeAlias = sendRecvIsT(send) && send.fun == core::Names::typeAlias(); return foundRef; } // Returns `true` if `asgn` is a field declaration. - bool handleFieldDeclaration(core::Context ctx, ast::Assign &asgn) { + bool handleFieldDeclaration(core::Context ctx, const ast::Assign &asgn) { auto *uid = ast::cast_tree(asgn.lhs); if (uid == nullptr) { return false; @@ -662,7 +695,7 @@ class SymbolFinder { return true; } - void postTransformAssign(core::Context ctx, ast::ExpressionPtr &tree) { + void postTransformAssign(core::Context ctx, const ast::ExpressionPtr &tree) { auto &asgn = ast::cast_tree_nonnull(tree); if (handleFieldDeclaration(ctx, asgn)) { @@ -869,7 +902,7 @@ class SymbolDefiner { ENFORCE(!inShadows, "shadow argument followed by non-shadow argument!"); if (swapArgs && arg.flags.isBlock) { - // see commnent on if (swapArgs) above + // see comment on if (swapArgs) above methodData->arguments.emplace_back(move(swappedArg)); } @@ -1217,6 +1250,11 @@ class SymbolDefiner { auto singletonClass = symbol.data(ctx)->singletonClass(ctx); // force singleton class into existence singletonClass.data(ctx)->addLoc(ctx, ctx.locAt(klass.declLoc)); + auto attachedClassTM = + singletonClass.data(ctx)->findMember(ctx, core::Names::Constants::AttachedClass()); + if (attachedClassTM.exists() && attachedClassTM.isTypeMember()) { + attachedClassTM.asTypeMemberRef().data(ctx)->addLoc(ctx, ctx.locAt(klass.declLoc)); + } // This willDeleteOldDefs condition is a hack to improve performance when editing within a method body. // Ideally, we would be able to make finalizeSymbols fast/incremental enough to run on all edits. @@ -1263,8 +1301,9 @@ class SymbolDefiner { symbolData->flags.isSealed = true; auto classOfKlass = symbolData->singletonClass(ctx); - auto sealedSubclasses = ctx.state.enterMethodSymbol(ctx.locAt(mod.loc), classOfKlass, - core::Names::sealedSubclasses(), core::Loc::none()); + auto loc = ctx.locAt(mod.loc); + auto sealedSubclasses = ctx.state.enterMethodSymbol(loc, classOfKlass, core::Names::sealedSubclasses(), core::Loc::none()); + sealedSubclasses.data(ctx)->addLoc(ctx, loc); auto &blkArg = ctx.state.enterMethodArgumentSymbol(core::Loc::none(), sealedSubclasses, core::Names::blkArg()); blkArg.flags.isBlock = true; @@ -1395,6 +1434,8 @@ class SymbolDefiner { core::TypeMemberRef sym; auto existingTypeMember = ctx.state.lookupTypeMemberSymbol(onSymbol, typeMember.name); + auto typeMemberName = typeMember.name; + if (existingTypeMember.exists()) { // if we already have a type member but it was constructed in a different file from the one we're // looking at, then we need to raise an error @@ -1426,28 +1467,33 @@ class SymbolDefiner { }) != members.end()); } sym = existingTypeMember; + sym.data(ctx)->addLoc(ctx, ctx.locAt(typeMember.asgnLoc)); } else { - auto name = typeMember.name; - auto oldSym = onSymbol.data(ctx)->findMemberNoDealias(ctx, name); + auto oldSym = onSymbol.data(ctx)->findMemberNoDealias(typeMemberName); if (oldSym.exists()) { emitRedefinedConstantError(ctx, typeMember.nameLoc, oldSym.name(ctx), core::SymbolRef::Kind::TypeMember, oldSym); - name = ctx.state.nextMangledName(onSymbol, name); + typeMemberName = ctx.state.nextMangledName(onSymbol, typeMemberName); } - sym = ctx.state.enterTypeMember(ctx.locAt(typeMember.asgnLoc), onSymbol, name, variance); + sym = ctx.state.enterTypeMember(ctx.locAt(typeMember.asgnLoc), onSymbol, typeMemberName, variance); // The todo bounds will be fixed by the resolver in ResolveTypeParamsWalk. auto todo = core::make_type(core::Symbols::todo()); sym.data(ctx)->resultType = core::make_type(sym, todo, todo); + } - if (isTypeTemplate) { - auto typeTemplateAliasName = typeMember.name; - auto context = ctx.owner.enclosingClass(ctx); - auto oldSym = context.data(ctx)->findMemberNoDealias(ctx, typeTemplateAliasName); + if (isTypeTemplate) { + auto typeTemplateAliasName = typeMember.name; + auto context = ctx.owner.enclosingClass(ctx); + if (existingTypeMember.exists()) { + auto alias = ctx.state.lookupStaticFieldSymbol(context, typeTemplateAliasName); + alias.data(ctx)->addLoc(ctx, ctx.locAt(typeMember.asgnLoc)); + } else { + auto oldSym = context.data(ctx)->findMemberNoDealias(typeTemplateAliasName); if (oldSym.exists() && !(oldSym.loc(ctx) == ctx.locAt(typeMember.asgnLoc) || oldSym.loc(ctx).isTombStoned(ctx))) { - emitRedefinedConstantError(ctx, typeMember.nameLoc, name, core::SymbolRef::Kind::TypeMember, - oldSym); + emitRedefinedConstantError(ctx, typeMember.nameLoc, typeMemberName, + core::SymbolRef::Kind::TypeMember, oldSym); typeTemplateAliasName = ctx.state.nextMangledName(context, typeTemplateAliasName); } // This static field with an AliasType is how we get `MyTypeTemplate` to resolve, @@ -1636,7 +1682,7 @@ class SymbolDefiner { } } - SymbolDefiner(const core::FoundDefinitions &foundDefs, optional oldFoundHashes) + SymbolDefiner(const core::FoundDefinitions &foundDefs, optional &&oldFoundHashes) : foundDefs(foundDefs), oldFoundHashes(move(oldFoundHashes)) {} SymbolDefiner::State enterClassDefinitions(core::MutableContext ctx, bool willDeleteOldDefs, @@ -1803,19 +1849,12 @@ class TreeSymbolizer { } } - // This decides if we need to keep a node around incase the current LSP query needs type information for it - bool shouldLeaveAncestorForIDE(const ast::ExpressionPtr &anc) { - // used in Desugar <-> resolver to signal classes that did not have explicit superclass - if (ast::isa_tree(anc) || anc.isSelfReference()) { - return false; - } - auto rcl = ast::cast_tree(anc); - if (rcl && rcl->symbol == core::Symbols::todo()) { - return false; - } - return true; - } - +#ifdef DEBUG_MODE + // After some refactors, the only thing left in this callback is a bunch of ENFORCEs, so I've + // compiled the entire callback out unless DEBUG_MODE is set. + // + // If you're changing this to put load bearing logic back into this method, feel free to remove + // the #ifdef above. void postTransformClassDef(core::Context ctx, ast::ExpressionPtr &tree) { auto &klass = ast::cast_tree_nonnull(tree); @@ -1828,22 +1867,11 @@ class TreeSymbolizer { // ENFORCE'ing it here makes certain errors apparent earlier. auto allowMissing = true; ENFORCE(ctx.state.lookupStaticInitForClass(klass.symbol, allowMissing).exists()); - - auto loc = klass.declLoc; - ast::InsSeq::STATS_store retSeqs; - retSeqs.emplace_back(std::move(tree)); - if (ast::isa_tree(klass.name)) { - retSeqs.emplace_back(ast::MK::KeepForIDE(loc.copyWithZeroLength(), klass.name.deepCopy())); - } - if (klass.kind == ast::ClassDef::Kind::Class && !klass.ancestors.empty() && - shouldLeaveAncestorForIDE(klass.ancestors.front())) { - retSeqs.emplace_back(ast::MK::KeepForIDE(loc.copyWithZeroLength(), klass.ancestors.front().deepCopy())); - } - - tree = ast::MK::InsSeq(loc, std::move(retSeqs), ast::MK::EmptyTree()); } +#endif - ast::MethodDef::ARGS_store fillInArgs(vector parsedArgs, ast::MethodDef::ARGS_store oldArgs) { + ast::MethodDef::ARGS_store fillInArgs(const vector &parsedArgs, + ast::MethodDef::ARGS_store oldArgs) { ast::MethodDef::ARGS_store args; int i = -1; for (auto &arg : parsedArgs) { @@ -2153,50 +2181,44 @@ class TreeSymbolizer { } }; -vector findSymbols(const core::GlobalState &gs, vector trees, - WorkerPool &workers) { +AllFoundDefinitions findSymbols(const core::GlobalState &gs, absl::Span trees, WorkerPool &workers) { Timer timeit(gs.tracer(), "naming.findSymbols"); - auto resultq = make_shared>>(trees.size()); - auto fileq = make_shared>(trees.size()); - vector allFoundDefinitions; - allFoundDefinitions.reserve(trees.size()); + auto resultq = make_shared>>(trees.size()); + auto fileq = make_shared>(trees.size()); + AllFoundDefinitions allFoundDefinitions(trees.size()); + size_t idx = 0; for (auto &tree : trees) { - fileq->push(move(tree), 1); + fileq->push(ParsedFileWithIdx(move(tree), idx++), 1); } workers.multiplexJob("findSymbols", [&gs, fileq, resultq]() { Timer timeit(gs.tracer(), "naming.findSymbolsWorker"); SymbolFinder finder; - vector output; - ast::ParsedFile job; + ParsedFileWithIdx job; for (auto result = fileq->try_pop(job); !result.done(); result = fileq->try_pop(job)) { if (result.gotItem()) { - Timer timeit(gs.tracer(), "naming.findSymbolsOne", {{"file", string(job.file.data(gs).path())}}); - core::Context ctx(gs, core::Symbols::root(), job.file); - ast::TreeWalk::apply(ctx, finder, job.tree); - SymbolFinderResult jobOutput{move(job), finder.getAndClearFoundDefinitions()}; - output.emplace_back(move(jobOutput)); + Timer timeit(gs.tracer(), "naming.findSymbolsOne", + {{"file", string(job.parsedFile.file.data(gs).path())}}); + core::Context ctx(gs, core::Symbols::root(), job.parsedFile.file); + ast::TreeWalk::apply(ctx, finder, job.parsedFile.tree); + auto threadResult = SymbolFinderResult(move(job.parsedFile), finder.getAndClearFoundDefinitions()); + resultq->push({job.idx, move(threadResult)}, 1); } } - if (!output.empty()) { - resultq->push(move(output), output.size()); - } }); - trees.clear(); { - vector threadResult; + pair threadResult; for (auto result = resultq->wait_pop_timed(threadResult, WorkerPool::BLOCK_INTERVAL(), gs.tracer()); !result.done(); result = resultq->wait_pop_timed(threadResult, WorkerPool::BLOCK_INTERVAL(), gs.tracer())) { if (result.gotItem()) { - allFoundDefinitions.insert(allFoundDefinitions.end(), make_move_iterator(threadResult.begin()), - make_move_iterator(threadResult.end())); + auto fref = threadResult.second.tree.file; + trees[threadResult.first] = move(threadResult.second.tree); + allFoundDefinitions[threadResult.first] = make_pair(fref, move(threadResult.second.names)); } } } - fast_sort(allFoundDefinitions, - [](const auto &lhs, const auto &rhs) -> bool { return lhs.tree.file < rhs.tree.file; }); return allFoundDefinitions; } @@ -2285,44 +2307,36 @@ void findConflictingClassDefs(const core::GlobalState &gs, ClassBehaviorLocsMap } } -ast::ParsedFilesOrCancelled defineSymbols(core::GlobalState &gs, vector allFoundDefinitions, - WorkerPool &workers, - UnorderedMap &&oldFoundHashesForFiles, - core::FoundDefHashes *foundHashesOut) { +void defineSymbols(core::GlobalState &gs, AllFoundDefinitions allFoundDefinitions, WorkerPool &workers, + UnorderedMap &&oldFoundHashesForFiles, + core::FoundDefHashes *foundHashesOut) { Timer timeit(gs.tracer(), "naming.defineSymbols"); - vector output; - output.reserve(allFoundDefinitions.size()); const auto &epochManager = *gs.epochManager; uint32_t count = 0; uint32_t foundMethods = 0; ClassBehaviorLocsMap classBehaviorLocs; UnorderedMap incrementalDefinitions; auto willDeleteOldDefs = !oldFoundHashesForFiles.empty(); - for (auto &fileFoundDefinitions : allFoundDefinitions) { - foundMethods += fileFoundDefinitions.names->methods().size(); + for (auto &[fref, fileFoundDefinitions] : allFoundDefinitions) { + foundMethods += fileFoundDefinitions->methods().size(); count++; // defineSymbols is really fast. Avoid this mildly expensive check for most turns of the loop. if (count % 250 == 0 && epochManager.wasTypecheckingCanceled()) { - for (; count <= allFoundDefinitions.size(); count++) { - output.emplace_back(move(allFoundDefinitions[count - 1].tree)); - } - return ast::ParsedFilesOrCancelled::cancel(move(output), workers); + return; } - auto fref = fileFoundDefinitions.tree.file; core::MutableContext ctx(gs, core::Symbols::root(), fref); auto frefIt = oldFoundHashesForFiles.find(fref); auto oldFoundHashes = frefIt == oldFoundHashesForFiles.end() ? optional() : std::move(frefIt->second); - SymbolDefiner symbolDefiner(*fileFoundDefinitions.names, move(oldFoundHashes)); + SymbolDefiner symbolDefiner(*fileFoundDefinitions, move(oldFoundHashes)); auto state = symbolDefiner.enterClassDefinitions(ctx, willDeleteOldDefs, classBehaviorLocs); if (willDeleteOldDefs) { symbolDefiner.deleteOldDefinitions(ctx, state); } incrementalDefinitions[fref] = move(state); - output.emplace_back(move(fileFoundDefinitions.tree)); if (foundHashesOut != nullptr) { - populateFoundDefHashes(ctx, *fileFoundDefinitions.names, *foundHashesOut); + populateFoundDefHashes(ctx, *fileFoundDefinitions, *foundHashesOut); } } @@ -2330,13 +2344,12 @@ ast::ParsedFilesOrCancelled defineSymbols(core::GlobalState &gs, vector oldFoundHashes; core::MutableContext ctx(gs, core::Symbols::root(), fref); - SymbolDefiner symbolDefiner(*fileFoundDefinitions.names, oldFoundHashes); + SymbolDefiner symbolDefiner(*fileFoundDefinitions, move(oldFoundHashes)); symbolDefiner.enterNewDefinitions(ctx, move(incrementalDefinitions[fref])); } - return output; + return; } -struct SymbolizeTreesResult { - vector trees; -}; - -vector symbolizeTrees(const core::GlobalState &gs, vector trees, - WorkerPool &workers) { +void symbolizeTrees(const core::GlobalState &gs, absl::Span trees, WorkerPool &workers) { Timer timeit(gs.tracer(), "naming.symbolizeTrees"); - auto resultq = make_shared>(trees.size()); - auto fileq = make_shared>(trees.size()); + auto resultq = make_shared>(trees.size()); + auto fileq = make_shared>(trees.size()); + size_t idx = 0; for (auto &tree : trees) { - fileq->push(move(tree), 1); + fileq->push(ParsedFileWithIdx(move(tree), idx++), 1); } workers.multiplexJob("symbolizeTrees", [&gs, fileq, resultq]() { Timer timeit(gs.tracer(), "naming.symbolizeTreesWorker"); TreeSymbolizer inserter; - SymbolizeTreesResult output; - ast::ParsedFile job; + ParsedFileWithIdx job; for (auto result = fileq->try_pop(job); !result.done(); result = fileq->try_pop(job)) { if (result.gotItem()) { - Timer timeit(gs.tracer(), "naming.symbolizeTreesOne", {{"file", string(job.file.data(gs).path())}}); - core::Context ctx(gs, core::Symbols::root(), job.file); - ast::TreeWalk::apply(ctx, inserter, job.tree); - output.trees.emplace_back(move(job)); + Timer timeit(gs.tracer(), "naming.symbolizeTreesOne", + {{"file", string(job.parsedFile.file.data(gs).path())}}); + core::Context ctx(gs, core::Symbols::root(), job.parsedFile.file); + ast::TreeWalk::apply(ctx, inserter, job.parsedFile.tree); + resultq->push(move(job), 1); } } - if (!output.trees.empty()) { - resultq->push(move(output), output.trees.size()); - } }); - trees.clear(); { - SymbolizeTreesResult threadResult; + ParsedFileWithIdx threadResult; for (auto result = resultq->wait_pop_timed(threadResult, WorkerPool::BLOCK_INTERVAL(), gs.tracer()); !result.done(); result = resultq->wait_pop_timed(threadResult, WorkerPool::BLOCK_INTERVAL(), gs.tracer())) { if (result.gotItem()) { - trees.insert(trees.end(), make_move_iterator(threadResult.trees.begin()), - make_move_iterator(threadResult.trees.end())); + trees[threadResult.idx] = move(threadResult.parsedFile); } } } - fast_sort(trees, [](const auto &lhs, const auto &rhs) -> bool { return lhs.file < rhs.file; }); - return trees; } } // namespace -ast::ParsedFilesOrCancelled -Namer::runInternal(core::GlobalState &gs, vector trees, WorkerPool &workers, - UnorderedMap &&oldFoundHashesForFiles, - core::FoundDefHashes *foundHashesOut) { - auto foundDefs = findSymbols(gs, move(trees), workers); +// Returns whether typechecking was cancelled +[[nodiscard]] bool Namer::runInternal(core::GlobalState &gs, absl::Span trees, WorkerPool &workers, + UnorderedMap &&oldFoundHashesForFiles, + core::FoundDefHashes *foundHashesOut) { + auto foundDefs = findSymbols(gs, trees, workers); if (gs.epochManager->wasTypecheckingCanceled()) { - trees.reserve(foundDefs.size()); - for (auto &def : foundDefs) { - trees.emplace_back(move(def.tree)); - } - return ast::ParsedFilesOrCancelled::cancel(move(trees), workers); + return true; } if (foundHashesOut != nullptr) { ENFORCE(foundDefs.size() == 1, "Producing foundMethodHashes is meant to only happen when hashing a single file"); } - auto result = defineSymbols(gs, move(foundDefs), workers, std::move(oldFoundHashesForFiles), foundHashesOut); - if (!result.hasResult()) { - return result; + defineSymbols(gs, move(foundDefs), workers, std::move(oldFoundHashesForFiles), foundHashesOut); + if (gs.epochManager->wasTypecheckingCanceled()) { + return true; } - trees = symbolizeTrees(gs, move(result.result()), workers); - return trees; + + symbolizeTrees(gs, trees, workers); + return false; } -ast::ParsedFilesOrCancelled Namer::run(core::GlobalState &gs, vector trees, WorkerPool &workers, - core::FoundDefHashes *foundHashesOut) { +[[nodiscard]] bool Namer::run(core::GlobalState &gs, absl::Span trees, WorkerPool &workers, + core::FoundDefHashes *foundHashesOut) { // In non-incremental namer, there are no old FoundDefHashes; just defineSymbols like normal. auto oldFoundHashesForFiles = UnorderedMap{}; - return runInternal(gs, move(trees), workers, std::move(oldFoundHashesForFiles), foundHashesOut); + return runInternal(gs, trees, workers, std::move(oldFoundHashesForFiles), foundHashesOut); } -ast::ParsedFilesOrCancelled -Namer::runIncremental(core::GlobalState &gs, std::vector trees, - UnorderedMap &&oldFoundHashesForFiles, WorkerPool &workers) { +[[nodiscard]] bool Namer::runIncremental(core::GlobalState &gs, absl::Span trees, + UnorderedMap &&oldFoundHashesForFiles, + WorkerPool &workers) { // foundHashesOut is only used when namer is run via hashing.cc to compute a FileHash for each file // The incremental namer mode should never be used for hashing. auto foundHashesOut = nullptr; - return runInternal(gs, move(trees), workers, std::move(oldFoundHashesForFiles), foundHashesOut); + return runInternal(gs, trees, workers, std::move(oldFoundHashesForFiles), foundHashesOut); } }; // namespace sorbet::namer diff --git a/namer/namer.h b/namer/namer.h index 568c8254a8..7041d9568c 100644 --- a/namer/namer.h +++ b/namer/namer.h @@ -11,19 +11,18 @@ class WorkerPool; namespace sorbet::namer { class Namer final { - static ast::ParsedFilesOrCancelled - runInternal(core::GlobalState &gs, std::vector trees, WorkerPool &workers, - UnorderedMap &&oldFoundDefHashesForFiles, - core::FoundDefHashes *foundHashesOut); + [[nodiscard]] static bool runInternal(core::GlobalState &gs, absl::Span trees, WorkerPool &workers, + UnorderedMap &&oldFoundDefHashesForFiles, + core::FoundDefHashes *foundHashesOut); public: // Note: foundHashes is an optional out parameter. // // Setting it to a non-nullptr requests that Namer compute a fingerprint of the FoundDefinitions - // it found while running. (Thus, it's usually nullptr except when pipeline::resolve is called + // it found while running. (Thus, it's usually nullptr except when pipeline::nameAndResolve is called // for the purpose of computing a FileHash.) - static ast::ParsedFilesOrCancelled run(core::GlobalState &gs, std::vector trees, - WorkerPool &workers, core::FoundDefHashes *foundHashesOut); + [[nodiscard]] static bool run(core::GlobalState &gs, absl::Span trees, WorkerPool &workers, + core::FoundDefHashes *foundHashesOut); // Version of Namer that accepts the old FoundDefHashes for each file to run Namer, which // it uses to figure out how to mutate the already-populated GlobalState into the right shape @@ -33,8 +32,8 @@ class Namer final { // `foundDefHashesForFiles[i]` should be the `FoundDefHashes` for `trees[i]`. // (Done this way, instead of using something like a `std::pair`, to avoid intermediate // allocations for phases that don't actually need to operate on the `FoundDefHashes`.) - static ast::ParsedFilesOrCancelled - runIncremental(core::GlobalState &gs, std::vector trees, + [[nodiscard]] static bool + runIncremental(core::GlobalState &gs, absl::Span trees, UnorderedMap &&oldFoundDefHashesForFiles, WorkerPool &workers); Namer() = delete; diff --git a/namer/test/namer_test.cc b/namer/test/namer_test.cc index d00b8c612b..7eecc21174 100644 --- a/namer/test/namer_test.cc +++ b/namer/test/namer_test.cc @@ -46,7 +46,9 @@ vector runNamer(core::GlobalState &gs, ast::ParsedFile tree) { v.emplace_back(move(tree)); auto workers = WorkerPool::create(0, *logger); core::FoundDefHashes foundHashes; // compute this just for test coverage - return move(namer::Namer::run(gs, move(v), *workers, &foundHashes).result()); + auto canceled = namer::Namer::run(gs, absl::Span(v), *workers, &foundHashes); + ENFORCE(!canceled); + return v; } } // namespace diff --git a/packager/VisibilityChecker.cc b/packager/VisibilityChecker.cc index 70fef14e9d..15754515af 100644 --- a/packager/VisibilityChecker.cc +++ b/packager/VisibilityChecker.cc @@ -80,7 +80,7 @@ class PropagateVisibility final { // Lookup the package name on the given root symbol, and mark the final symbol as exported. void exportRoot(core::GlobalState &gs, core::ClassOrModuleRef sym) { // For a package named `A::B`, the ClassDef that we see in this pass is for a symbol named - // `::A::B`. In order to make the name `A::B` visibile to packages that have imported + // `::A::B`. In order to make the name `A::B` visible to packages that have imported // `A::B`, we explicitly lookup and export them here. This is a design decision inherited from the previous // packages implementation, and we could remove it after migrating Stripe's codebase to not depend on package // names being exported by default. @@ -216,6 +216,16 @@ class PropagateVisibility final { } // If sym is an enum value, it can't be exported directly. Instead, its wrapping enum class must be exported. + // + // This was originally an implementation limitation, but is now an intentional choice. The considerations: + // + // - Each additional export was previously expensive in the rewriter-based package visibility checker. + // - Nothing prevents `MyEnum.deserialize('x')` to simply hide visibility violations. + // - It was hard for end users to know whether an enum had only exported some values intentionally. + // In practice people just exported the new values without thinking. + // + // See also how when we get a visibility violation for an enum value not being exported we export the entire + // enum, not the specific enum value, to avoid conflict-inducing churn on `__package.rb` files. auto enumClass = getEnumClassForEnumValue(ctx.state, sym); if (enumClass.exists()) { if (auto e = ctx.beginError(loc, core::errors::Packager::InvalidExport)) { @@ -236,8 +246,7 @@ class PropagateVisibility final { public: // Find uses of export and mark the symbols they mention as exported. - void postTransformSend(core::MutableContext ctx, ast::ExpressionPtr &tree) { - auto &send = ast::cast_tree_nonnull(tree); + void postTransformSend(core::MutableContext ctx, const ast::Send &send) { if (send.fun != core::Names::export_()) { return; } @@ -301,9 +310,7 @@ class PropagateVisibility final { } } - void preTransformClassDef(core::MutableContext ctx, ast::ExpressionPtr &tree) { - auto &original = ast::cast_tree_nonnull(tree); - + void preTransformClassDef(core::MutableContext ctx, const ast::ClassDef &original) { if (original.symbol == core::Symbols::root()) { return; } @@ -330,7 +337,7 @@ class PropagateVisibility final { pass.exportPackageRoots(gs); core::MutableContext ctx{gs, core::Symbols::root(), f.file}; - ast::TreeWalk::apply(ctx, pass, f.tree); + ast::ConstTreeWalk::apply(ctx, pass, f.tree); // if we used `export_all`, then there were no `export` // directives in the previous pass; we should instead export @@ -362,27 +369,7 @@ class VisibilityCheckerPass final { VisibilityCheckerPass(core::Context ctx, const core::packages::PackageInfo &package) : package{package}, insideTestFile{ctx.file.data(ctx).isPackagedTest()} {} - // `keep-def` will reference constants in a way that looks like a packaging violation, but is actually fine. This - // boolean allows for an early exit when we know we're in the context of processing one of these sends. Currently - // the only sends that we process this way will not have any nested method calls, but if that changes this will need - // to become a stack. - bool ignoreConstant = false; - - void preTransformSend(core::Context ctx, ast::ExpressionPtr &tree) { - auto &send = ast::cast_tree_nonnull(tree); - ENFORCE(!this->ignoreConstant, "keepForIde has nested sends"); - this->ignoreConstant = send.fun == core::Names::keepForIde(); - } - - void postTransformSend(core::Context ctx, ast::ExpressionPtr &tree) { - this->ignoreConstant = false; - } - void postTransformConstantLit(core::Context ctx, ast::ExpressionPtr &tree) { - if (this->ignoreConstant) { - return; - } - auto &lit = ast::cast_tree_nonnull(tree); if (!lit.symbol.isClassOrModule() && !lit.symbol.isFieldOrStaticField()) { return; @@ -401,6 +388,7 @@ class VisibilityCheckerPass final { e.setHeader("`{}` is defined in a test namespace and cannot be referenced in a non-test file", lit.symbol.show(ctx)); } + return; } auto &db = ctx.state.packageDB(); @@ -419,7 +407,7 @@ class VisibilityCheckerPass final { } // Did we use a constant that wasn't exported? - if (!isExported) { + if (!isExported && !db.allowRelaxedPackagerChecksFor(this->package.mangledName())) { if (auto e = ctx.beginError(lit.loc, core::errors::Packager::UsedPackagePrivateName)) { auto &pkg = ctx.state.packageDB().getPackageInfo(otherPackage); e.setHeader("`{}` resolves but is not exported from `{}`", lit.symbol.show(ctx), pkg.show(ctx)); @@ -456,7 +444,7 @@ class VisibilityCheckerPass final { if (auto e = ctx.beginError(lit.loc, core::errors::Packager::MissingImport)) { auto &pkg = ctx.state.packageDB().getPackageInfo(otherPackage); e.setHeader("`{}` resolves but its package is not imported", lit.symbol.show(ctx)); - bool isTestImport = otherFile.data(ctx).isPackagedTest(); + bool isTestImport = otherFile.data(ctx).isPackagedTest() || ctx.file.data(ctx).isPackagedTest(); e.addErrorLine(pkg.declLoc(), "Exported from package here"); if (auto exp = this->package.addImport(ctx, pkg, isTestImport)) { e.addAutocorrect(std::move(exp.value())); diff --git a/packager/packager.cc b/packager/packager.cc index 0f7db3590d..6506caf985 100644 --- a/packager/packager.cc +++ b/packager/packager.cc @@ -27,17 +27,18 @@ namespace { constexpr string_view PACKAGE_FILE_NAME = "__package.rb"sv; -bool isPrimaryTestNamespace(const core::NameRef ns) { - return ns == TEST_NAME; +bool isTestNamespace(const core::NameRef ns) { + return ns == core::packages::PackageDB::TEST_NAMESPACE; } -bool isSecondaryTestNamespace(const core::GlobalState &gs, const core::NameRef ns) { - const vector &secondaryTestPackageNamespaceRefs = gs.packageDB().secondaryTestPackageNamespaceRefs(); - return absl::c_find(secondaryTestPackageNamespaceRefs, ns) != secondaryTestPackageNamespaceRefs.end(); -} - -bool isTestNamespace(const core::GlobalState &gs, const core::NameRef ns) { - return isPrimaryTestNamespace(ns) || isSecondaryTestNamespace(gs, ns); +bool visibilityApplies(const core::packages::VisibleTo vt, absl::Span name) { + if (vt.visibleToType == core::packages::VisibleToType::Wildcard) { + // a wildcard will match if it's a proper prefix of the package name + return vt.packageName == name.subspan(0, vt.packageName.size()); + } else { + // otherwise it needs to be the same + return vt.packageName == name; + } } struct FullyQualifiedName { @@ -70,7 +71,7 @@ struct FullyQualifiedName { struct PackageName { core::LocOffsets loc; - core::NameRef mangledName = core::NameRef::noName(); + core::packages::MangledName mangledName; FullyQualifiedName fullName; FullyQualifiedName fullTestPkgName; @@ -88,13 +89,9 @@ struct PackageName { } }; -enum class ImportType { +enum class ImportType : uint8_t { Normal, Test, // test_import - - // "friend-import": This represents code that is re-mapped into a package's own public->private mapping or - // its private test namespace. - Friend, }; struct Import { @@ -154,7 +151,7 @@ class LexNext final { class PackageInfoImpl final : public core::packages::PackageInfo { public: - core::NameRef mangledName() const { + core::packages::MangledName mangledName() const { return name.mangledName; } @@ -174,10 +171,6 @@ class PackageInfoImpl final : public core::packages::PackageInfo { return declLoc_; } - bool legacyAutoloaderCompatibility() const { - return legacyAutoloaderCompatibility_; - } - bool exportAll() const { return exportAll_; } @@ -186,33 +179,35 @@ class PackageInfoImpl final : public core::packages::PackageInfo { return visibleToTests_; } - // The possible path prefixes associated with files in the package, including path separator at end. - vector packagePathPrefixes; PackageName name; // loc for the package definition. Full loc, from class to end keyword. Used for autocorrects. core::Loc loc; // loc for the package definition. Single line (just the class def). Used for error messages. core::Loc declLoc_; + // The possible path prefixes associated with files in the package, including path separator at end. + vector packagePathPrefixes = {}; // The names of each package imported by this package. - vector importedPackageNames; + vector importedPackageNames = {}; // List of exported items that form the body of this package's public API. // These are copied into every package that imports this package. - vector exports_; - - // Code in this package is _completely incompatible_ for path-based autoloading, and only works with the 'legacy' - // Sorbet-generated autoloader. - bool legacyAutoloaderCompatibility_; + vector exports_ = {}; // Whether this package should just export everything - bool exportAll_; + bool exportAll_ = false; // The other packages to which this package is visible. If this vector is empty, then it means // the package is fully public and can be imported by anything. - vector visibleTo_; + // + // The `VisibleToType` here represents whether to treat this line as a "wildcard". `Wildcard` means the + // `visible_to` line allows this package to be imported not just by the referenced package name + // but also any package name underneath it. `Normal` means the package can be imported + // by the referenced package name but not any child packages (unless they have a separate + // `visible_to` line of their own.) + vector> visibleTo_ = {}; // Whether `visible_to` directives should be ignored for test code - bool visibleToTests_; + bool visibleToTests_ = false; // PackageInfoImpl is the only implementation of PackageInfoImpl const static PackageInfoImpl &from(const core::packages::PackageInfo &pkg) { @@ -235,7 +230,7 @@ class PackageInfoImpl final : public core::packages::PackageInfo { return this->mangledName() == pkg.mangledName(); } - PackageInfoImpl() = default; + PackageInfoImpl(PackageName name, core::Loc loc, core::Loc declLoc_) : name(name), loc(loc), declLoc_(declLoc_) {} explicit PackageInfoImpl(const PackageInfoImpl &) = default; PackageInfoImpl &operator=(const PackageInfoImpl &) = delete; @@ -284,7 +279,7 @@ class PackageInfoImpl final : public core::packages::PackageInfo { ENFORCE(insertionLoc.exists()); // now find the appropriate place for it, specifically by - // finding the import that directly preceeds it, if any + // finding the import that directly precedes it, if any core::AutocorrectSuggestion suggestion( fmt::format("Import `{}` in package `{}`", info.name.toString(gs), name.toString(gs)), {{insertionLoc, @@ -320,7 +315,7 @@ class PackageInfoImpl final : public core::packages::PackageInfo { ENFORCE(insertionLoc.exists()); // now find the appropriate place for it, specifically by - // finding the import that directly preceeds it, if any + // finding the import that directly precedes it, if any auto strName = newExport.show(gs); core::AutocorrectSuggestion suggestion(fmt::format("Export `{}` in package `{}`", strName, name.toString(gs)), {{insertionLoc, fmt::format("\n export {}", strName)}}); @@ -360,15 +355,15 @@ class PackageInfoImpl final : public core::packages::PackageInfo { } return rv; } - vector> visibleTo() const { - vector> rv; + vector visibleTo() const { + vector rv; for (auto &v : visibleTo_) { - rv.emplace_back(v.fullName.parts); + rv.emplace_back(v.first.fullName.parts, v.second); } return rv; } - std::optional importsPackage(core::NameRef mangledName) const { + std::optional importsPackage(core::packages::MangledName mangledName) const { if (!mangledName.exists()) { return std::nullopt; } @@ -384,18 +379,26 @@ class PackageInfoImpl final : public core::packages::PackageInfo { return core::packages::ImportType::Normal; case ImportType::Test: return core::packages::ImportType::Test; - case ImportType::Friend: - ENFORCE(false, "Should not happen"); - return nullopt; } } }; -void checkPackageName(core::Context ctx, ast::UnresolvedConstantLit *constLit) { +// If the __package.rb file itself is a test file, then the whole package is a test-only package. +// For example, `test/__package.rb` is a test-only package (e.g. Critic in Stripe's codebase). +bool isTestOnlyPackage(const core::GlobalState &gs, const PackageInfoImpl &pkg) { + return pkg.loc.file().data(gs).isPackagedTest(); +} + +[[nodiscard]] bool validatePackageName(core::Context ctx, const ast::UnresolvedConstantLit *constLit) { + bool valid = true; while (constLit != nullptr) { if (absl::StrContains(constLit->cnst.shortName(ctx), "_")) { - // By forbidding package names to have an underscore, we can trivially convert between mangled names and - // unmangled names by replacing `_` with `::`. + // By forbidding package names to have an underscore, we can trivially convert between + // mangled names and unmangled names by replacing `_` with `::`. + // + // Even with packages into the symbol table this restriction is useful, because we have + // a lot of tooling that will create directory structures like Foo_Bar to store + // generated files associated with package Foo::Bar if (auto e = ctx.beginError(constLit->loc, core::errors::Packager::InvalidPackageName)) { e.setHeader("Package names cannot contain an underscore"); auto replacement = absl::StrReplaceAll(constLit->cnst.shortName(ctx), {{"_", ""}}); @@ -407,12 +410,15 @@ void checkPackageName(core::Context ctx, ast::UnresolvedConstantLit *constLit) { fmt::format("Replace `{}` with `{}`", constLit->cnst.shortName(ctx), replacement), {core::AutocorrectSuggestion::Edit{ctx.locAt(nameLoc), replacement}}}); } + valid = false; } constLit = ast::cast_tree(constLit->scope); } + + return valid; } -FullyQualifiedName getFullyQualifiedName(core::Context ctx, ast::UnresolvedConstantLit *constantLit) { +FullyQualifiedName getFullyQualifiedName(core::Context ctx, const ast::UnresolvedConstantLit *constantLit) { FullyQualifiedName fqn; fqn.loc = ctx.locAt(constantLit->loc); while (constantLit != nullptr) { @@ -425,26 +431,43 @@ FullyQualifiedName getFullyQualifiedName(core::Context ctx, ast::UnresolvedConst } // Gets the package name in `tree` if applicable. -PackageName getPackageName(core::MutableContext ctx, ast::UnresolvedConstantLit *constantLit) { +PackageName getPackageName(core::Context ctx, const ast::UnresolvedConstantLit *constantLit) { ENFORCE(constantLit != nullptr); PackageName pName; pName.loc = constantLit->loc; pName.fullName = getFullyQualifiedName(ctx, constantLit); - pName.fullTestPkgName = pName.fullName.withPrefix(TEST_NAME); + pName.fullTestPkgName = pName.fullName.withPrefix(core::packages::PackageDB::TEST_NAMESPACE); - // Foo::Bar => Foo_Bar_Package - pName.mangledName = core::packages::MangledName::mangledNameFromParts(ctx.state, pName.fullName.parts); + // pname.mangledName will be populated later, when we have a mutable GlobalState return pName; } -bool isReferenceToPackageSpec(core::Context ctx, ast::ExpressionPtr &expr) { +// TODO(jez) Rename this to lookupMangledName, and make it take a const GlobalState +void populateMangledName(core::GlobalState &gs, PackageName &pName) { + pName.mangledName = core::packages::MangledName::mangledNameFromParts(gs, pName.fullName.parts); +} + +bool isReferenceToPackageSpec(core::Context ctx, const ast::ExpressionPtr &expr) { auto constLit = ast::cast_tree(expr); return constLit != nullptr && constLit->cnst == core::Names::Constants::PackageSpec(); } -ast::ExpressionPtr prependName(ast::ExpressionPtr scope, core::NameRef prefix) { +void mustContainPackageDef(core::Context ctx, core::LocOffsets loc) { + // HACKFIX: Tolerate completely empty packages. LSP does not support the notion of a deleted file, and + // instead replaces deleted files with the empty string. It should really mark files as Tombstones instead. + if (!ctx.file.data(ctx).source().empty()) { + if (auto e = ctx.beginError(loc, core::errors::Packager::InvalidPackageDefinition)) { + e.setHeader("`{}` file must contain a package definition", "__package.rb"); + e.addErrorNote("Package definitions are class definitions like `{}`.\n" + " For more information, see http://go/package-layout", + "class Foo::Bar < PackageSpec"); + } + } +} + +ast::ExpressionPtr prependName(ast::ExpressionPtr scope) { auto lastConstLit = ast::cast_tree(scope); ENFORCE(lastConstLit != nullptr); while (auto constLit = ast::cast_tree(lastConstLit->scope)) { @@ -455,6 +478,20 @@ ast::ExpressionPtr prependName(ast::ExpressionPtr scope, core::NameRef prefix) { return scope; } +bool startsWithPackageSpecRegistry(const ast::UnresolvedConstantLit *cnst) { + while (cnst != nullptr) { + if (auto *scope = ast::cast_tree(cnst->scope)) { + return scope->symbol == core::Symbols::PackageSpecRegistry(); + } else if (auto *scope = ast::cast_tree(cnst->scope)) { + return startsWithPackageSpecRegistry(scope); + } else { + return false; + } + } + + return false; +} + ast::ExpressionPtr prependRoot(ast::ExpressionPtr scope) { auto *lastConstLit = &ast::cast_tree_nonnull(scope); while (auto constLit = ast::cast_tree(lastConstLit->scope)) { @@ -465,14 +502,37 @@ ast::ExpressionPtr prependRoot(ast::ExpressionPtr scope) { return scope; } -ast::UnresolvedConstantLit *verifyConstant(core::Context ctx, core::NameRef fun, ast::ExpressionPtr &expr) { +bool recursiveVerifyConstant(core::Context ctx, core::NameRef fun, const ast::ExpressionPtr &root, + const ast::ExpressionPtr &expr) { + if (ast::isa_tree(expr)) { + return true; + } + + auto target = ast::cast_tree(expr); + if (target == nullptr) { + if (auto e = ctx.beginError(root.loc(), core::errors::Packager::InvalidConfiguration)) { + e.setHeader("Argument to `{}` must be a constant", fun.show(ctx)); + } + return false; + } + + return recursiveVerifyConstant(ctx, fun, root, target->scope); +} + +const ast::UnresolvedConstantLit *verifyConstant(core::Context ctx, core::NameRef fun, const ast::ExpressionPtr &expr) { auto target = ast::cast_tree(expr); if (target == nullptr) { if (auto e = ctx.beginError(expr.loc(), core::errors::Packager::InvalidConfiguration)) { e.setHeader("Argument to `{}` must be a constant", fun.show(ctx)); } + return nullptr; + } + + if (recursiveVerifyConstant(ctx, fun, expr, target->scope)) { + return target; } - return target; + + return nullptr; } // Binary search to find a packages index in the global packages list @@ -502,8 +562,8 @@ uint16_t findPackageIndex(core::Context ctx, const PackageInfoImpl &pkg) { class PackageNamespaces final { using Bound = pair; - const vector &packages; // Mangled names sorted lexicographically - const PackageInfoImpl &filePkg; // Package for current file + const vector &packages; // Mangled names sorted lexicographically + const PackageInfoImpl &filePkg; // Package for current file // Current bounds: uint16_t begin; uint16_t end; @@ -517,16 +577,16 @@ class PackageNamespaces final { vector bounds; vector nameParts; vector namePartsLocs; - vector> curPkg; + vector> curPkg; core::NameRef foundTestNS = core::NameRef::noName(); core::LocOffsets foundTestNSLoc; static constexpr uint16_t SKIP_BOUND_VAL = 0; public: - PackageNamespaces(core::Context ctx, const PackageInfoImpl &filePkg, bool isTestFile) + PackageNamespaces(core::Context ctx, const PackageInfoImpl &filePkg) : packages(ctx.state.packageDB().packages()), filePkg(filePkg), begin(0), end(packages.size()), - isTestFile(isTestFile), filePkgIdx(findPackageIndex(ctx, filePkg)) { + isTestFile(ctx.file.data(ctx).isPackagedTest()), filePkgIdx(findPackageIndex(ctx, filePkg)) { ENFORCE(packages.size() < numeric_limits::max()); } @@ -549,9 +609,9 @@ class PackageNamespaces final { return res; } - core::NameRef packageForNamespace() const { + core::packages::MangledName packageForNamespace() const { if (curPkg.empty()) { - return core::NameRef::noName(); + return core::packages::MangledName(); } return curPkg.back().first; } @@ -574,13 +634,18 @@ class PackageNamespaces final { bool boundsEmpty = bounds.empty(); if (isTestFile && boundsEmpty && !foundTestNS.exists()) { - if (isPrimaryTestNamespace(name)) { + if (isTestNamespace(name)) { foundTestNS = name; foundTestNSLoc = loc; return; - } else if (!isTestNamespace(ctx, name)) { - // Inside a test file, but not inside a test namespace. Set bounds such that - // begin == end, stopping any subsequent search. + } else if (!isTestOnlyPackage(ctx, filePkg)) { + // In test-only packages, code can freely be inside the package's namespace, or the + // package's test namespace (i.e., either Critic or Test::Critic). + // Convention would say that the former is for test helpers and the latter is for + // runnable tests, but there is nothing enforcing this convention in Sorbet. + // + // If this *not* a test-only package, set bounds such that begin == end, stopping + // any subsequent search. bounds.emplace_back(begin, end); nameParts.emplace_back(name); namePartsLocs.emplace_back(loc); @@ -678,7 +743,16 @@ class PackageNamespaces final { // prefix. class EnforcePackagePrefix final { const PackageInfoImpl &pkg; - const bool isTestFile; + + // Whether code in this file must use the `Test::` namespace. + // + // Obviously tests *can* use the `Test::` namespace, but tests in test-only packages don't have to. + // + // (This is a wart of the original implementation, not an intentional design choice. It would + // probably be good in the future to require that runnable tests live in the `Test::` namespace + // for the package.) + const bool mustUseTestNamespace; + PackageNamespaces namespaces; // Counter to avoid duplicate errors: // - Only emit errors when depth is 0 @@ -690,13 +764,13 @@ class EnforcePackagePrefix final { vector> tmpNameParts; public: - EnforcePackagePrefix(core::Context ctx, const PackageInfoImpl &pkg, bool isTestFile) - : pkg(pkg), isTestFile(isTestFile), namespaces(ctx, pkg, isTestFile) { + EnforcePackagePrefix(core::Context ctx, const PackageInfoImpl &pkg) + : pkg(pkg), mustUseTestNamespace(ctx.file.data(ctx).isPackagedTest() && !isTestOnlyPackage(ctx, pkg)), + namespaces(ctx, pkg) { ENFORCE(pkg.exists()); } - void preTransformClassDef(core::Context ctx, ast::ExpressionPtr &tree) { - auto &classDef = ast::cast_tree_nonnull(tree); + void preTransformClassDef(core::Context ctx, const ast::ClassDef &classDef) { if (classDef.symbol == core::Symbols::root()) { // Ignore top-level return; @@ -706,7 +780,7 @@ class EnforcePackagePrefix final { return; } - ast::UnresolvedConstantLit *constantLit = ast::cast_tree(classDef.name); + auto *constantLit = ast::cast_tree(classDef.name); if (constantLit == nullptr) { return; } @@ -728,8 +802,7 @@ class EnforcePackagePrefix final { } } - void postTransformClassDef(core::Context ctx, ast::ExpressionPtr &tree) { - auto &classDef = ast::cast_tree_nonnull(tree); + void postTransformClassDef(core::Context ctx, const ast::ClassDef &classDef) { if (classDef.symbol == core::Symbols::root()) { // Sanity check bookkeeping ENFORCE(rootConsts == 0); @@ -745,7 +818,7 @@ class EnforcePackagePrefix final { } } - ast::UnresolvedConstantLit *constantLit = ast::cast_tree(classDef.name); + auto *constantLit = ast::cast_tree(classDef.name); if (constantLit == nullptr) { return; } @@ -753,12 +826,11 @@ class EnforcePackagePrefix final { popConstantLit(constantLit); } - void preTransformAssign(core::Context ctx, ast::ExpressionPtr &original) { + void preTransformAssign(core::Context ctx, const ast::Assign &asgn) { if (errorDepth > 0) { errorDepth++; return; } - auto &asgn = ast::cast_tree_nonnull(original); auto *lhs = ast::cast_tree(asgn.lhs); if (lhs != nullptr && rootConsts == 0) { @@ -776,36 +848,35 @@ class EnforcePackagePrefix final { } } - void postTransformAssign(core::Context ctx, ast::ExpressionPtr &original) { + void postTransformAssign(core::Context ctx, const ast::Assign &asgn) { if (errorDepth > 0) { errorDepth--; } } - void preTransformMethodDef(core::Context ctx, ast::ExpressionPtr &original) { + void preTransformMethodDef(core::Context ctx, const ast::MethodDef &def) { if (errorDepth > 0) { errorDepth++; return; } - auto &def = ast::cast_tree_nonnull(original); checkBehaviorLoc(ctx, def.declLoc); } - void postTransformMethodDef(core::Context ctx, ast::ExpressionPtr &original) { + void postTransformMethodDef(core::Context ctx, const ast::MethodDef &def) { if (errorDepth > 0) { errorDepth--; } } - void preTransformSend(core::Context ctx, ast::ExpressionPtr &original) { + void preTransformSend(core::Context ctx, const ast::Send &send) { if (errorDepth > 0) { errorDepth++; return; } - checkBehaviorLoc(ctx, original.loc()); + checkBehaviorLoc(ctx, send.loc); } - void postTransformSend(core::Context ctx, ast::ExpressionPtr &original) { + void postTransformSend(core::Context ctx, const ast::Send &send) { if (errorDepth > 0) { errorDepth--; } @@ -839,7 +910,7 @@ class EnforcePackagePrefix final { } private: - void pushConstantLit(core::Context ctx, ast::UnresolvedConstantLit *lit) { + void pushConstantLit(core::Context ctx, const ast::UnresolvedConstantLit *lit) { ENFORCE(tmpNameParts.empty()); auto prevDepth = namespaces.depth(); while (lit != nullptr) { @@ -858,15 +929,14 @@ class EnforcePackagePrefix final { } } - if (prevDepth == 0 && isTestFile && namespaces.depth() > 0) { - useTestNamespace = isPrimaryTestNamespace(tmpNameParts.back().first) || - !isSecondaryTestNamespace(ctx, pkg.name.fullName.parts[0]); + if (prevDepth == 0 && mustUseTestNamespace && namespaces.depth() > 0) { + useTestNamespace = true; } tmpNameParts.clear(); } - void popConstantLit(ast::UnresolvedConstantLit *lit) { + void popConstantLit(const ast::UnresolvedConstantLit *lit) { while (lit != nullptr) { if (rootConsts == 0) { namespaces.popName(); @@ -916,20 +986,14 @@ class EnforcePackagePrefix final { } }; -struct PackageInfoFinder { - unique_ptr info = nullptr; - vector exported; +struct PackageSpecBodyWalk { + PackageSpecBodyWalk(PackageInfoImpl &info) : info(info) {} - void postTransformCast(core::MutableContext ctx, ast::ExpressionPtr &tree) { - auto &cast = ast::cast_tree_nonnull(tree); - if (!ast::isa_tree(cast.typeExpr)) { - if (auto e = ctx.beginError(cast.typeExpr.loc(), core::errors::Packager::InvalidPackageExpression)) { - e.setHeader("Invalid expression in package: Arguments to functions must be literals"); - } - } - } + PackageInfoImpl &info; + vector exported; + bool foundFirstPackageSpec = false; - void postTransformSend(core::MutableContext ctx, ast::ExpressionPtr &tree) { + void postTransformSend(core::Context ctx, ast::ExpressionPtr &tree) { auto &send = ast::cast_tree_nonnull(tree); // Ignore methods @@ -958,11 +1022,6 @@ struct PackageInfoFinder { } } - if (info == nullptr) { - // We haven't yet entered the package class. - return; - } - if (send.fun == core::Names::export_() && send.numPosArgs() == 1) { // null indicates an invalid export. if (auto target = verifyConstant(ctx, core::Names::export_(), send.getPosArg(0))) { @@ -974,23 +1033,14 @@ struct PackageInfoFinder { if ((send.fun == core::Names::import() || send.fun == core::Names::testImport()) && send.numPosArgs() == 1) { // null indicates an invalid import. - if (auto target = verifyConstant(ctx, send.fun, send.getPosArg(0))) { - auto name = getPackageName(ctx, target); - ENFORCE(name.mangledName.exists()); - - if (name.mangledName == info->name.mangledName) { - if (auto e = ctx.beginError(target->loc, core::errors::Packager::NoSelfImport)) { - e.setHeader("Package `{}` cannot {} itself", info->name.toString(ctx), send.fun.toString(ctx)); - } - } - + if (auto *target = verifyConstant(ctx, send.fun, send.getPosArg(0))) { // Transform: `import Foo` -> `import ::Foo` auto importArg = move(send.getPosArg(0)); send.removePosArg(0); ENFORCE(send.numPosArgs() == 0); - send.addPosArg(prependName(move(importArg), core::Names::Constants::PackageSpecRegistry())); + send.addPosArg(prependName(move(importArg))); - info->importedPackageNames.emplace_back(move(name), method2ImportType(send)); + info.importedPackageNames.emplace_back(getPackageName(ctx, target), method2ImportType(send)); } } @@ -999,52 +1049,11 @@ struct PackageInfoFinder { auto importArg = move(send.getPosArg(0)); send.removePosArg(0); ENFORCE(send.numPosArgs() == 0); - send.addPosArg(prependName(move(importArg), core::Names::Constants::PackageSpecRegistry())); + send.addPosArg(prependName(move(importArg))); } if (send.fun == core::Names::exportAll() && send.numPosArgs() == 0) { - info->exportAll_ = true; - } - - if (send.fun == core::Names::autoloaderCompatibility() && send.numPosArgs() == 1) { - // Parse autoloader_compatibility DSL and set strict bit on PackageInfoImpl if configured - auto *compatibilityAnnotationLit = ast::cast_tree(send.getPosArg(0)); - if (compatibilityAnnotationLit == nullptr || !compatibilityAnnotationLit->isString()) { - if (auto e = ctx.beginError(send.loc, core::errors::Packager::InvalidConfiguration)) { - e.setHeader("Argument to `{}` must be a string literal", send.fun.show(ctx)); - - if (compatibilityAnnotationLit != nullptr && compatibilityAnnotationLit->isSymbol()) { - auto symbol = compatibilityAnnotationLit->asSymbol(); - if (symbol == core::Names::strict() || symbol == core::Names::legacy()) { - e.replaceWith("Convert to string arg", ctx.locAt(compatibilityAnnotationLit->loc), "\"{}\"", - symbol.shortName(ctx)); - } - } - } - - return; - } - - auto compatibilityAnnotation = compatibilityAnnotationLit->asString(); - if (compatibilityAnnotation != core::Names::legacy()) { - if (auto e = ctx.beginError(send.loc, core::errors::Packager::InvalidConfiguration)) { - if (compatibilityAnnotation == core::Names::strict()) { - e.setHeader("The 'strict' argument has been deprecated as an argument to `{}`", - send.fun.show(ctx)); - e.addErrorNote("If you wish to mark your " - "package as strictly path-based-autoloading compatible, do not provide an " - "autoloader_compatibility annotation"); - } else { - e.setHeader("Argument to `{}` can only be 'legacy'", send.fun.show(ctx)); - } - } - - return; - } - - if (compatibilityAnnotation == core::Names::legacy()) { - info->legacyAutoloaderCompatibility_ = true; - } + info.exportAll_ = true; } if (send.fun == core::Names::visibleTo() && send.numPosArgs() == 1) { @@ -1057,99 +1066,82 @@ struct PackageInfoFinder { } return; } - info->visibleToTests_ = true; - } else if (auto target = verifyConstant(ctx, send.fun, send.getPosArg(0))) { - auto name = getPackageName(ctx, target); - ENFORCE(name.mangledName.exists()); - - if (name.mangledName == info->name.mangledName) { - if (auto e = ctx.beginError(target->loc, core::errors::Packager::NoSelfImport)) { - e.setHeader("Useless `{}`, because {} cannot import itself", "visible_to", - info->name.toString(ctx)); + info.visibleToTests_ = true; + } else if (auto target = ast::cast_tree(send.getPosArg(0))) { + // Constant::* is valid Ruby, and parses as a send of the method * to Constant + // so let's take advantage of this to implement wildcards + if (target->fun != core::Names::star() || target->numPosArgs() > 0 || target->numKwArgs() > 0 || + target->hasBlock()) { + if (auto e = ctx.beginError(target->loc, core::errors::Packager::InvalidConfiguration)) { + e.setHeader("Argument to `{}` must be a constant or the string literal `{}`", + send.fun.show(ctx), "\"tests\""); } + return; } + if (auto *recv = verifyConstant(ctx, send.fun, target->recv)) { + auto importArg = move(target->recv); + send.removePosArg(0); + ENFORCE(send.numPosArgs() == 0); + send.addPosArg(prependName(move(importArg))); + info.visibleTo_.emplace_back(getPackageName(ctx, recv), core::packages::VisibleToType::Wildcard); + } else { + if (auto e = ctx.beginError(target->loc, core::errors::Packager::InvalidConfiguration)) { + e.setHeader("Argument to `{}` must be a constant or the string literal `{}`", + send.fun.show(ctx), "\"tests\""); + } + return; + } + } else if (auto *target = verifyConstant(ctx, send.fun, send.getPosArg(0))) { auto importArg = move(send.getPosArg(0)); send.removePosArg(0); ENFORCE(send.numPosArgs() == 0); - send.addPosArg(prependName(move(importArg), core::Names::Constants::PackageSpecRegistry())); + send.addPosArg(prependName(move(importArg))); - info->visibleTo_.emplace_back(move(name)); + info.visibleTo_.emplace_back(getPackageName(ctx, target), core::packages::VisibleToType::Normal); } } } - void preTransformClassDef(core::MutableContext ctx, ast::ExpressionPtr &tree) { + void preTransformClassDef(core::Context ctx, const ast::ExpressionPtr &tree) { auto &classDef = ast::cast_tree_nonnull(tree); if (classDef.symbol == core::Symbols::root()) { // Ignore top-level return; } - if (classDef.ancestors.size() != 1 || !isReferenceToPackageSpec(ctx, classDef.ancestors[0]) || - !ast::isa_tree(classDef.name)) { - if (auto e = ctx.beginError(classDef.declLoc, core::errors::Packager::InvalidPackageDefinition)) { - e.setHeader("Expected package definition of form `Foo::Bar < PackageSpec`"); - } - } else if (info == nullptr) { - auto nameTree = ast::cast_tree(classDef.name); - info = make_unique(); - checkPackageName(ctx, nameTree); - auto packageName = getPackageName(ctx, nameTree); - ENFORCE(packageName.mangledName.exists()); - - info->name = move(packageName); - info->loc = ctx.locAt(classDef.loc); - info->declLoc_ = ctx.locAt(classDef.declLoc); - - // `class Foo < PackageSpec` -> `class ::Foo < PackageSpec` - // This removes the PackageSpec's themselves from the top-level namespace - classDef.name = prependName(move(classDef.name), core::Names::Constants::PackageSpecRegistry()); - } else { + auto *nameTree = ast::cast_tree(classDef.name); + if (nameTree == nullptr) { + // Already reported an error + return; + } + + if (startsWithPackageSpecRegistry(nameTree)) { + this->foundFirstPackageSpec = true; + } else if (this->foundFirstPackageSpec) { if (auto e = ctx.beginError(classDef.declLoc, core::errors::Packager::MultiplePackagesInOneFile)) { e.setHeader("Package files can only declare one package"); - e.addErrorLine(info->loc, "Previous package declaration found here"); + e.addErrorLine(info.loc, "Previous package declaration found here"); } + } else { + mustContainPackageDef(ctx, tree.loc()); } } - void postTransformClassDef(core::MutableContext ctx, ast::ExpressionPtr &tree) { - auto &classDef = ast::cast_tree_nonnull(tree); - if (classDef.symbol == core::Symbols::root()) { - // Ignore top-level - return; - } - - return; - } - // Generate a list of FQNs exported by this package. No export may be a prefix of another. - void finalize(core::MutableContext ctx) { - if (info == nullptr) { - // HACKFIX: Tolerate completely empty packages. LSP does not support the notion of a deleted file, and - // instead replaces deleted files with the empty string. It should really mark files as Tombstones instead. - // Additional note: immutable incremental packager mode now depends on being allowed to set `info = nullptr` - // for the sake of doing best-effort packager runs. - if (!ctx.file.data(ctx).source().empty()) { - if (auto e = ctx.beginError(core::LocOffsets{0, 0}, core::errors::Packager::InvalidPackageDefinition)) { - e.setHeader("Package file must contain a package definition of form `Foo::Bar < PackageSpec`"); - } - } - return; - } - + void finalize(core::Context ctx) { if (exported.empty()) { return; } - if (info->exportAll()) { + if (info.exportAll()) { // we're only here because exports exist, which means if // `exportAll` is set then we've got conflicting // information about export; flag the exports as wrong for (auto it = exported.begin(); it != exported.end(); ++it) { if (auto e = ctx.beginError(it->fqn.loc.offsets(), core::errors::Packager::ExportConflict)) { e.setHeader("Package `{}` declares `{}` and therefore should not use explicit exports", - info->name.toString(ctx), "export_all!"); + info.name.toString(ctx), "export_all!"); } } } @@ -1184,8 +1176,8 @@ struct PackageInfoFinder { exported.erase(exported.begin() + *indIt); } - ENFORCE(info->exports_.empty()); - std::swap(exported, info->exports_); + ENFORCE(info.exports_.empty()); + std::swap(exported, info.exports_); } bool isSpecMethod(const sorbet::ast::Send &send) const { @@ -1194,7 +1186,6 @@ struct PackageInfoFinder { case core::Names::testImport().rawId(): case core::Names::export_().rawId(): case core::Names::restrictToService().rawId(): - case core::Names::autoloaderCompatibility().rawId(): case core::Names::visibleTo().rawId(): case core::Names::exportAll().rawId(): return true; @@ -1216,144 +1207,220 @@ struct PackageInfoFinder { } /* Forbid arbitrary computation in packages */ - - void illegalNode(core::MutableContext ctx, core::LocOffsets loc, string_view type) { - if (auto e = ctx.beginError(loc, core::errors::Packager::InvalidPackageExpression)) { - e.setHeader("Invalid expression in package: {} not allowed", type); + void illegalNode(core::Context ctx, const ast::ExpressionPtr &original) { + if (auto e = ctx.beginError(original.loc(), core::errors::Packager::InvalidPackageExpression)) { + e.setHeader("Invalid expression in package: `{}` not allowed", original.nodeName()); + e.addErrorNote("To learn about what's allowed in `{}` files, see http://go/package-layout", "__package.rb"); + } + } + + void preTransformExpressionPtr(core::Context ctx, const ast::ExpressionPtr &original) { + auto tag = original.tag(); + if ( // PackageSpec definition; handled above explicitly + tag == ast::Tag::ClassDef || + // Various DSL methods; handled above explicitly + tag == ast::Tag::Send || + // Arguments to DSL methods; always allowed + tag == ast::Tag::UnresolvedConstantLit || tag == ast::Tag::ConstantLit || tag == ast::Tag::Literal || + // Technically only in scopes of constant literals, but easier to just always allow + tag == ast::Tag::EmptyTree || + // Technically only as receiver of DSL method, but easier to just always allow + original.isSelfReference()) { + return; } - } - void preTransformIf(core::MutableContext ctx, ast::ExpressionPtr &original) { - illegalNode(ctx, original.loc(), "`if`"); + illegalNode(ctx, original); } +}; - void preTransformWhile(core::MutableContext ctx, ast::ExpressionPtr &original) { - illegalNode(ctx, original.loc(), "`while`"); - } +unique_ptr definePackage(const core::GlobalState &gs, ast::ParsedFile &package) { + ENFORCE(package.file.exists()); + ENFORCE(package.file.data(gs).isPackage()); + // Assumption: Root of AST is class. (This won't be true + // for `typed: ignore` files, so we should make sure to catch that + // elsewhere.) + ENFORCE(ast::isa_tree(package.tree)); + ENFORCE(ast::cast_tree_nonnull(package.tree).symbol == core::Symbols::root()); - void postTransformBreak(core::MutableContext ctx, ast::ExpressionPtr &original) { - illegalNode(ctx, original.loc(), "`break`"); - } + core::Context ctx(gs, core::Symbols::root(), package.file); - void postTransformRetry(core::MutableContext ctx, ast::ExpressionPtr &original) { - illegalNode(ctx, original.loc(), "`retry`"); - } + auto &rootClass = ast::cast_tree_nonnull(package.tree); - void postTransformNext(core::MutableContext ctx, ast::ExpressionPtr &original) { - illegalNode(ctx, original.loc(), "`next`"); - } + unique_ptr info; + bool reportedError = false; + for (auto &rootStmt : rootClass.rhs) { + if (info != nullptr) { + // No error here; let the error be reported in the tree walk later as a bad node type. + continue; + } - void preTransformReturn(core::MutableContext ctx, ast::ExpressionPtr &original) { - illegalNode(ctx, original.loc(), "`return`"); - } + auto *packageSpecClass = ast::cast_tree(rootStmt); + if (packageSpecClass == nullptr) { + // No error here; let this be reported in the tree walk later as a bad node type, + // or at the end of this function if no PackageSpec is found. + continue; + } - void preTransformRescueCase(core::MutableContext ctx, ast::ExpressionPtr &original) { - illegalNode(ctx, original.loc(), "`rescue case`"); - } + if (packageSpecClass->ancestors.size() != 1 || + !ast::isa_tree(packageSpecClass->name)) { + mustContainPackageDef(ctx, packageSpecClass->declLoc); + reportedError = true; + continue; + } - void preTransformRescue(core::MutableContext ctx, ast::ExpressionPtr &original) { - illegalNode(ctx, original.loc(), "`rescue`"); - } + if (!isReferenceToPackageSpec(ctx, packageSpecClass->ancestors[0])) { + mustContainPackageDef(ctx, packageSpecClass->ancestors[0].loc()); + reportedError = true; + continue; + } - void preTransformAssign(core::MutableContext ctx, ast::ExpressionPtr &original) { - illegalNode(ctx, original.loc(), "`=`"); - } + auto *nameTree = ast::cast_tree(packageSpecClass->name); + if (!validatePackageName(ctx, nameTree)) { + reportedError = true; + continue; + } - void preTransformHash(core::MutableContext ctx, ast::ExpressionPtr &original) { - illegalNode(ctx, original.loc(), "hash literals"); - } + // ---- Mutates the tree ---- + // `class Foo < PackageSpec` -> `class ::Foo < PackageSpec` + // This removes the PackageSpec's themselves from the top-level namespace + // + // We can't do this rewrite in rewriter, because this rewrite should only happen if + // `opts.stripePackages` is set. That would mean we would have to add another cache flavor, + // which would double the size of Sorbet's disk cache. + // + // Other than being able to say "we don't mutate the trees in packager" there's not much + // value in going that far (even namer mutates the trees; the packager fills a similar role). + packageSpecClass->name = prependName(move(packageSpecClass->name)); - void preTransformArray(core::MutableContext ctx, ast::ExpressionPtr &original) { - illegalNode(ctx, original.loc(), "array literals"); + // Pre-resolve the super class. This makes it easier to detect that this is a package + // spec-related class def in later passes without having to recursively walk up the constant + // lit's scope to find if it starts with . + auto superClassLoc = packageSpecClass->ancestors[0].loc(); + packageSpecClass->ancestors[0] = ast::make_expression( + superClassLoc, core::Symbols::PackageSpec(), move(packageSpecClass->ancestors[0])); + + info = make_unique(getPackageName(ctx, nameTree), ctx.locAt(packageSpecClass->loc), + ctx.locAt(packageSpecClass->declLoc)); } - void preTransformMethodDef(core::MutableContext ctx, ast::ExpressionPtr &original) { - illegalNode(ctx, original.loc(), "method definitions"); + // Only report an error if we didn't already + // (the one we reported will have been more descriptive than this one) + if (info == nullptr && !reportedError) { + auto errLoc = rootClass.rhs.empty() ? core::LocOffsets{0, 0} : rootClass.rhs[0].loc(); + mustContainPackageDef(ctx, errLoc); } - void preTransformBlock(core::MutableContext ctx, ast::ExpressionPtr &original) { - illegalNode(ctx, original.loc(), "blocks"); + return info; +} + +void rewritePackageSpec(const core::GlobalState &gs, ast::ParsedFile &package, PackageInfoImpl &info) { + PackageSpecBodyWalk bodyWalk(info); + core::Context ctx(gs, core::Symbols::root(), package.file); + ast::TreeWalk::apply(ctx, bodyWalk, package.tree); + bodyWalk.finalize(ctx); +} + +unique_ptr createAndPopulatePackageInfo(core::GlobalState &gs, ast::ParsedFile &package) { + auto info = definePackage(gs, package); + if (info == nullptr) { + return info; + } + populateMangledName(gs, info->name); + + rewritePackageSpec(gs, package, *info); + for (auto &importedPackageName : info->importedPackageNames) { + populateMangledName(gs, importedPackageName.name); + + if (importedPackageName.name.mangledName == info->name.mangledName) { + if (auto e = gs.beginError(core::Loc(package.file, importedPackageName.name.loc), + core::errors::Packager::NoSelfImport)) { + string import_; + switch (importedPackageName.type) { + case ImportType::Normal: + import_ = "import"; + break; + case ImportType::Test: + import_ = "test_import"; + break; + } + e.setHeader("Package `{}` cannot {} itself", info->name.toString(gs), import_); + } + } } - void preTransformInsSeq(core::MutableContext ctx, ast::ExpressionPtr &original) { - illegalNode(ctx, original.loc(), "`begin` and `end`"); + for (auto &visibleTo : info->visibleTo_) { + populateMangledName(gs, visibleTo.first); + + if (visibleTo.first.mangledName == info->name.mangledName) { + if (auto e = + gs.beginError(core::Loc(package.file, visibleTo.first.loc), core::errors::Packager::NoSelfImport)) { + e.setHeader("Useless `{}`, because {} cannot import itself", "visible_to", info->name.toString(gs)); + } + } } -}; -unique_ptr -runPackageInfoFinder(core::MutableContext ctx, ast::ParsedFile &package, - const vector &extraPackageFilesDirectoryUnderscorePrefixes, - const vector &extraPackageFilesDirectorySlashPrefixes) { - ENFORCE(package.file.exists()); - ENFORCE(package.file.data(ctx).isPackage()); - // Assumption: Root of AST is class. (This won't be true - // for `typed: ignore` files, so we should make sure to catch that - // elsewhere.) - ENFORCE(ast::isa_tree(package.tree)); - ENFORCE(ast::cast_tree_nonnull(package.tree).symbol == core::Symbols::root()); - auto packageFilePath = package.file.data(ctx).path(); - ENFORCE(FileOps::getFileName(packageFilePath) == PACKAGE_FILE_NAME); - PackageInfoFinder finder; - ast::TreeWalk::apply(ctx, finder, package.tree); - finder.finalize(ctx); - if (finder.info) { - const auto numPrefixes = - extraPackageFilesDirectoryUnderscorePrefixes.size() + extraPackageFilesDirectorySlashPrefixes.size() + 1; - finder.info->packagePathPrefixes.reserve(numPrefixes); - finder.info->packagePathPrefixes.emplace_back(packageFilePath.substr(0, packageFilePath.find_last_of('/') + 1)); - const string_view shortName = finder.info->name.mangledName.shortName(ctx.state); - const string_view dirNameFromShortName = shortName.substr(0, shortName.rfind(core::PACKAGE_SUFFIX)); - - for (const string &prefix : extraPackageFilesDirectoryUnderscorePrefixes) { - // Project_FooBar -- munge with underscore - string additionalDirPath = absl::StrCat(prefix, dirNameFromShortName, "/"); - finder.info->packagePathPrefixes.emplace_back(std::move(additionalDirPath)); - } - - for (const string &prefix : extraPackageFilesDirectorySlashPrefixes) { - // project/Foo_bar -- convert camel-case to snake-case and munge with slash - std::stringstream ss; - ss << prefix; - for (int i = 0; i < dirNameFromShortName.length(); i++) { - if (dirNameFromShortName[i] == '_') { - ss << '/'; - } else if (i == 0 || dirNameFromShortName[i - 1] == '_') { - // Capitalizing first letter in each directory name to avoid conflicts with ignored directories, - // which tend to be all lower case - char upper = std::toupper(dirNameFromShortName[i]); - ss << std::move(upper); - } else { - if (isupper(dirNameFromShortName[i])) { - ss << '_'; // snake-case munging - } + auto extraPackageFilesDirectoryUnderscorePrefixes = gs.packageDB().extraPackageFilesDirectoryUnderscorePrefixes(); + auto extraPackageFilesDirectorySlashPrefixes = gs.packageDB().extraPackageFilesDirectorySlashPrefixes(); - char lower = std::tolower(dirNameFromShortName[i]); - ss << std::move(lower); + const auto numPrefixes = + extraPackageFilesDirectoryUnderscorePrefixes.size() + extraPackageFilesDirectorySlashPrefixes.size() + 1; + info->packagePathPrefixes.reserve(numPrefixes); + auto packageFilePath = package.file.data(gs).path(); + ENFORCE(FileOps::getFileName(packageFilePath) == PACKAGE_FILE_NAME); + info->packagePathPrefixes.emplace_back(packageFilePath.substr(0, packageFilePath.find_last_of('/') + 1)); + const string_view shortName = info->name.mangledName.mangledName.shortName(gs); + const string_view dirNameFromShortName = shortName.substr(0, shortName.rfind(core::PACKAGE_SUFFIX)); + + for (const string &prefix : extraPackageFilesDirectoryUnderscorePrefixes) { + // Project_FooBar -- munge with underscore + string additionalDirPath = absl::StrCat(prefix, dirNameFromShortName, "/"); + info->packagePathPrefixes.emplace_back(std::move(additionalDirPath)); + } + + for (const string &prefix : extraPackageFilesDirectorySlashPrefixes) { + // project/Foo_bar -- convert camel-case to snake-case and munge with slash + std::stringstream ss; + ss << prefix; + for (int i = 0; i < dirNameFromShortName.length(); i++) { + if (dirNameFromShortName[i] == '_') { + ss << '/'; + } else if (i == 0 || dirNameFromShortName[i - 1] == '_') { + // Capitalizing first letter in each directory name to avoid conflicts with ignored directories, + // which tend to be all lower case + char upper = std::toupper(dirNameFromShortName[i]); + ss << std::move(upper); + } else { + if (isupper(dirNameFromShortName[i])) { + ss << '_'; // snake-case munging } - } - ss << '/'; - std::string additionalDirPath(ss.str()); - finder.info->packagePathPrefixes.emplace_back(std::move(additionalDirPath)); + char lower = std::tolower(dirNameFromShortName[i]); + ss << std::move(lower); + } } + ss << '/'; + + std::string additionalDirPath(ss.str()); + info->packagePathPrefixes.emplace_back(std::move(additionalDirPath)); } - return move(finder.info); + + return info; } } // namespace // Validate that the package file is marked `# typed: strict`. -ast::ParsedFile validatePackage(core::Context ctx, ast::ParsedFile file) { +void validatePackage(core::Context ctx) { const auto &packageDB = ctx.state.packageDB(); - auto &absPkg = packageDB.getPackageForFile(ctx, file.file); + auto &absPkg = packageDB.getPackageForFile(ctx, ctx.file); if (!absPkg.exists()) { // We already produced an error on this package when producing its package info. // The correct course of action is to abort the transform. - return file; + return; } auto &pkgInfo = PackageInfoImpl::from(absPkg); - bool skipImportVisibilityCheck = packageDB.skipImportVisibilityCheckFor(pkgInfo.mangledName()); + bool skipImportVisibilityCheck = packageDB.allowRelaxedPackagerChecksFor(pkgInfo.mangledName()); if (!skipImportVisibilityCheck) { for (auto &i : pkgInfo.importedPackageNames) { @@ -1374,8 +1441,9 @@ ast::ParsedFile validatePackage(core::Context ctx, ast::ParsedFile file) { continue; } - bool allowed = absl::c_any_of(otherPkg.visibleTo(), - [&absPkg](const auto &other) { return other == absPkg.fullName(); }); + bool allowed = absl::c_any_of(otherPkg.visibleTo(), [&absPkg](const auto &other) { + return visibilityApplies(other, absPkg.fullName()); + }); if (!allowed) { if (auto e = ctx.beginError(i.name.loc, core::errors::Packager::ImportNotVisible)) { @@ -1389,17 +1457,15 @@ ast::ParsedFile validatePackage(core::Context ctx, ast::ParsedFile file) { } // Sanity check: __package.rb files _must_ be typed: strict - if (file.file.data(ctx).originalSigil < core::StrictLevel::Strict) { + if (ctx.file.data(ctx).originalSigil < core::StrictLevel::Strict) { if (auto e = ctx.beginError(core::LocOffsets{0, 0}, core::errors::Packager::PackageFileMustBeStrict)) { e.setHeader("Package files must be at least `{}`", "# typed: strict"); } } - - return file; } -ast::ParsedFile rewritePackagedFile(core::Context ctx, ast::ParsedFile parsedFile) { - auto &file = parsedFile.file.data(ctx); +void validatePackagedFile(core::Context ctx, const ast::ExpressionPtr &tree) { + auto &file = ctx.file.data(ctx); ENFORCE(!file.isPackage()); if (file.isPayload()) { @@ -1410,7 +1476,7 @@ ast::ParsedFile rewritePackagedFile(core::Context ctx, ast::ParsedFile parsedFil // // We normally skip running the packager when building in sorbet-orig mode, which computes // the stored state, but payload files can be retypechecked by the fast path during LSP. - return parsedFile; + return; } auto &pkg = ctx.state.packageDB().getPackageForFile(ctx, ctx.file); @@ -1421,43 +1487,21 @@ ast::ParsedFile rewritePackagedFile(core::Context ctx, ast::ParsedFile parsedFil "of its parent directories", ctx.file.data(ctx).path(), PACKAGE_FILE_NAME); } - return parsedFile; + return; } auto &pkgImpl = PackageInfoImpl::from(pkg); - EnforcePackagePrefix enforcePrefix(ctx, pkgImpl, file.isPackagedTest()); - ast::ShallowWalk::apply(ctx, enforcePrefix, parsedFile.tree); - - return parsedFile; + EnforcePackagePrefix enforcePrefix(ctx, pkgImpl); + ast::ConstShallowWalk::apply(ctx, enforcePrefix, tree); } -// Re-write source files to be in packages. This is only called if no package definitions were changed. -vector rewriteFilesFast(core::GlobalState &gs, vector files) { - Timer timeit(gs.tracer(), "packager.rewriteFilesFast"); - for (auto &file : files) { - core::Context ctx(gs, core::Symbols::root(), file.file); - if (file.file.data(gs).isPackage()) { - { - core::MutableContext ctx(gs, core::Symbols::root(), file.file); - runPackageInfoFinder(ctx, file, gs.packageDB().extraPackageFilesDirectoryUnderscorePrefixes(), - gs.packageDB().extraPackageFilesDirectorySlashPrefixes()); - } - // Re-write imports and exports: - file = validatePackage(ctx, move(file)); - } else { - file = rewritePackagedFile(ctx, move(file)); - } - } - return files; -} - -vector Packager::findPackages(core::GlobalState &gs, WorkerPool &workers, - vector files) { +void Packager::findPackages(core::GlobalState &gs, absl::Span files) { // Ensure files are in canonical order. + // TODO(jez) Is this sort redundant? Should we move this sort to callers? fast_sort(files, [](const auto &a, const auto &b) -> bool { return a.file < b.file; }); - // Step 1: Find packages and determine their imports/exports. + // Find packages and determine their imports/exports. { Timer timeit(gs.tracer(), "packager.findPackages"); core::UnfreezeNameTable unfreeze(gs); @@ -1467,24 +1511,8 @@ vector Packager::findPackages(core::GlobalState &gs, WorkerPool continue; } - if (file.file.data(gs).strictLevel == core::StrictLevel::Ignore) { - // if the `__package.rb` file is at `typed: - // ignore`, then we haven't even parsed it, which - // means none of the other stuff here is going to - // actually work (since it all assumes we've got a - // file to actually analyze.) If we've got a - // `typed: ignore` package, then skip it. - - // `File::isPackage` is determined by the filename, so we need to clear it explicitly at this point - // to ensure that the file is no longer treated as a package. - file.file.data(gs).setIsPackage(false); - - continue; - } - core::MutableContext ctx(gs, core::Symbols::root(), file.file); - auto pkg = runPackageInfoFinder(ctx, file, gs.packageDB().extraPackageFilesDirectoryUnderscorePrefixes(), - gs.packageDB().extraPackageFilesDirectorySlashPrefixes()); + auto pkg = createAndPopulatePackageInfo(ctx, file); if (pkg == nullptr) { // There was an error creating a PackageInfoImpl for this file, and getPackageInfo has already // surfaced that error to the user. Nothing to do here. @@ -1502,16 +1530,12 @@ vector Packager::findPackages(core::GlobalState &gs, WorkerPool } } } - - return files; } -void Packager::setPackageNameOnFiles(core::GlobalState &gs, const vector &files) { - std::vector> mapping; +void Packager::setPackageNameOnFiles(core::GlobalState &gs, absl::Span files) { + std::vector> mapping; mapping.reserve(files.size()); - // Step 1a, add package references to every file. This could be parallel if needed, file access will be unique and - // no symbols will be allocated. { auto &db = gs.packageDB(); for (auto &f : files) { @@ -1530,18 +1554,12 @@ void Packager::setPackageNameOnFiles(core::GlobalState &gs, const vector &files) { - std::vector> mapping; +void Packager::setPackageNameOnFiles(core::GlobalState &gs, absl::Span files) { + std::vector> mapping; mapping.reserve(files.size()); - // Step 1a, add package references to every file. This could be parallel if needed, file access will be unique and - // no symbols will be allocated. { auto &db = gs.packageDB(); for (auto &f : files) { @@ -1560,21 +1578,16 @@ void Packager::setPackageNameOnFiles(core::GlobalState &gs, const vector Packager::run(core::GlobalState &gs, WorkerPool &workers, vector files) { +void Packager::run(core::GlobalState &gs, WorkerPool &workers, absl::Span files) { ENFORCE(!gs.runningUnderAutogen, "Packager pass does not run in autogen"); Timer timeit(gs.tracer(), "packager"); - files = findPackages(gs, workers, std::move(files)); + findPackages(gs, files); setPackageNameOnFiles(gs, files); - // Step 2: - // * Find package files and rewrite them into virtual AST mappings. - // * Find files within each package and rewrite each to be wrapped by their virtual package namespace. { Timer timeit(gs.tracer(), "packager.rewritePackagesAndFiles"); @@ -1595,9 +1608,9 @@ vector Packager::run(core::GlobalState &gs, WorkerPool &workers core::Context ctx(gs, core::Symbols::root(), job.file); if (file.isPackage()) { - job = validatePackage(ctx, move(job)); + validatePackage(ctx); } else { - job = rewritePackagedFile(ctx, move(job)); + validatePackagedFile(ctx, job.tree); } } } @@ -1607,19 +1620,28 @@ vector Packager::run(core::GlobalState &gs, WorkerPool &workers barrier.Wait(); } - - return files; } -vector Packager::runIncremental(core::GlobalState &gs, vector files) { +// TODO(jez) Parallelize this +vector Packager::runIncremental(const core::GlobalState &gs, vector files) { // Note: This will only run if packages have not been changed (byte-for-byte equality). // TODO(nroman-stripe) This could be further incrementalized to avoid processing all packages by // building in an understanding of the dependencies between packages. - auto namesUsed = gs.namesUsedTotal(); - files = rewriteFilesFast(gs, move(files)); - ENFORCE(gs.namesUsedTotal() == namesUsed); - return files; - Packager::setPackageNameOnFiles(gs, files); + Timer timeit(gs.tracer(), "packager.runIncremental"); + for (auto &file : files) { + core::Context ctx(gs, core::Symbols::root(), file.file); + if (file.file.data(gs).isPackage()) { + // Only rewrites the `__package.rb` file to mention `` and + // report some syntactic packager errors. + auto info = definePackage(gs, file); + if (info != nullptr) { + rewritePackageSpec(gs, file, *info); + } + validatePackage(ctx); + } else { + validatePackagedFile(ctx, file.tree); + } + } return files; } @@ -1656,13 +1678,14 @@ struct PackageFiles { class PackageInfoFormatter final { const core::GlobalState &gs; - const UnorderedMap &packageFiles; + const UnorderedMap &packageFiles; public: - PackageInfoFormatter(const core::GlobalState &gs, const UnorderedMap &packageFiles) + PackageInfoFormatter(const core::GlobalState &gs, + const UnorderedMap &packageFiles) : gs(gs), packageFiles(packageFiles) {} - void operator()(std::string *out, core::NameRef mangledName) const { + void operator()(std::string *out, core::packages::MangledName mangledName) const { const auto &pkg = gs.packageDB().getPackageInfo(mangledName); out->append("{{"); out->append("\"name\":"); @@ -1690,7 +1713,7 @@ class PackageInfoFormatter final { void Packager::dumpPackageInfo(const core::GlobalState &gs, std::string outputFile) { const auto &pkgDB = gs.packageDB(); // package => files - UnorderedMap packageFiles; + UnorderedMap packageFiles; for (uint32_t i = 1; i < gs.filesUsed(); ++i) { core::FileRef file(i); const auto &pkg = pkgDB.getPackageForFile(gs, file); diff --git a/packager/packager.h b/packager/packager.h index 2e99eccfe8..3e360fd990 100644 --- a/packager/packager.h +++ b/packager/packager.h @@ -9,8 +9,6 @@ class WorkerPool; } namespace sorbet::packager { -const core::NameRef TEST_NAME = core::Names::Constants::Test(); - /** * This pass transforms package files (`foo/__package.rb`) from * @@ -30,8 +28,8 @@ const core::NameRef TEST_NAME = core::Names::Constants::Test(); * * to: * - * class ::Project::Foo < PackageSpec - * import ::Project::Bar + * class ::Project::Foo < PackageSpec + * import ::Project::Bar * * export Package::Baz * end @@ -42,22 +40,20 @@ const core::NameRef TEST_NAME = core::Names::Constants::Test(); */ class Packager final { public: - static std::vector findPackages(core::GlobalState &gs, WorkerPool &workers, - std::vector files); + static void findPackages(core::GlobalState &gs, absl::Span files); - static std::vector run(core::GlobalState &gs, WorkerPool &workers, - std::vector files); + static void run(core::GlobalState &gs, WorkerPool &workers, absl::Span files); // Run packager incrementally. Note: `files` must contain all packages files. Does not support package changes. - static std::vector runIncremental(core::GlobalState &gs, std::vector files); + static std::vector runIncremental(const core::GlobalState &gs, std::vector files); static void dumpPackageInfo(const core::GlobalState &gs, std::string output); // For each file, set its package name. - static void setPackageNameOnFiles(core::GlobalState &gs, const std::vector &files); + static void setPackageNameOnFiles(core::GlobalState &gs, absl::Span files); // For each file, set its package name. - static void setPackageNameOnFiles(core::GlobalState &gs, const std::vector &files); + static void setPackageNameOnFiles(core::GlobalState &gs, absl::Span files); static core::SymbolRef getEnumClassForEnumValue(const core::GlobalState &gs, core::SymbolRef sym); diff --git a/packager/rbi_gen.cc b/packager/rbi_gen.cc index 03a90c9a7a..d3cf245a0f 100644 --- a/packager/rbi_gen.cc +++ b/packager/rbi_gen.cc @@ -118,7 +118,7 @@ core::SymbolRef lookupFQN(const core::GlobalState &gs, const vectorfindMemberNoDealias(gs, name); + auto result = scope.asClassOrModuleRef().data(gs)->findMemberNoDealias(name); if (!result.exists()) { return core::Symbols::noClassOrModule(); } @@ -218,7 +218,7 @@ class RBIExporter final { return ""; } enqueueSymbolsInType(type); - auto options = core::ShowOptions{}.withShowForRBI(); + auto options = core::ShowOptions{}.withUseValidSyntax(); return type.show(gs, options); } @@ -243,7 +243,7 @@ class RBIExporter final { const core::TypeConstraint *constraint) { ENFORCE(method.exists()); ENFORCE(method.data(gs)->dealiasMethod(gs) == method); - // handle this case anyways so that we don't crash in prod when this method is mis-used + // handle this case anyways so that we don't crash in prod when this method is misused if (!method.exists()) { return ""; } @@ -316,7 +316,7 @@ class RBIExporter final { string prettyDefForMethod(core::MethodRef method) { ENFORCE(method.exists()); - // handle this case anyways so that we don't crash in prod when this method is mis-used + // handle this case anyways so that we don't crash in prod when this method is misused if (!method.exists()) { return ""; } @@ -1208,7 +1208,7 @@ class RBIExporter final { RBIGenerator::RBIOutput emit() { RBIGenerator::RBIOutput output; - output.baseFilePath = pkg.mangledName().show(gs); + output.baseFilePath = pkg.mangledName().mangledName.show(gs); vector exports; vector testExports; @@ -1289,7 +1289,7 @@ UnorderedSet RBIGenerator::buildPackageNamespace(core::G return packageNamespaces; } -RBIGenerator::RBIOutput RBIGenerator::runOnce(const core::GlobalState &gs, core::NameRef pkgName, +RBIGenerator::RBIOutput RBIGenerator::runOnce(const core::GlobalState &gs, core::packages::MangledName pkgName, const UnorderedSet &packageNamespaces) { auto &pkg = gs.packageDB().getPackageInfo(pkgName); ENFORCE(pkg.exists()); @@ -1303,14 +1303,14 @@ void RBIGenerator::run(core::GlobalState &gs, const UnorderedSet>(packages.size()); + auto inputq = make_shared>(packages.size()); for (auto package : packages) { inputq->push(move(package), 1); } workers.multiplexJob( "RBIGenerator", [inputq, outputDir, &threadBarrier, &rogs = std::as_const(gs), &packageNamespaces]() { - core::NameRef job; + core::packages::MangledName job; for (auto result = inputq->try_pop(job); !result.done(); result = inputq->try_pop(job)) { if (result.gotItem()) { auto output = runOnce(rogs, job, packageNamespaces); @@ -1335,7 +1335,7 @@ void RBIGenerator::run(core::GlobalState &gs, const UnorderedSet &packageNamespaces, - core::NameRef package, string outputDir, WorkerPool &workers) { + core::packages::MangledName package, string outputDir, WorkerPool &workers) { auto output = runOnce(gs, package, packageNamespaces); if (!output.rbi.empty()) { FileOps::write(absl::StrCat(outputDir, "/", output.baseFilePath, ".package.rbi"), output.rbi); diff --git a/packager/rbi_gen.h b/packager/rbi_gen.h index 056885e47f..cc39356407 100644 --- a/packager/rbi_gen.h +++ b/packager/rbi_gen.h @@ -20,7 +20,7 @@ class RBIGenerator final { // Exposed for testing. static UnorderedSet buildPackageNamespace(core::GlobalState &gs, WorkerPool &workers); - static RBIOutput runOnce(const core::GlobalState &gs, core::NameRef pkg, + static RBIOutput runOnce(const core::GlobalState &gs, core::packages::MangledName pkg, const UnorderedSet &packageNamespaces); // Generate RBIs for all packages present in the package database of `gs`. @@ -29,7 +29,7 @@ class RBIGenerator final { // Generate RBIs for a single package, provided as the mangled package name `package`. static void runSinglePackage(core::GlobalState &gs, const UnorderedSet &packageNamespaces, - core::NameRef package, std::string outputDir, WorkerPool &workers); + core::packages::MangledName package, std::string outputDir, WorkerPool &workers); }; } // namespace sorbet::packager diff --git a/parser/Builder.cc b/parser/Builder.cc index 2a75d18de6..ee73971521 100644 --- a/parser/Builder.cc +++ b/parser/Builder.cc @@ -430,14 +430,14 @@ class Builder::Impl { if (body == nullptr) { return make_unique(loc, sorbet::parser::NodeVec()); } - if (auto *b = parser::cast_node(body.get())) { + if (parser::isa_node(body.get())) { if (begin == nullptr && end == nullptr) { // Synthesized (begin) from compstmt "a; b" or (mlhs) // from multi_lhs "(a, b) = *foo". return body; } } - if (auto *m = parser::cast_node(body.get())) { + if (parser::isa_node(body.get())) { return body; } sorbet::parser::NodeVec stmts; @@ -536,7 +536,7 @@ class Builder::Impl { } bool isNumblock = false; - if (auto *numparams = parser::cast_node(args.get())) { + if (parser::isa_node(args.get())) { isNumblock = true; } @@ -1302,7 +1302,7 @@ class Builder::Impl { } unique_ptr multi_lhs1(const token *begin, unique_ptr item, const token *end) { - if (auto *mlhs = parser::cast_node(item.get())) { + if (parser::isa_node(item.get())) { return item; } sorbet::parser::NodeVec args; diff --git a/parser/Parser.cc b/parser/Parser.cc index 969cb8a243..385161ba83 100644 --- a/parser/Parser.cc +++ b/parser/Parser.cc @@ -146,7 +146,7 @@ unique_ptr Parser::run(core::GlobalState &gs, core::FileRef file, Parser:: auto ast = builder.build(driver.get(), settings.traceParser); // Always report the original parse errors. If we need to run the parser again, we'll only - // report the hints. Always reporting the origial parse errors ensures that the user can always + // report the hints. Always reporting the original parse errors ensures that the user can always // see the real cause in case the hints are misleading. if (!driver->diagnostics.empty()) { file.data(gs).setHasParseErrors(true); diff --git a/parser/parser/BUILD b/parser/parser/BUILD index 510afe2313..ffec33566f 100644 --- a/parser/parser/BUILD +++ b/parser/parser/BUILD @@ -10,8 +10,7 @@ ragel( ragel_options = select({ # emscripten builds are usually dealing with small amounts of Ruby source, # so they don't need a super-fast parser. - "@com_stripe_ruby_typer//tools/config:webasm_opt": [], - "@com_stripe_ruby_typer//tools/config:webasm": [], + "@platforms//cpu:wasm32": [], "@com_stripe_ruby_typer//tools/config:opt": ["-G1"], "//conditions:default": [], }), @@ -104,6 +103,7 @@ cc_library( hdrs = glob(["include/**/*.hh"]), copts = [ "-Wno-unused-const-variable", + "-Wno-unused-but-set-variable", ] + select({ # What we really want here is to say "it's never ok to use -Os, because # that will take 2+ minutes to compile." We have to do this to override diff --git a/parser/parser/cc/driver.cc b/parser/parser/cc/driver.cc index 8fc24a9aad..a565d295f8 100644 --- a/parser/parser/cc/driver.cc +++ b/parser/parser/cc/driver.cc @@ -154,7 +154,7 @@ ForeignPtr base_driver::rewind_and_munge_body_if_dedented(SelfPtr self, token_t this->rewind_to_tok_start(endToken); return body; } else if (this->lex.compare_indent_level(bodyStartToken, beginToken) <= 0) { - // Not even the very first thing in the body is indented. Treat this like emtpy method. + // Not even the very first thing in the body is indented. Treat this like empty method. this->rewind_and_reset(headerEndPos); auto emptyBody = this->build.compstmt(self, this->alloc.node_list()); return emptyBody; diff --git a/parser/parser/cc/grammars/typedruby.ypp b/parser/parser/cc/grammars/typedruby.ypp index 9bc7a71834..5bdb50e9ed 100644 --- a/parser/parser/cc/grammars/typedruby.ypp +++ b/parser/parser/cc/grammars/typedruby.ypp @@ -4335,7 +4335,7 @@ f_opt_paren_args: f_paren_args // (imagine that the user is in the middle of typing out the `b`, and we want to provide completion) // We can't do that, because by default, the `tLABEL` (x:) that comes after the `b` tricks the parser // into thinking that an fcall is coming, like how `b x: y` is valid Ruby code. But Ruby has banned - // that syntax in args (for the obvious reason that it's ambigous with keyword args). So it gets + // that syntax in args (for the obvious reason that it's ambiguous with keyword args). So it gets // halfway done with parsing it as an fcall, and realizes theres an error. // // And the last bit is that since there's no tCOMMA (the user is still filling in the arg, hasn't diff --git a/parser/parser/cc/lexer.rl b/parser/parser/cc/lexer.rl index 225fcc16d0..1062ca587a 100644 --- a/parser/parser/cc/lexer.rl +++ b/parser/parser/cc/lexer.rl @@ -194,7 +194,7 @@ int lexer::compare_indent_level(token_t left, token_t right) { i++; if (i > 100) { // Attempt to defeat pathologically long whitespace prefixes. - // This will basically mean falling back to the indendation-agnostic behavior. + // This will basically mean falling back to the indentation-agnostic behavior. // We could alternatively attempt to return some sort of error state here. return 0; } diff --git a/parser/tools/generate_ast.cc b/parser/tools/generate_ast.cc index fa10801bb9..a9108312ed 100644 --- a/parser/tools/generate_ast.cc +++ b/parser/tools/generate_ast.cc @@ -439,7 +439,7 @@ NodeDef nodes[] = { "__LINE__", vector(), }, - // local variable referense + // local variable reference { "LVar", "lvar", @@ -611,7 +611,7 @@ NodeDef nodes[] = { vector({{"body", FieldType::Node}}), }, // wraps the sole argument of a 1-arg block - // because there's a diffence between m {|a|} and m{|a,|} + // because there's a difference between m {|a|} and m{|a,|} { "Procarg0", "procarg0", diff --git a/payload/payload.cc b/payload/payload.cc index c7cf127b8e..4b91a75c3a 100644 --- a/payload/payload.cc +++ b/payload/payload.cc @@ -96,7 +96,7 @@ bool retainGlobalState(core::GlobalState &gs, const realmain::options::Options & if (kvstore && gs.wasModified() && !gs.hadCriticalError()) { auto maybeGsBytes = kvstore->read(GLOBAL_STATE_KEY); // Verify that no other GlobalState was written to kvstore between when we read GlobalState and wrote it - // into the databaase. + // into the database. if (kvstoreUnchangedSinceGsCreation(gs, maybeGsBytes.data)) { // Generate a new UUID, since this GS has changed since it was read. gs.kvstoreUuid = Random::uniformU4(); diff --git a/payload/text/populate.cc b/payload/text/populate.cc index 34db09bd29..dc232be136 100644 --- a/payload/text/populate.cc +++ b/payload/text/populate.cc @@ -22,14 +22,19 @@ void populateRBIsInto(unique_ptr &gs) { realmain::options::Options emptyOpts; unique_ptr kvstore; auto workers = WorkerPool::create(emptyOpts.threads, gs->tracer()); - auto indexed = realmain::pipeline::index(*gs, payloadFiles, emptyOpts, *workers, kvstore); + auto indexed = + realmain::pipeline::index(*gs, absl::Span(payloadFiles), emptyOpts, *workers, kvstore); + + // We don't run the payload with any packager options, so we can skip pipeline::package() + // While we want the FoundMethodHashes to end up in the payload, these hashes (including // LocalGlobalStateHashes and UsageHash) are not computed until `computeFileHashes` is called in // realmain when the `storeState` flag is passed. This means that e.g. sorbet-orig -e // '[0].to_set' will typecheck (using text-based payload) but never calculate hashes for the // payload files (because neither `--lsp` nor `--store-state` was passed). auto foundMethodHashes = nullptr; - realmain::pipeline::resolve(gs, move(indexed), emptyOpts, *workers, foundMethodHashes); // result is thrown away + realmain::pipeline::nameAndResolve(gs, move(indexed), emptyOpts, *workers, foundMethodHashes); + // ^ result is thrown away gs->ensureCleanStrings = false; } diff --git a/plugin_injector/BUILD b/plugin_injector/BUILD deleted file mode 100644 index a84acca860..0000000000 --- a/plugin_injector/BUILD +++ /dev/null @@ -1,23 +0,0 @@ -cc_library( - name = "plugin_injector", - srcs = [ - "plugin_injector.cc", - ], - linkstatic = select({ - "@com_stripe_ruby_typer//tools/config:linkshared": 0, - "//conditions:default": 1, - }), - visibility = ["//visibility:public"], - deps = [ - "//compiler/Errors", - "//compiler/IREmitter", - "//compiler/ObjectFileEmitter", - "@com_google_absl//absl/cleanup", - "@com_stripe_ruby_typer//ast", - "@com_stripe_ruby_typer//cfg", - "@com_stripe_ruby_typer//common", - "@com_stripe_ruby_typer//main/options", - "@com_stripe_ruby_typer//main/pipeline/semantic_extension:interface", - "@cxxopts", - ], -) diff --git a/plugin_injector/plugin_injector.cc b/plugin_injector/plugin_injector.cc deleted file mode 100644 index 957b09f79e..0000000000 --- a/plugin_injector/plugin_injector.cc +++ /dev/null @@ -1,436 +0,0 @@ -// These violate our poisons so have to happen first -#include "llvm/IR/DIBuilder.h" -#include "llvm/IR/IRBuilder.h" -#include "llvm/IR/LLVMContext.h" -#include "llvm/IR/Module.h" -#include "llvm/Support/Host.h" -#include "llvm/Transforms/Utils/Cloning.h" - -#include "absl/cleanup/cleanup.h" -#include "absl/strings/str_split.h" -#include "absl/synchronization/mutex.h" -#include "ast/ast.h" -#include "cfg/CFG.h" -#include "common/FileOps.h" -#include "common/typecase.h" -#include "compiler/Core/AbortCompilation.h" -#include "compiler/Core/CompilerState.h" -#include "compiler/Core/FailCompilation.h" -#include "compiler/Core/OptimizerException.h" -#include "compiler/Errors/Errors.h" -#include "compiler/IREmitter/IREmitter.h" -#include "compiler/IREmitter/IREmitterHelpers.h" -#include "compiler/IREmitter/Payload/PayloadLoader.h" -#include "compiler/ObjectFileEmitter/ObjectFileEmitter.h" -#include "core/ErrorQueue.h" -#include "main/options/options.h" -#include "main/pipeline/semantic_extension/SemanticExtension.h" -#include -#include - -using namespace std; -namespace sorbet::pipeline::semantic_extension { -namespace { -string objectFileName(const core::GlobalState &gs, const core::FileRef &f) { - string sourceFile(f.data(gs).path()); - if (sourceFile[0] == '.' && sourceFile[1] == '/') { - sourceFile = sourceFile.substr(2); - } - return sourceFile; -} - -void ensureOutputDir(const string_view outputDir, string_view fileName) { - string path(outputDir); - - auto finalSlashPos = fileName.rfind('/'); - if (finalSlashPos == string_view::npos) { - return; - } - - // Trim the filename so that we only iterate directory parts below - fileName.remove_suffix(fileName.size() - finalSlashPos); - - for (auto part : absl::StrSplit(fileName, '/')) { - absl::StrAppend(&path, "/", part); - FileOps::ensureDir(path); - } -} - -} // namespace - -// Sorbet's pipeline is architected such that one thread is typechecking one file at a time. -// This struct allows us to store state local to one typechecking thread. -class TypecheckThreadState { -public: - // Creating an LLVMContext is somewhat expensive, so we don't want to create more than one per thread. - llvm::LLVMContext lctx; - - // The file this thread is currently typechecking - core::FileRef file; - - // The output of IREmitter for a file. - // - // "Combined" because all the methods in this file get compiled and accumulated into this Module, - // but each method is typechecked (and thus compiled) individually. - unique_ptr combinedModule; - - unique_ptr debugInfo; - llvm::DICompileUnit *compileUnit = nullptr; - - // The basic-block that holds the initialization of string constants. - llvm::BasicBlock *allocRubyIdsEntry = nullptr; - - compiler::StringTable stringTable; - - compiler::IDTable idTable; - - compiler::RubyStringTable rubyStringTable; - - // The function that holds calls to global constructors - // - // This works as a replacement to llvm.global_ctors so that we can delay initialization until after - // our sorbet_ruby version check. - llvm::BasicBlock *globalConstructorsEntry = nullptr; - - bool aborted = false; - - const unique_ptr codegenPayload; - - TypecheckThreadState() : codegenPayload(compiler::PayloadLoader::readDefaultModule(lctx)) {} -}; - -class LLVMSemanticExtension : public SemanticExtension { - optional compiledOutputDir; - optional irOutputDir; - bool forceCompiled; - mutable struct { - UnorderedMap> states; - absl::Mutex mtx; - } mutableState; - - shared_ptr getTypecheckThreadState() const { - { - absl::ReaderMutexLock lock(&mutableState.mtx); - if (mutableState.states.contains(std::this_thread::get_id())) { - return mutableState.states.at(std::this_thread::get_id()); - } - } - { - absl::WriterMutexLock lock(&mutableState.mtx); - return mutableState.states[std::this_thread::get_id()] = make_shared(); - } - } - - bool shouldCompile(const core::GlobalState &gs, const core::FileRef &f) const { - if (!compiledOutputDir.has_value()) { - return false; - } - if (forceCompiled) { - return true; - } - // TODO parse this the same way as `typed:` - return isCompiledTrue(gs, f); - } - - bool isCompiledTrue(const core::GlobalState &gs, const core::FileRef &f) const { - return f.data(gs).compiledLevel == core::CompiledLevel::True; - } - - // There are a certain class of method calls that sorbet generates for auxiliary - // information for IDEs that do not have meaning at runtime. These calls are all - // of the form `foo(bar(baz))`, i.e. straight line code with no variables. - // We take advantage of this special knowledge to do a simple form of dead code - // elimination. - void deleteDoNothingSends(cfg::CFG &cfg) const { - for (auto &block : cfg.basicBlocks) { - UnorderedSet refsToDelete; - for (auto i = block->exprs.rbegin(), e = block->exprs.rend(); i != e; ++i) { - auto &binding = *i; - if (auto *send = cfg::cast_instruction(binding.value)) { - switch (send->fun.rawId()) { - case core::Names::keepForIde().rawId(): - // TODO: figure out why we can't delete this. - // case core::Names::keepForCfg().rawId(): - refsToDelete.emplace(binding.bind.variable); - break; - default: - if (!refsToDelete.contains(binding.bind.variable)) { - continue; - } - break; - } - - // We're binding a ref that is unneeded, so anything that this - // instruction requires must be unneeded as well. - for (auto &arg : send->args) { - refsToDelete.emplace(arg.variable); - } - refsToDelete.emplace(send->recv.variable); - } else if (auto *read = cfg::cast_instruction(binding.value)) { - refsToDelete.emplace(read->what); - } - } - - auto e = std::remove_if(block->exprs.begin(), block->exprs.end(), - [&](auto &binding) { return refsToDelete.contains(binding.bind.variable); }); - block->exprs.erase(e, block->exprs.end()); - } - } - -public: - LLVMSemanticExtension(optional compiledOutputDir, optional irOutputDir, bool forceCompiled) { - this->compiledOutputDir = move(compiledOutputDir); - this->irOutputDir = move(irOutputDir); - this->forceCompiled = forceCompiled; - } - - virtual void run(core::MutableContext &ctx, ast::ClassDef *klass) const override{}; - - virtual void prepareForTypechecking(const core::GlobalState &gs) override{}; - - virtual void typecheck(const core::GlobalState &gs, core::FileRef file, cfg::CFG &cfg, - ast::MethodDef &md) const override { - if (!shouldCompile(gs, file)) { - return; - } - - // This method will be handled as a VM_METHOD_TYPE_IVAR method by the - // standard VM mechanisms, so we don't need to generate code for it. - if (md.flags.isAttrReader && !md.symbol.data(gs)->flags.isFinal) { - return; - } - - if (md.symbol.data(gs)->name == core::Names::staticInit()) { - auto attachedClass = md.symbol.data(gs)->owner.data(gs)->attachedClass(gs); - if (attachedClass.exists() && attachedClass.data(gs)->name.isTEnumName(gs)) { - return; - } - } - - auto threadState = getTypecheckThreadState(); - if (threadState->aborted) { - return; - } - - deleteDoNothingSends(cfg); - - llvm::LLVMContext &lctx = threadState->lctx; - unique_ptr &module = threadState->combinedModule; - unique_ptr &debug = threadState->debugInfo; - llvm::DICompileUnit *&compUnit = threadState->compileUnit; - // TODO: Figure out why this isn't true - // ENFORCE(absl::c_find(cfg.symbol.data(gs)->locs(), md->loc) != cfg.symbol.data(gs)->locs().end(), - // loc.toString(gs)); - ENFORCE(file.exists()); - if (!module) { - ENFORCE(threadState->globalConstructorsEntry == nullptr); - ENFORCE(debug == nullptr); - ENFORCE(compUnit == nullptr); - module = llvm::CloneModule(*threadState->codegenPayload); - - module->addModuleFlag(llvm::Module::Warning, "Debug Info Version", llvm::DEBUG_METADATA_VERSION); - module->addModuleFlag(llvm::Module::Override, "cf-protection-return", 1); - module->addModuleFlag(llvm::Module::Override, "cf-protection-branch", 1); - - if (llvm::Triple(llvm::sys::getProcessTriple()).isOSDarwin()) { - // osx only supports dwarf2 - module->addModuleFlag(llvm::Module::Warning, "Dwarf Version", 2); - } - - debug = std::make_unique(*module); - - // NOTE: we use C here because our generated functions follow its abi - auto language = llvm::dwarf::DW_LANG_C; - auto filename = file.data(gs).path(); - auto isOptimized = false; - auto runtimeVersion = 0; - compUnit = debug->createCompileUnit( - language, debug->createFile(llvm::StringRef(filename.data(), filename.size()), "."), "Sorbet LLVM", - isOptimized, "", runtimeVersion); - - threadState->file = file; - threadState->stringTable.clear(); - threadState->idTable.clear(); - threadState->rubyStringTable.clear(); - - { - auto linkageType = llvm::Function::InternalLinkage; - auto argTys = std::vector{llvm::Type::getInt64Ty(lctx)}; - auto varArgs = false; - auto ft = llvm::FunctionType::get(llvm::Type::getVoidTy(lctx), argTys, varArgs); - auto globalConstructors = llvm::Function::Create(ft, linkageType, "sorbet_globalConstructors", *module); - threadState->allocRubyIdsEntry = llvm::BasicBlock::Create(lctx, "allocRubyIds", globalConstructors); - threadState->globalConstructorsEntry = - llvm::BasicBlock::Create(lctx, "globalConstructors", globalConstructors); - } - - } else { - ENFORCE(threadState->file == file); - ENFORCE(threadState->globalConstructorsEntry != nullptr); - } - ENFORCE(threadState->file.exists()); - compiler::CompilerState state(gs, lctx, module.get(), debug.get(), compUnit, threadState->file, - threadState->allocRubyIdsEntry, threadState->globalConstructorsEntry, - threadState->stringTable, threadState->idTable, threadState->rubyStringTable); - absl::Cleanup dropInternalState = [&] { - threadState->aborted = true; - module = nullptr; - threadState->file = core::FileRef(); - }; - try { - compiler::IREmitter::run(state, cfg, md); - string fileName = objectFileName(gs, file); - compiler::IREmitter::buildInitFor(state, cfg.symbol, fileName); - std::move(dropInternalState).Cancel(); - } catch (sorbet::compiler::AbortCompilation &) { - threadState->aborted = true; - std::move(dropInternalState).Cancel(); - } catch (sorbet::compiler::OptimizerException &oe) { - threadState->aborted = true; - std::move(dropInternalState).Cancel(); - // This exception is thrown from within an optimizer pass, where GlobalState - // is not available, so we need to emit an error here, where we do have - // access to GlobalState. - if (auto e = gs.beginError(core::Loc(file, 0, 0), core::errors::Compiler::OptimizerFailure)) { - e.setHeader("{}", oe.what()); - } - } - }; - - virtual void finishTypecheckFile(const core::GlobalState &gs, const core::FileRef &f) const override { - if (!shouldCompile(gs, f)) { - return; - } - - if (f.data(gs).minErrorLevel() >= core::StrictLevel::True) { - if (f.data(gs).source().find("frozen_string_literal: true"sv) == string_view::npos) { - compiler::failCompilation(gs, core::Loc(f, 0, 0), - "Compiled files need to have '# frozen_string_literal: true'"); - } - } else { - compiler::failCompilation(gs, core::Loc(f, 0, 0), - "Compiled files must be at least '# typed: true' or above"); - } - - auto threadState = getTypecheckThreadState(); - llvm::LLVMContext &lctx = threadState->lctx; - - unique_ptr module = move(threadState->combinedModule); - unique_ptr debug = move(threadState->debugInfo); - threadState->compileUnit = nullptr; - - // It is possible, though unusual, to never have typecheck() called. - if (!module) { - ENFORCE(!threadState->file.exists()); - return; - } - if (threadState->aborted) { - threadState->file = core::FileRef(); - return; - } - - { - llvm::IRBuilder<> builder(lctx); - - threadState->stringTable.defineGlobalVariables(lctx, *module); - - builder.SetInsertPoint(threadState->allocRubyIdsEntry); - threadState->idTable.defineGlobalVariables(lctx, *module, builder); - threadState->rubyStringTable.defineGlobalVariables(lctx, *module, builder); - builder.CreateBr(threadState->globalConstructorsEntry); - - builder.SetInsertPoint(threadState->globalConstructorsEntry); - builder.CreateRetVoid(); - } - - threadState->globalConstructorsEntry = nullptr; - - ENFORCE(threadState->file.exists()); - ENFORCE(f == threadState->file); - - ENFORCE(threadState->combinedModule == nullptr); - ENFORCE(threadState->globalConstructorsEntry == nullptr); - threadState->file = core::FileRef(); - - debug->finalize(); - - string fileName = objectFileName(gs, f); - ensureOutputDir(compiledOutputDir.value(), fileName); - if (irOutputDir.has_value()) { - ensureOutputDir(irOutputDir.value(), fileName); - } - if (!compiler::ObjectFileEmitter::run(gs.tracer(), lctx, move(module), compiledOutputDir.value(), irOutputDir, - fileName)) { - compiler::failCompilation(gs, core::Loc(f, 0, 0), "Object file emitter failed"); - } - }; - - virtual void finishTypecheck(const core::GlobalState &gs) const override {} - - virtual ~LLVMSemanticExtension(){}; - virtual std::unique_ptr deepCopy(const core::GlobalState &from, core::GlobalState &to) override { - return make_unique(this->compiledOutputDir, this->irOutputDir, this->forceCompiled); - }; - virtual void merge(const core::GlobalState &from, core::GlobalState &to, core::NameSubstitution &subst) override {} -}; - -class LLVMSemanticExtensionProvider : public SemanticExtensionProvider { -public: - virtual void injectOptions(cxxopts::Options &optsBuilder) const override { - optsBuilder.add_options("compiler")( - "compiled-out-dir", "Output compiled code (*.rb.so or *.rb.bundle) to directory, which must already exist", - cxxopts::value()); - optsBuilder.add_options("compiler")("llvm-ir-dir", "Output LLVM IR to directory, which must already exist", - cxxopts::value()); - optsBuilder.add_options("compiler")("force-compiled", "Force all files to this compiled level", - cxxopts::value()); - }; - virtual std::unique_ptr readOptions(cxxopts::ParseResult &providedOptions) const override { - if (providedOptions["version"].as()) { - fmt::print("Sorbet compiler {}\n", sorbet_full_version_string); - throw EarlyReturnWithCode(0); - } - - optional compiledOutputDir; - optional irOutputDir; - bool forceCompiled = false; - if (providedOptions.count("compiled-out-dir") > 0) { - auto outputDir = providedOptions["compiled-out-dir"].as(); - - if (!FileOps::dirExists(outputDir)) { - fmt::print("Missing output directory {}\n", outputDir); - throw EarlyReturnWithCode(1); - } - - compiledOutputDir = outputDir; - } - if (providedOptions.count("llvm-ir-dir") > 0) { - auto outputDir = providedOptions["llvm-ir-dir"].as(); - - if (!FileOps::dirExists(outputDir)) { - fmt::print("Missing output directory {}\n", outputDir); - throw EarlyReturnWithCode(1); - } - - irOutputDir = outputDir; - } - if (providedOptions.count("force-compiled") > 0) { - forceCompiled = providedOptions["force-compiled"].as(); - } - - return make_unique(compiledOutputDir, irOutputDir, forceCompiled); - }; - virtual std::unique_ptr defaultInstance() const override { - optional compiledOutputDir; - optional irOutputDir; - auto forceCompile = false; - return make_unique(compiledOutputDir, irOutputDir, forceCompile); - } - virtual ~LLVMSemanticExtensionProvider(){}; -}; - -vector SemanticExtensionProvider::getProviders() { - static LLVMSemanticExtensionProvider provider; - return {&provider}; -} -} // namespace sorbet::pipeline::semantic_extension diff --git a/proto/BUILD b/proto/BUILD index e6e54dfcdc..e05327247c 100644 --- a/proto/BUILD +++ b/proto/BUILD @@ -1,4 +1,5 @@ -load("@com_google_protobuf//:protobuf.bzl", "cc_proto_library") +load("@com_google_protobuf//bazel:cc_proto_library.bzl", "cc_proto_library") +load("@com_google_protobuf//bazel:proto_library.bzl", "proto_library") filegroup( name = "protos", @@ -6,12 +7,13 @@ filegroup( visibility = ["//visibility:public"], ) +proto_library( + name = "protos_proto", + srcs = [":protos"], +) + cc_proto_library( name = "proto", - srcs = glob(["**/*.proto"]), - linkstatic = select({ - "//tools/config:linkshared": 0, - "//conditions:default": 1, - }), visibility = ["//visibility:public"], + deps = [":protos_proto"], ) diff --git a/proto/Name.proto b/proto/Name.proto index b43dbae44d..6be26d5112 100644 --- a/proto/Name.proto +++ b/proto/Name.proto @@ -27,6 +27,7 @@ message Name { PACKAGER = 13; // PACKAGER_PRIVATE = 14; MANGLE_RENAME_OVERLOAD = 15; + STRUCT = 16; }; Kind kind = 1; diff --git a/proto/pay-server/BUILD b/proto/pay-server/BUILD index 985913e0c6..c52a8fc3c3 100644 --- a/proto/pay-server/BUILD +++ b/proto/pay-server/BUILD @@ -1,12 +1,13 @@ -load("@com_google_protobuf//:protobuf.bzl", "cc_proto_library") +load("@com_google_protobuf//bazel:cc_proto_library.bzl", "cc_proto_library") +load("@com_google_protobuf//bazel:proto_library.bzl", "proto_library") + +proto_library( + name = "pay-server_proto", + srcs = glob(["**/*.proto"]), +) cc_proto_library( name = "pay-server", - srcs = glob(["**/*.proto"]), - # has to come last: https://github.com/bazelbuild/bazel/issues/5924 - linkstatic = select({ - "//tools/config:linkshared": 0, - "//conditions:default": 1, - }), visibility = ["//visibility:public"], + deps = [":pay-server_proto"], ) diff --git a/rbi/BUILD b/rbi/BUILD index dc8945f904..b9d1a1e3e4 100644 --- a/rbi/BUILD +++ b/rbi/BUILD @@ -1,8 +1,11 @@ +load("@bazel_skylib//rules:diff_test.bzl", "diff_test") +load("@aspect_bazel_lib//lib:write_source_files.bzl", "write_source_file") + filegroup( name = "rbi", srcs = glob( ["**/*.rbi"], - ) + [":generate_procs_rbi"], + ), visibility = ["//visibility:public"], ) @@ -18,7 +21,20 @@ cc_binary( genrule( name = "generate_procs_rbi", - outs = ["procs.rbi"], + outs = ["procs.rbi.gen"], cmd = "$(location :generate_procs) $@", tools = [":generate_procs"], ) + +diff_test( + name = "validate_procs_rbi", + failure_message = "\n\n\"rbi/procs.rbi\" is autogenerated. Please run:\n bazel run //rbi:update_procs_rbi\nto update it.\n", + file1 = "procs.rbi", + file2 = ":generate_procs_rbi", +) + +write_source_file( + name = "update_procs_rbi", + in_file = ":generate_procs_rbi", + out_file = "procs.rbi", +) diff --git a/rbi/core/array.rbi b/rbi/core/array.rbi index e1feccd1a1..69f845685f 100644 --- a/rbi/core/array.rbi +++ b/rbi/core/array.rbi @@ -2934,7 +2934,7 @@ class Array < Object ### ### @example returning {Integer} ### T::Array[Float].new.sum(&:to_f) #=> 0 - ### @example returing generic type + ### @example returning generic type ### ['a', 'b'].sum{|t| t.ord.to_f} #=> 195.0 sig do type_parameters(:T).params( diff --git a/rbi/core/basic_object.rbi b/rbi/core/basic_object.rbi index 74736ee2be..82be9d0b92 100644 --- a/rbi/core/basic_object.rbi +++ b/rbi/core/basic_object.rbi @@ -223,6 +223,10 @@ class BasicObject end def equal?(other); end + # Default constructor. Does not do anything interesting. + sig {void} + def initialize(); end + # Evaluates a string containing Ruby source code, or the given block, within # the context of the receiver (*obj*). In order to set the context, the # variable `self` is set to *obj* while the code is executing, giving the code @@ -290,4 +294,10 @@ class BasicObject .returns(T.type_parameter(:U)) end def instance_exec(*args, &blk); end + + sig {overridable.params(method: Symbol).returns(T.untyped)} + private def singleton_method_added(method); end + + sig {params(method: Symbol, args: T.untyped).returns(T.untyped)} + private def method_missing(method, *args); end end diff --git a/rbi/core/binding.rbi b/rbi/core/binding.rbi index 523cc6d1d6..6cc4579aa2 100644 --- a/rbi/core/binding.rbi +++ b/rbi/core/binding.rbi @@ -141,4 +141,63 @@ class Binding < Object # Returns the Ruby source filename and line number of the binding object. sig {returns([String, Integer])} def source_location(); end + + # Opens an IRB session where +binding.irb+ is called which allows for + # interactive debugging. You can call any methods or variables available in + # the current scope, and mutate state if you need to. + # + # + # Given a Ruby file called +potato.rb+ containing the following code: + # + # class Potato + # def initialize + # @cooked = false + # binding.irb + # puts "Cooked potato: #{@cooked}" + # end + # end + # + # Potato.new + # + # Running ruby potato.rb will open an IRB session where + # +binding.irb+ is called, and you will see the following: + # + # $ ruby potato.rb + # + # From: potato.rb @ line 4 : + # + # 1: class Potato + # 2: def initialize + # 3: @cooked = false + # => 4: binding.irb + # 5: puts "Cooked potato: #{@cooked}" + # 6: end + # 7: end + # 8: + # 9: Potato.new + # + # irb(#):001:0> + # + # You can type any valid Ruby code and it will be evaluated in the current + # context. This allows you to debug without having to run your code repeatedly: + # + # irb(#):001:0> @cooked + # => false + # irb(#):002:0> self.class + # => Potato + # irb(#):003:0> caller.first + # => ".../2.5.1/lib/ruby/2.5.0/irb/workspace.rb:85:in `eval'" + # irb(#):004:0> @cooked = true + # => true + # + # You can exit the IRB session with the +exit+ command. Note that exiting will + # resume execution where +binding.irb+ had paused it, as you can see from the + # output printed to standard output in this example: + # + # irb(#):005:0> exit + # Cooked potato: true + # + # See IRB for more information. + sig {params(show_code: T::Boolean).void} + def irb(show_code: true); end end diff --git a/rbi/core/class.rbi b/rbi/core/class.rbi index 1559d3f034..9ac40a91d9 100644 --- a/rbi/core/class.rbi +++ b/rbi/core/class.rbi @@ -74,8 +74,6 @@ class Class < Module # because RBI files are not typechecked anyways. has_attached_class!(:out) - ### TODO(jez) After T::Class change: Use `T.attached_class` in `allocate` - # Allocates space for a new object of *class*'s class and does not call # initialize on the new instance. The returned object must be an instance of # *class*. @@ -93,7 +91,7 @@ class Class < Module # # klass.allocate.initialized? #=> false # ``` - sig {returns(T.untyped)} + sig {returns(T.attached_class)} def allocate(); end @@ -130,7 +128,7 @@ class Class < Module # # If a block is given, it is passed the class object, and the block is # evaluated in the context of this class like class_eval. - sig { params(blk: T.untyped).returns(T::Class[Object]) } + sig { params(blk: T.untyped).returns(T::Class[T.untyped]) } sig do type_parameters(:Parent) .params( diff --git a/rbi/core/data.rbi b/rbi/core/data.rbi index e31f4ce053..22008c82e0 100644 --- a/rbi/core/data.rbi +++ b/rbi/core/data.rbi @@ -93,7 +93,7 @@ class Data < Object # ``` # # Note that member-less Data is acceptable and might be a useful technique - # for defining several homogenous data classes, like + # for defining several homogeneous data classes, like # # ```ruby # class HTTPFetcher @@ -152,7 +152,7 @@ class Data < Object # Measure.new(amount: 1, unit: 'km') # #=> # # - # # Alternative shorter intialization with [] + # # Alternative shorter initialization with [] # Measure[1, 'km'] # #=> # # Measure[amount: 1, unit: 'km'] @@ -226,12 +226,10 @@ class Data < Object # return Qnil; # } # ``` - sig { params(args: T.untyped).returns(Data) } - sig { params(kwargs: T.untyped).returns(Data) } + sig { params(args: T.untyped, kwargs: T.untyped).returns(Data) } def new(*args, **kwargs); end - sig { params(args: T.untyped).returns(Data) } - sig { params(kwargs: T.untyped).returns(Data) } + sig { params(args: T.untyped, kwargs: T.untyped).returns(Data) } def self.[](*args, **kwargs); end # Returns `true` if `other` is the same class as `self`, and all members are diff --git a/rbi/core/dir.rbi b/rbi/core/dir.rbi index 2b794cf350..a1506f78a8 100644 --- a/rbi/core/dir.rbi +++ b/rbi/core/dir.rbi @@ -589,9 +589,10 @@ class Dir < Object params( pattern: T.any(String, Pathname), base: T.nilable(T.any(String, Pathname)), + sort: T::Boolean, blk: T.nilable(T.proc.params(arg0: String).returns(BasicObject)) ) .returns(T::Array[String]) end - def self.[](*pattern, base: nil, &blk); end + def self.[](*pattern, base: nil, sort: true, &blk); end end diff --git a/rbi/core/encoding.rbi b/rbi/core/encoding.rbi index 18e808167d..2051b7b951 100644 --- a/rbi/core/encoding.rbi +++ b/rbi/core/encoding.rbi @@ -257,6 +257,7 @@ class Encoding < Object Big5_HKSCS = T.let(T.unsafe(nil), Encoding) Big5_HKSCS_2008 = T.let(T.unsafe(nil), Encoding) Big5_UAO = T.let(T.unsafe(nil), Encoding) + CESU_8 = T.let(T.unsafe(nil), Encoding) CP1250 = T.let(T.unsafe(nil), Encoding) CP1251 = T.let(T.unsafe(nil), Encoding) CP1252 = T.let(T.unsafe(nil), Encoding) @@ -272,6 +273,7 @@ class Encoding < Object CP51932 = T.let(T.unsafe(nil), Encoding) CP65000 = T.let(T.unsafe(nil), Encoding) CP65001 = T.let(T.unsafe(nil), Encoding) + CP720 = T.let(T.unsafe(nil), Encoding) CP737 = T.let(T.unsafe(nil), Encoding) CP775 = T.let(T.unsafe(nil), Encoding) CP850 = T.let(T.unsafe(nil), Encoding) @@ -322,6 +324,7 @@ class Encoding < Object GBK = T.let(T.unsafe(nil), Encoding) IBM037 = T.let(T.unsafe(nil), Encoding) IBM437 = T.let(T.unsafe(nil), Encoding) + IBM720 = T.let(T.unsafe(nil), Encoding) IBM737 = T.let(T.unsafe(nil), Encoding) IBM775 = T.let(T.unsafe(nil), Encoding) IBM850 = T.let(T.unsafe(nil), Encoding) diff --git a/rbi/core/enum.rbi b/rbi/core/enum.rbi index 4626419104..396e357077 100644 --- a/rbi/core/enum.rbi +++ b/rbi/core/enum.rbi @@ -3,6 +3,20 @@ class T::Enum extend T::Props::CustomType + # Assume that this is always included (even if it isn't) so that it never + # shows up in RBI files generated from reflection. + module LegacyMigrationMode + sig {params(other: T.anything).returns(T::Boolean)} + def ==(other); end + + sig {params(other: T.anything).returns(T::Boolean)} + def ===(other); end + + sig {params(method: Symbol, other: T.untyped).void} + private def comparison_assertion_failed(method, other); end + end + include LegacyMigrationMode + ## Enum class methods ## # All the values defined in the Enum class @@ -65,6 +79,9 @@ class T::Enum sig {returns(String)} def to_s; end + sig {returns(String)} + def to_str; end + sig {returns(String)} def inspect; end diff --git a/rbi/core/enumerable.rbi b/rbi/core/enumerable.rbi index d916e5ec99..9afae6900e 100644 --- a/rbi/core/enumerable.rbi +++ b/rbi/core/enumerable.rbi @@ -748,6 +748,21 @@ module Enumerable # res = c.grep(/SEEK/) { |v| IO.const_get(v) } # res #=> [0, 1, 2] # ``` + sig do + type_parameters(:Instance) + .params( + arg0: T::Class[T.type_parameter(:Instance)], + ) + .returns(T::Array[T.all(Elem, T.type_parameter(:Instance))]) + end + sig do + type_parameters(:Instance, :U) + .params( + arg0: T::Class[T.type_parameter(:Instance)], + blk: T.proc.params(arg0: T.type_parameter(:Instance)).returns(T.type_parameter(:U)), + ) + .returns(T::Array[T.type_parameter(:U)]) + end sig do params( arg0: BasicObject, diff --git a/rbi/core/enumerator.rbi b/rbi/core/enumerator.rbi index 6c29c4863c..da29ad515e 100644 --- a/rbi/core/enumerator.rbi +++ b/rbi/core/enumerator.rbi @@ -789,6 +789,21 @@ class Enumerator::Lazy < Enumerator # # Also aliased as: # [`_enumerable_grep`](https://docs.ruby-lang.org/en/2.7.0/Enumerator/Lazy.html#method-i-_enumerable_grep) + sig do + type_parameters(:Instance) + .params( + arg0: T::Class[T.type_parameter(:Instance)], + ) + .returns(T::Enumerator::Lazy[T.all(Elem, T.type_parameter(:Instance))]) + end + sig do + type_parameters(:Instance, :U) + .params( + arg0: T::Class[T.type_parameter(:Instance)], + blk: T.proc.params(arg0: T.type_parameter(:Instance)).returns(T.type_parameter(:U)), + ) + .returns(T::Enumerator::Lazy[T.type_parameter(:U)]) + end sig do params( arg0: BasicObject diff --git a/rbi/core/errors.rbi b/rbi/core/errors.rbi index b3c91689fd..7fe7f19ce0 100644 --- a/rbi/core/errors.rbi +++ b/rbi/core/errors.rbi @@ -170,6 +170,10 @@ end # h.fetch("baz") #=> KeyError: key not found: "baz" # ``` class KeyError < IndexError + # Construct a new KeyError exception with the given message, receiver and key. + sig { params(msg: T.untyped, receiver: T.untyped, key: T.untyped).void } + def initialize(msg = nil, receiver: nil, key: nil); end + # Return the key caused this # [`KeyError`](https://docs.ruby-lang.org/en/2.7.0/KeyError.html) exception. def key; end diff --git a/rbi/core/exception.rbi b/rbi/core/exception.rbi index c7126e1349..377308bdbe 100644 --- a/rbi/core/exception.rbi +++ b/rbi/core/exception.rbi @@ -186,6 +186,45 @@ class Exception < Object sig {returns(T.nilable(Exception))} def cause(); end + # Processes a string returned by + # [`message`](https://docs.ruby-lang.org/en/2.7.0/Exception.html#method-i-message). + # + # It may add the class name of the exception to the end of the first line. + # Also, when `highlight` keyword is true, it adds ANSI escape sequences to + # make the message bold. + # + # If you override this method, it must be tolerant for unknown keyword + # arguments. All keyword arguments passed to + # [`full_message`](https://docs.ruby-lang.org/en/2.7.0/Exception.html#method-i-full_message) + # are delegated to this method. + # + # This method is overridden by did\_you\_mean and error\_highlight to add + # their information. + # + # A user-defined exception class can also define their own `detailed_message` + # method to add supplemental information. When `highlight` is true, it can + # return a string containing escape sequences, but use widely-supported ones. + # It is recommended to limit the following codes: + # + # * Reset (`\e[0m`) + # * Bold (`\e[1m`) + # * Underline (`\e[4m`) + # * Foreground color except white and black + # * Red (`\e[31m`) + # * Green (`\e[32m`) + # * Yellow (`\e[33m`) + # * Blue (`\e[34m`) + # * Magenta (`\e[35m`) + # * Cyan (`\e[36m`) + # + # + # + # Use escape sequences carefully even if `highlight` is true. Do not use + # escape sequences to express essential information; the message should be + # readable even if all escape sequences are ignored. + sig { overridable.params(highlight: T.nilable(T::Boolean), opts: T.untyped).returns(String) } + def detailed_message(highlight: nil, **opts); end + # With no argument, or if the argument is the same as the receiver, return the # receiver. Otherwise, create a new exception object of the same class as the # receiver, but with a message equal to `string.to_str`. diff --git a/rbi/core/fiber.rbi b/rbi/core/fiber.rbi index 6e9b3b0572..31716140f8 100644 --- a/rbi/core/fiber.rbi +++ b/rbi/core/fiber.rbi @@ -161,4 +161,8 @@ class Fiber < Object # [`Fiber.yield`](https://docs.ruby-lang.org/en/2.7.0/Fiber.html#method-c-yield) # expression evaluates to. def self.yield(*_); end + + def self.[]=(key, value); end + + def self.[](key); end end diff --git a/rbi/core/float.rbi b/rbi/core/float.rbi index 5c6f749280..dddcd1eef5 100644 --- a/rbi/core/float.rbi +++ b/rbi/core/float.rbi @@ -117,6 +117,12 @@ class Float < Numeric ) .returns(BigDecimal) end + sig do + params( + arg0: Numeric, + ) + .returns(Numeric) + end def %(arg0); end # Returns a new [`Float`](https://docs.ruby-lang.org/en/2.7.0/Float.html) @@ -157,6 +163,12 @@ class Float < Numeric ) .returns(Float) end + sig do + params( + arg0: Numeric, + ) + .returns(Numeric) + end def *(arg0); end # Raises `float` to the power of `other`. @@ -194,6 +206,12 @@ class Float < Numeric ) .returns(Complex) end + sig do + params( + arg0: Numeric, + ) + .returns(Numeric) + end def **(arg0); end # Returns a new [`Float`](https://docs.ruby-lang.org/en/2.7.0/Float.html) @@ -234,6 +252,12 @@ class Float < Numeric ) .returns(Float) end + sig do + params( + arg0: Numeric, + ) + .returns(Numeric) + end def +(arg0); end sig {returns(Float)} @@ -277,6 +301,12 @@ class Float < Numeric ) .returns(Float) end + sig do + params( + arg0: Numeric, + ) + .returns(Numeric) + end def -(arg0); end # Returns `float`, negated. @@ -321,6 +351,12 @@ class Float < Numeric ) .returns(Float) end + sig do + params( + arg0: Numeric, + ) + .returns(Numeric) + end def /(arg0); end # Returns `true` if `float` is less than `real`. diff --git a/rbi/core/gc.rbi b/rbi/core/gc.rbi index f4623ca466..59bf55d325 100644 --- a/rbi/core/gc.rbi +++ b/rbi/core/gc.rbi @@ -214,6 +214,21 @@ module GC # This method is implementation specific. Now this method checks generational # consistency if RGenGC is supported. def self.verify_internal_consistency; end + + # Enable to measure GC time. + # You can get the result with `GC.stat(:time)`. + # Note that GC time measurement can cause some performance overhead. + sig {params(flag: T::Boolean).void} + def self.measure_total_time=(flag); end + + # Return measure_total_time flag (default: `true`). + # Note that measurement can affect the application performance. + sig {returns(T::Boolean)} + def self.measure_total_time; end + + # Return measured GC total time in nano seconds. + sig {returns(Integer)} + def self.total_time; end end # The [`GC`](https://docs.ruby-lang.org/en/2.7.0/GC.html) profiler provides diff --git a/rbi/core/hash.rbi b/rbi/core/hash.rbi index deac3ec381..01ab3d9f5f 100644 --- a/rbi/core/hash.rbi +++ b/rbi/core/hash.rbi @@ -1143,7 +1143,13 @@ class Hash < Object # h.slice(:a) #=> {:a=>100} # h.slice(:b, :c, :d) #=> {:b=>200, :c=>300} # ``` - def slice(*_); end + sig do + params( + arg0: K, + ) + .returns(T::Hash[K, V]) + end + def slice(*arg0); end # Returns a new hash consisting of entries for which the block returns true. # diff --git a/rbi/core/integer.rbi b/rbi/core/integer.rbi index 3c2bc132c5..cfca0dd0ef 100644 --- a/rbi/core/integer.rbi +++ b/rbi/core/integer.rbi @@ -41,6 +41,12 @@ class Integer < Numeric ) .returns(T.any(Integer, Float)) end + sig do + params( + arg0: Numeric, + ) + .returns(Numeric) + end def %(arg0); end # Bitwise AND. @@ -90,6 +96,12 @@ class Integer < Numeric ) .returns(T.any(Integer, Float)) end + sig do + params( + arg0: Numeric, + ) + .returns(Numeric) + end def *(arg0); end # Raises `int` to the power of `numeric`, which may be negative or fractional. @@ -139,6 +151,12 @@ class Integer < Numeric ) .returns(Complex) end + sig do + params( + arg0: Numeric, + ) + .returns(Numeric) + end def **(arg0); end # Performs addition: the class of the resulting object depends on the class of @@ -179,6 +197,12 @@ class Integer < Numeric ) .returns(T.any(Integer, Float)) end + sig do + params( + arg0: Numeric, + ) + .returns(Numeric) + end def +(arg0); end sig {returns(Integer)} @@ -222,6 +246,12 @@ class Integer < Numeric ) .returns(T.any(Integer, Float)) end + sig do + params( + arg0: Numeric, + ) + .returns(Numeric) + end def -(arg0); end # Returns `int`, negated. @@ -266,6 +296,12 @@ class Integer < Numeric ) .returns(T.any(Integer, Float)) end + sig do + params( + arg0: Numeric, + ) + .returns(Numeric) + end def /(arg0); end # Returns `true` if the value of `int` is less than that of `real`. diff --git a/rbi/core/io.rbi b/rbi/core/io.rbi index ca8de698c3..17164a16e1 100644 --- a/rbi/core/io.rbi +++ b/rbi/core/io.rbi @@ -1821,10 +1821,18 @@ class IO < Object params( ext_enc: T.nilable(T.any(String, Encoding)), int_enc: T.nilable(T.any(String, Encoding)), - opt: T.nilable(T::Hash[Symbol, String]), - blk: T.nilable(T.proc.params(read_io: IO, write_io: IO).void) + opt: T.nilable(T::Hash[Symbol, String]) ).returns([IO, IO]) end + sig do + type_parameters(:T) + .params( + ext_enc: T.nilable(T.any(String, Encoding)), + int_enc: T.nilable(T.any(String, Encoding)), + opt: T.nilable(T::Hash[Symbol, String]), + blk: T.nilable(T.proc.params(read_io: IO, write_io: IO).returns(T.type_parameter(:T))) + ).returns(T.type_parameter(:T)) + end def self.pipe(ext_enc = nil, int_enc = nil, opt = nil, &blk); end # Returns the current position (in bytes) in `self` (see @@ -2718,11 +2726,11 @@ class IO < Object # ``` sig do params( - arg0: Object, + args: Object, ) .returns(Integer) end - def write(arg0); end + def write(*args); end # Opens the file, optionally seeks to the given *offset*, then returns # *length* bytes (defaulting to the rest of the file). binread ensures the diff --git a/rbi/core/kernel.rbi b/rbi/core/kernel.rbi index 61fb14737f..3f3501c194 100644 --- a/rbi/core/kernel.rbi +++ b/rbi/core/kernel.rbi @@ -168,6 +168,9 @@ module Kernel ) .returns(T.nilable(T::Array[Thread::Backtrace::Location])) end + sig do + returns(T::Array[Thread::Backtrace::Location]) + end def caller_locations(start_or_range=T.unsafe(nil), length=T.unsafe(nil)); end # `catch` executes its block. If `throw` is not called, the block executes @@ -485,9 +488,13 @@ module Kernel sig {returns(Integer)} def hash(); end + private def initialize_clone(*args); end + sig {params(object: T.self_type).returns(T.self_type)} def initialize_copy(object); end + private def initialize_dup(orig); end + sig {returns(String)} def inspect(); end @@ -545,7 +552,7 @@ module Kernel sig do params( - arg0: Symbol, + arg0: T.any(Symbol, String) ) .returns(Method) end @@ -621,6 +628,16 @@ module Kernel end def respond_to?(arg0,include_all=false); end + # DO NOT USE THIS DIRECTLY. + # + # Hook method to return whether the obj can respond to id method or not. + # + # When the method name parameter is given as a string, the string is converted to a symbol. + # + # See respond_to?, and the example of BasicObject. + sig {params(method_name: Symbol, include_private: T::Boolean).returns(T::Boolean)} + private def respond_to_missing?(method_name, include_private = false); end + sig do params( arg0: T.any(String, Symbol), @@ -675,6 +692,7 @@ module Kernel # ``` sig do params( + # `x` should be `T.self_type`, but it's blocked by https://github.com/sorbet/sorbet/issues/5632 blk: T.proc.params(x: T.untyped).void ) .returns(T.self_type) @@ -956,7 +974,7 @@ module Kernel # ``` sig do params( - arg: T.any(Numeric, String), + arg: T.any(Numeric, String, NilClass), base: Integer, exception: T::Boolean ) @@ -3000,8 +3018,15 @@ module Kernel # exec "echo", "*" # echoes an asterisk # # never get here # ``` - sig { params(args: String).returns(T.noreturn) } - def exec(*args); end + sig do + params( + env: T.any(String, [String, String], T::Hash[String, T.nilable(String)]), + argv0: T.any(String, [String, String]), + args: String, + options: T.untyped, + ).returns(T.noreturn) + end + def exec(env, argv0 = T.unsafe(nil), *args, **options); end # Executes *command...* in a subshell. *command...* is one of following forms. # diff --git a/rbi/core/module.rbi b/rbi/core/module.rbi index 9b5f955202..247915b2b3 100644 --- a/rbi/core/module.rbi +++ b/rbi/core/module.rbi @@ -164,9 +164,8 @@ class Module < Object # instance of one of *mod*'s descendants. Of limited use for modules, but can # be used in `case` statements to classify objects by class. sig do - type_parameters(:U) - .params( - other: T.type_parameter(:U), + params( + other: T.anything ) .returns(T::Boolean) end diff --git a/rbi/core/nil_class.rbi b/rbi/core/nil_class.rbi index 42ca16c2c8..79a62cc44a 100644 --- a/rbi/core/nil_class.rbi +++ b/rbi/core/nil_class.rbi @@ -16,7 +16,13 @@ class NilClass < Object # [`Object`](https://docs.ruby-lang.org/en/2.7.0/Object.html), effectively the # same as calling `#==`, but typically overridden by descendants to provide # meaningful semantics in `case` statements. - def ===(_); end + sig do + params( + arg0: T.anything, + ) + .returns(T::Boolean) + end + def ===(arg0); end # Exclusive Or---If *obj* is `nil` or `false`, returns `false`; otherwise, # returns `true`. diff --git a/rbi/core/proc.rbi b/rbi/core/proc.rbi index 497d69fa8f..78fc67d8bb 100644 --- a/rbi/core/proc.rbi +++ b/rbi/core/proc.rbi @@ -347,21 +347,21 @@ # # Numbered parameters were introduced in Ruby 2.7. class Proc < Object - # Creates a new [`Proc`](https://docs.ruby-lang.org/en/2.7.0/Proc.html) + # Creates a new [`Proc`](https://docs.ruby-lang.org/en/3.2/Proc.html) # object, bound to the current context. - # [`Proc::new`](https://docs.ruby-lang.org/en/2.7.0/Proc.html#method-c-new) - # may be called without a block only within a method with an attached block, - # in which case that block is converted to the - # [`Proc`](https://docs.ruby-lang.org/en/2.7.0/Proc.html) object. # # ```ruby - # def proc_from - # Proc.new - # end - # proc = proc_from { "hello" } + # proc = Proc.new { "hello" } # proc.call #=> "hello" # ``` - sig {params(blk: T.untyped).returns(T.attached_class)} + # + # Raises [`ArgumentError`](https://docs.ruby-lang.org/en/3.2/ArgumentError.html) + # if called without a block. + # + # ```ruby + # Proc.new #=> ArgumentError + # ``` + sig {params(blk: Proc).returns(T.attached_class)} def self.new(&blk); end # Invokes the block with `obj` as the proc's parameter like diff --git a/rbi/core/random.rbi b/rbi/core/random.rbi index e4f41387fc..d32380cdd7 100644 --- a/rbi/core/random.rbi +++ b/rbi/core/random.rbi @@ -747,4 +747,82 @@ module SecureRandom returns(String) end def self.uuid; end + + sig do + returns(String) + end + def self.uuid_v4; end + + # Generate a random v7 UUID (Universally Unique IDentifier). + # + # require 'random/formatter' + # + # Random.uuid_v7 # => "0188d4c3-1311-7f96-85c7-242a7aa58f1e" + # Random.uuid_v7 # => "0188d4c3-16fe-744f-86af-38fa04c62bb5" + # Random.uuid_v7 # => "0188d4c3-1af8-764f-b049-c204ce0afa23" + # Random.uuid_v7 # => "0188d4c3-1e74-7085-b14f-ef6415dc6f31" + # # |<--sorted-->| |<----- random ---->| + # + # # or + # prng = Random.new + # prng.uuid_v7 # => "0188ca51-5e72-7950-a11d-def7ff977c98" + # + # The version 7 UUID starts with the least significant 48 bits of a 64 bit + # Unix timestamp (milliseconds since the epoch) and fills the remaining bits + # with random data, excluding the version and variant bits. + # + # This allows version 7 UUIDs to be sorted by creation time. Time ordered + # UUIDs can be used for better database index locality of newly inserted + # records, which may have a significant performance benefit compared to random + # data inserts. + # + # The result contains 74 random bits (9.25 random bytes). + # + # Note that this method cannot be made reproducable because its output + # includes not only random bits but also timestamp. + # + # See draft-ietf-uuidrev-rfc4122bis[https://datatracker.ietf.org/doc/draft-ietf-uuidrev-rfc4122bis/] + # for details of UUIDv7. + # + # ==== Monotonicity + # + # UUIDv7 has millisecond precision by default, so multiple UUIDs created + # within the same millisecond are not issued in monotonically increasing + # order. To create UUIDs that are time-ordered with sub-millisecond + # precision, up to 12 bits of additional timestamp may added with + # +extra_timestamp_bits+. The extra timestamp precision comes at the expense + # of random bits. Setting extra_timestamp_bits: 12 provides ~244ns + # of precision, but only 62 random bits (7.75 random bytes). + # + # prng = Random.new + # Array.new(4) { prng.uuid_v7(extra_timestamp_bits: 12) } + # # => + # ["0188d4c7-13da-74f9-8b53-22a786ffdd5a", + # "0188d4c7-13da-753b-83a5-7fb9b2afaeea", + # "0188d4c7-13da-754a-88ea-ac0baeedd8db", + # "0188d4c7-13da-7557-83e1-7cad9cda0d8d"] + # # |<--- sorted --->| |<-- random --->| + # + # Array.new(4) { prng.uuid_v7(extra_timestamp_bits: 8) } + # # => + # ["0188d4c7-3333-7a95-850a-de6edb858f7e", + # "0188d4c7-3333-7ae8-842e-bc3a8b7d0cf9", # <- out of order + # "0188d4c7-3333-7ae2-995a-9f135dc44ead", # <- out of order + # "0188d4c7-3333-7af9-87c3-8f612edac82e"] + # # |<--- sorted -->||<---- random --->| + # + # Any rollbacks of the system clock will break monotonicity. UUIDv7 is based + # on UTC, which excludes leap seconds and can rollback the clock. To avoid + # this, the system clock can synchronize with an NTP server configured to use + # a "leap smear" approach. NTP or PTP will also be needed to synchronize + # across distributed nodes. + # + # Counters and other mechanisms for stronger guarantees of monotonicity are + # not implemented. Applications with stricter requirements should follow + # {Section 6.2}[https://www.ietf.org/archive/id/draft-ietf-uuidrev-rfc4122bis-07.html#monotonicity_counters] + # of the specification. + sig do + params(extra_timestamp_bits: T.nilable(Integer)).returns(String) + end + def self.uuid_v7(extra_timestamp_bits:); end end diff --git a/rbi/core/rb_config.rbi b/rbi/core/rb_config.rbi index 5539e5f96e..458d66e0b5 100644 --- a/rbi/core/rb_config.rbi +++ b/rbi/core/rb_config.rbi @@ -48,4 +48,7 @@ module RbConfig MAKEFILE_CONFIG = T.let(T.unsafe(nil), T::Hash[T.untyped, T.untyped]) # Ruby installed directory. TOPDIR = T.let(T.unsafe(nil), String) + + sig {returns(String)} + def self.ruby; end end diff --git a/rbi/core/regexp.rbi b/rbi/core/regexp.rbi index 62c9a9a9d5..468e88bdea 100644 --- a/rbi/core/regexp.rbi +++ b/rbi/core/regexp.rbi @@ -1080,11 +1080,11 @@ class Regexp < Object options: BasicObject, kcode: String, ) - .returns(Object) + .void end sig do params( - arg0: Regexp, + arg0: T.any(Regexp, String), ) .void end @@ -1363,4 +1363,12 @@ class Regexp < Object params(pats: T.untyped).returns(Regexp) end def self.union(*pats); end + + + # [TimeoutError](https://docs.ruby-lang.org/en/3.2/Regexp/TimeoutError.html) + # is raised when the timeout set by calling + # [::timeout=][https://docs.ruby-lang.org/en/3.2/Regexp.html#method-c-timeout-3D] + # is reached during matching. + class TimeoutError < RegexpError + end end diff --git a/rbi/core/ruby_vm.rbi b/rbi/core/ruby_vm.rbi index 9acb4856b9..38f408f654 100644 --- a/rbi/core/ruby_vm.rbi +++ b/rbi/core/ruby_vm.rbi @@ -81,12 +81,12 @@ module RubyVM::AbstractSyntaxTree # for explanation of keyword argument meaning and usage. sig do params( - arg: T.any(T::proc.void, Method), + arg: T.any(T::proc.void, Method, Thread::Backtrace::Location), keep_script_lines: T::Boolean, error_tolerant: T::Boolean, keep_tokens: T::Boolean, ) - .returns(RubyVM::AbstractSyntaxTree::Node) + .returns(T.nilable(RubyVM::AbstractSyntaxTree::Node)) end def self.of(arg, keep_script_lines: false, error_tolerant: false, keep_tokens: false); end @@ -791,3 +791,79 @@ class RubyVM::InstructionSequence < Object # ``` def self.of(_); end end + +# This module allows for introspection of YJIT, CRuby's just-in-time compiler. +# Everything in the module is highly implementation specific and the API might +# be less stable compared to the standard library. +# +# This module may not exist if YJIT does not support the particular platform +# for which CRuby is built. +module RubyVM::YJIT + # Check if YJIT is enabled. + sig { returns(T::Boolean) } + def self.enabled?; end + + # Check if `--yjit-stats` is used. + sig { returns(T::Boolean) } + def self.stats_enabled?; end + + # Discard statistics collected for `--yjit-stats`. + sig { void } + def self.reset_stats!; end + + # Enable YJIT compilation. `stats` option decides whether to enable YJIT stats or not. + # + # * `false`: Disable stats. + # * `true`: Enable stats. Print stats at exit. + # * `:quiet`: Enable stats. Do not print stats at exit. + sig { params(stats: T.any(T::Boolean, Symbol)).returns(T::Boolean) } + def self.enable(stats: false); end + + # [`Marshal`](https://docs.ruby-lang.org/en/2.7.0/Marshal.html) dumps exit locations to the given filename. + # + # Usage: + # + # If `--yjit-exit-locations` is passed, a file named + # "yjit_exit_locations.dump" will automatically be generated. + # + # If you want to collect traces manually, call `dump_exit_locations` + # directly. + # + # Note that calling this in a script will generate stats after the + # dump is created, so the stats data may include exits from the + # dump itself. + # + # In a script call: + # + # ``` + # at_exit do + # RubyVM::YJIT.dump_exit_locations("my_file.dump") + # end + # ```` + # + # Then run the file with the following options: + # + # ``` + # ruby --yjit --yjit-trace-exits test.rb + # ``` + # + # Once the code is done running, use Stackprof to read the dump file. + # See Stackprof documentation for options. + sig { params(filename: T.any(String, Pathname)).returns(Integer) } + def self.dump_exit_locations(filename); end + + # Return a hash for statistics generated for the `--yjit-stats` command line option. + # Return `nil` when option is not passed or unavailable. + sig { params(context: T::Boolean).returns(T.nilable(T::Hash[Symbol, T.untyped])) } + def self.runtime_stats(context: false); end + + # Format and print out counters as a [`String`](https://docs.ruby-lang.org/en/2.7.0/String.html). + # This returns a non-empty content only when `--yjit-stats` is enabled. + sig { returns(String) } + def self.stats_string; end + + # Discard existing compiled code to reclaim memory + # and allow for recompilations in the future. + sig { void } + def self.code_gc; end +end diff --git a/rbi/core/string.rbi b/rbi/core/string.rbi index d5cf43070d..d254d25e90 100644 --- a/rbi/core/string.rbi +++ b/rbi/core/string.rbi @@ -290,7 +290,7 @@ class String < Object sig do params( arg0: Regexp, - arg1: String, + arg1: T.any(String, Symbol), ) .returns(T.nilable(String)) end @@ -2037,7 +2037,7 @@ class String < Object sig do params( arg0: Regexp, - arg1: String, + arg1: T.any(String, Symbol), ) .returns(T.nilable(String)) end @@ -2945,7 +2945,7 @@ class String < Object sig do params( arg0: Regexp, - arg1: String, + arg1: T.any(String, Symbol), ) .returns(T.nilable(String)) end diff --git a/rbi/core/thread.rbi b/rbi/core/thread.rbi index b2aef02cff..53c7f94cb5 100644 --- a/rbi/core/thread.rbi +++ b/rbi/core/thread.rbi @@ -495,6 +495,25 @@ class Thread < Object sig {params(name: T.untyped).returns(T.untyped)} def name=(name); end + # Return the native thread ID which is used by the Ruby thread. + # + # The ID depends on the OS. (not POSIX thread ID returned by pthread_self(3)) + # * On Linux it is TID returned by gettid(2). + # * On macOS it is the system-wide unique integral ID of thread returned + # by pthread_threadid_np(3). + # * On FreeBSD it is the unique integral ID of the thread returned by + # pthread_getthreadid_np(3). + # * On Windows it is the thread identifier returned by GetThreadId(). + # * On other platforms, it raises NotImplementedError. + # + # NOTE: + # If the thread is not associated yet or already deassociated with a native + # thread, it returns _nil_. + # If the Ruby implementation uses M:N thread model, the ID may change + # depending on the timing. + sig { returns(Integer) } + def native_thread_id; end; + # Returns whether or not the asynchronous queue is empty for the target # thread. # @@ -1208,6 +1227,11 @@ class Thread < Object # ``` sig {returns(T.untyped)} def self.stop; end + + # Yields each frame of the current execution stack as a + # backtrace location object. + sig {params(blk: T.proc.params(location: Thread::Backtrace::Location).void).returns(T.untyped)} + def self.each_caller_location(&blk); end end class Thread::Backtrace < Object @@ -1423,7 +1447,7 @@ class Thread::Mutex < Object # Obtains a lock, runs the block, and releases the lock when the block # completes. See the example under `Mutex`. - sig {params(blk: T.proc.void).returns(T.untyped)} + sig {type_parameters(:T).params(blk: T.proc.returns(T.type_parameter(:T))).returns(T.type_parameter(:T))} def synchronize(&blk); end # Attempts to obtain the lock and returns immediately. Returns `true` if the @@ -1525,8 +1549,8 @@ class Thread::Queue < Object # Alias for: # [`pop`](https://docs.ruby-lang.org/en/2.7.0/Queue.html#method-i-pop) - sig {params(args: T.untyped).returns(T.untyped)} - def deq(*args); end + sig {params(non_block: T::Boolean, timeout: T.nilable(Integer)).returns(T.untyped)} + def deq(non_block=false, timeout: nil); end # Returns `true` if the queue is empty. sig {returns(T::Boolean)} @@ -1548,7 +1572,7 @@ class Thread::Queue < Object def marshal_dump; end # Returns the number of threads waiting on the queue. - sig {returns(T.untyped)} + sig {returns(Integer)} def num_waiting; end # Retrieves data from the queue. @@ -1560,8 +1584,8 @@ class Thread::Queue < Object # Also aliased as: # [`deq`](https://docs.ruby-lang.org/en/2.7.0/Queue.html#method-i-deq), # [`shift`](https://docs.ruby-lang.org/en/2.7.0/Queue.html#method-i-shift) - sig {params(args: T.untyped).returns(T.untyped)} - def pop(*args); end + sig {params(non_block: T::Boolean, timeout: T.nilable(Integer)).returns(T.untyped)} + def pop(non_block=false, timeout: nil); end # Pushes the given `object` to the queue. # @@ -1573,8 +1597,8 @@ class Thread::Queue < Object # Alias for: # [`pop`](https://docs.ruby-lang.org/en/2.7.0/Queue.html#method-i-pop) - sig {params(args: T.untyped).returns(T.untyped)} - def shift(*args); end + sig {params(non_block: T::Boolean, timeout: T.nilable(Integer)).returns(T.untyped)} + def shift(non_block=false, timeout: nil); end # Alias for: # [`length`](https://docs.ruby-lang.org/en/2.7.0/Queue.html#method-i-length) diff --git a/rbi/procs.rbi b/rbi/procs.rbi new file mode 100644 index 0000000000..a8bb6379d1 --- /dev/null +++ b/rbi/procs.rbi @@ -0,0 +1,255 @@ +# typed: true + +# DO NOT EDIT! +# This file is autogenerated. Regenerate with: +# bazel run //rbi:update_procs_rbi + +class Proc0 < Proc + Return = type_member(:out) + + sig {returns(Return)} + def call(); end + + alias_method :[], :call +end + +class Proc1 < Proc + Return = type_member(:out) + Arg0 = type_member(:in) + + sig do + params( + arg0: Arg0, + ) + .returns(Return) + end + def call(arg0); end + + alias_method :[], :call +end + +class Proc2 < Proc + Return = type_member(:out) + Arg0 = type_member(:in) + Arg1 = type_member(:in) + + sig do + params( + arg0: Arg0, + arg1: Arg1, + ) + .returns(Return) + end + def call(arg0, arg1); end + + alias_method :[], :call +end + +class Proc3 < Proc + Return = type_member(:out) + Arg0 = type_member(:in) + Arg1 = type_member(:in) + Arg2 = type_member(:in) + + sig do + params( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + ) + .returns(Return) + end + def call(arg0, arg1, arg2); end + + alias_method :[], :call +end + +class Proc4 < Proc + Return = type_member(:out) + Arg0 = type_member(:in) + Arg1 = type_member(:in) + Arg2 = type_member(:in) + Arg3 = type_member(:in) + + sig do + params( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + ) + .returns(Return) + end + def call(arg0, arg1, arg2, arg3); end + + alias_method :[], :call +end + +class Proc5 < Proc + Return = type_member(:out) + Arg0 = type_member(:in) + Arg1 = type_member(:in) + Arg2 = type_member(:in) + Arg3 = type_member(:in) + Arg4 = type_member(:in) + + sig do + params( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4, + ) + .returns(Return) + end + def call(arg0, arg1, arg2, arg3, arg4); end + + alias_method :[], :call +end + +class Proc6 < Proc + Return = type_member(:out) + Arg0 = type_member(:in) + Arg1 = type_member(:in) + Arg2 = type_member(:in) + Arg3 = type_member(:in) + Arg4 = type_member(:in) + Arg5 = type_member(:in) + + sig do + params( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4, + arg5: Arg5, + ) + .returns(Return) + end + def call(arg0, arg1, arg2, arg3, arg4, arg5); end + + alias_method :[], :call +end + +class Proc7 < Proc + Return = type_member(:out) + Arg0 = type_member(:in) + Arg1 = type_member(:in) + Arg2 = type_member(:in) + Arg3 = type_member(:in) + Arg4 = type_member(:in) + Arg5 = type_member(:in) + Arg6 = type_member(:in) + + sig do + params( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4, + arg5: Arg5, + arg6: Arg6, + ) + .returns(Return) + end + def call(arg0, arg1, arg2, arg3, arg4, arg5, arg6); end + + alias_method :[], :call +end + +class Proc8 < Proc + Return = type_member(:out) + Arg0 = type_member(:in) + Arg1 = type_member(:in) + Arg2 = type_member(:in) + Arg3 = type_member(:in) + Arg4 = type_member(:in) + Arg5 = type_member(:in) + Arg6 = type_member(:in) + Arg7 = type_member(:in) + + sig do + params( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4, + arg5: Arg5, + arg6: Arg6, + arg7: Arg7, + ) + .returns(Return) + end + def call(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7); end + + alias_method :[], :call +end + +class Proc9 < Proc + Return = type_member(:out) + Arg0 = type_member(:in) + Arg1 = type_member(:in) + Arg2 = type_member(:in) + Arg3 = type_member(:in) + Arg4 = type_member(:in) + Arg5 = type_member(:in) + Arg6 = type_member(:in) + Arg7 = type_member(:in) + Arg8 = type_member(:in) + + sig do + params( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4, + arg5: Arg5, + arg6: Arg6, + arg7: Arg7, + arg8: Arg8, + ) + .returns(Return) + end + def call(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); end + + alias_method :[], :call +end + +class Proc10 < Proc + Return = type_member(:out) + Arg0 = type_member(:in) + Arg1 = type_member(:in) + Arg2 = type_member(:in) + Arg3 = type_member(:in) + Arg4 = type_member(:in) + Arg5 = type_member(:in) + Arg6 = type_member(:in) + Arg7 = type_member(:in) + Arg8 = type_member(:in) + Arg9 = type_member(:in) + + sig do + params( + arg0: Arg0, + arg1: Arg1, + arg2: Arg2, + arg3: Arg3, + arg4: Arg4, + arg5: Arg5, + arg6: Arg6, + arg7: Arg7, + arg8: Arg8, + arg9: Arg9, + ) + .returns(Return) + end + def call(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); end + + alias_method :[], :call +end + diff --git a/rbi/sorbet/builder.rbi b/rbi/sorbet/builder.rbi index 75e21aef39..38337dc3b9 100644 --- a/rbi/sorbet/builder.rbi +++ b/rbi/sorbet/builder.rbi @@ -18,8 +18,8 @@ class T::Private::Methods::DeclBuilder sig {returns(T::Private::Methods::DeclBuilder)} def overridable; end - sig {params(claz: T.untyped).returns(T::Private::Methods::DeclBuilder)} - def bind(claz); end + sig {params(klass: T.untyped).returns(T::Private::Methods::DeclBuilder)} + def bind(klass); end sig {params(unused_positional_params: T.untyped, params: T.untyped).returns(T::Private::Methods::DeclBuilder)} def params(*unused_positional_params, **params); end diff --git a/rbi/sorbet/sorbet.rbi b/rbi/sorbet/sorbet.rbi index 8896d9ac6f..48d6242511 100644 --- a/rbi/sorbet/sorbet.rbi +++ b/rbi/sorbet/sorbet.rbi @@ -11,15 +11,6 @@ module Sorbet::Private::Static def self.sig(arg0, arg1=nil, &blk) end - sig do - params( - expr: T.untyped, - ) - .void - end - def self.keep_for_ide(expr) - end - sig do type_parameters(:U) .params(this: T.untyped, fun: T.all(T.type_parameter(:U), Symbol), kind: Symbol) @@ -70,9 +61,6 @@ module Sorbet::Private::Static::ResolvedSig end end -module Sorbet::Private::Static::StubModule -end - class Sorbet::Private::Static::ImplicitModuleSuperclass < BasicObject end @@ -407,6 +395,7 @@ end ::Sorbet::Private::Static::IOLike = T.type_alias do T.any( IO, - StringIO + StringIO, + Tempfile ) end diff --git a/rbi/sorbet/t.rbi b/rbi/sorbet/t.rbi index c4904be054..e1d0b04037 100644 --- a/rbi/sorbet/t.rbi +++ b/rbi/sorbet/t.rbi @@ -127,6 +127,7 @@ module T # Deprecated. Use `T::Enum` instead. # # For more information, see https://sorbet.org/docs/tenum + sig { params(values: T.any(T::Set[T.anything], T::Array[T.anything])).returns(T.untyped) } def self.deprecated_enum(values); end # Type syntax to opt out of static type checking. @@ -198,7 +199,7 @@ module T # Statically, declares to Sorbet that the argument is never `nil`, despite # what the type system would otherwise infer for the type. # - # At runtime, raises an exception contining the provided reason if the + # At runtime, raises an exception containing the provided reason if the # argument is ever `nil`. # # Takes the reason as a block that should return a `String`, so that the code @@ -322,38 +323,56 @@ end module T::Array # Type syntax to specify the element type of a standard library Array def self.[](type); end + # Don't use case/when on T::Array--just use `when Array` instead. + def self.===(arg0); end end module T::Hash # Type syntax to specify the key and value types of a standard library Hash def self.[](keys, values); end + # Don't use case/when on T::Hash--just use `when Hash` instead. + def self.===(arg0); end end module T::Set # Type syntax to specify the element type of a standard library Set def self.[](type); end + # Don't use case/when on T::Set--just use `when Set` instead. + def self.===(arg0); end end module T::Range # Type syntax to specify the element type of a standard library Range def self.[](type); end + # Don't use case/when on T::Range--just use `when Range` instead. + def self.===(arg0); end end module T::Class # Type syntax to specify the element type of a standard library Class def self.[](type); end + # Don't use case/when on T::Class--just use `when Class` instead. + def self.===(arg0); end end module T::Enumerable # Type syntax to specify the element type of a standard library Enumerable def self.[](type); end + # Don't use case/when on T::Enumerable--just use `when Enumerable` instead. + def self.===(arg0); end end module T::Enumerator # Type syntax to specify the element type of a standard library Enumerator def self.[](type); end + # Don't use case/when on T::Enumerator--just use `when Enumerator` instead. + def self.===(arg0); end end module T::Enumerator::Lazy # Type syntax to specify the element type of a standard library Enumerator::Lazy def self.[](type); end + # Don't use case/when on T::Enumerator::Lazy--just use `when Enumerator::Lazy` instead. + def self.===(arg0); end end module T::Enumerator::Chain # Type syntax to specify the element type of a standard library Enumerator::Chain def self.[](type); end + # Don't use case/when on T::Enumerator::Chain--just use `when Enumerator::Chain` instead. + def self.===(arg0); end end # Type syntax for either a `true` or `false` value. @@ -425,7 +444,7 @@ module T::Utils def self.coerce(val); end def self.resolve_alias(type); end - def self.run_all_sig_blocks; end + def self.run_all_sig_blocks(force_type_init: true); end def self.signature_for_method(method); end def self.signature_for_instance_method(mod, method_name); end def self.unwrap_nilable(type); end @@ -444,10 +463,6 @@ module T::AbstractUtils def self.declared_abstract_methods_for(mod); end end -class T::InterfaceWrapper - def self.dynamic_cast(obj, mod); end -end - module T::Utils::Nilable def self.get_type_info(prop_type); end def self.get_underlying_type(prop_type); end @@ -456,6 +471,6 @@ end module T::NonForcingConstants # See for full docs. - sig {params(val: BasicObject, klass: String, package: T.nilable(String)).returns(T::Boolean)} - def self.non_forcing_is_a?(val, klass, package: nil); end + sig {params(val: BasicObject, klass: String).returns(T::Boolean)} + def self.non_forcing_is_a?(val, klass); end end diff --git a/rbi/sorbet/tprivate.rbi b/rbi/sorbet/tprivate.rbi index 63c2581458..ace03ee827 100644 --- a/rbi/sorbet/tprivate.rbi +++ b/rbi/sorbet/tprivate.rbi @@ -18,11 +18,3 @@ end module T::Private::Methods::CallValidation def self.disable_fast_path; end end - -module T::Private::Compiler - sig {returns(T::Boolean)} - def self.running_compiled?; end - - sig {returns(T.nilable(String))} - def self.compiler_version; end -end diff --git a/rbi/sorbet/tprops.rbi b/rbi/sorbet/tprops.rbi index 2c4197b48d..6ed2a1bb7f 100644 --- a/rbi/sorbet/tprops.rbi +++ b/rbi/sorbet/tprops.rbi @@ -21,10 +21,10 @@ module T::Props end module T::Props::ClassMethods - sig {params(name: Symbol, cls_or_args: T.untyped, args: T::Hash[Symbol, T.untyped]).void} - def const(name, cls_or_args, args={}); end + sig {params(name: Symbol, cls_or_args: T.untyped, args: T.untyped).void} + def const(name, cls_or_args, **args); end sig {params(name: Symbol, cls: T.untyped, rules: T.untyped).void} - def prop(name, cls, rules = nil); end + def prop(name, cls, **rules); end def decorator; end def decorator_class; end def plugin(mod); end diff --git a/rbi/stdlib/benchmark.rbi b/rbi/stdlib/benchmark.rbi index 3a93842990..ac186d9f85 100644 --- a/rbi/stdlib/benchmark.rbi +++ b/rbi/stdlib/benchmark.rbi @@ -306,7 +306,7 @@ module Benchmark # ``` sig do params( - label: String, + label: Object, blk: T.proc.void, ) .returns(Benchmark::Tms) @@ -327,7 +327,7 @@ class Benchmark::Job # Registers the given label and block pair in the job list. sig do params( - label: String, + label: Object, blk: T.proc.void ) .returns(T.self_type) @@ -341,7 +341,7 @@ class Benchmark::Job # Registers the given label and block pair in the job list. sig do params( - label: String, + label: Object, blk: T.proc.void ) .returns(T.self_type) @@ -359,7 +359,7 @@ class Benchmark::Report # formatting rules. sig do params( - label: String, + label: Object, format: T.untyped, blk: T.proc.void ) @@ -376,7 +376,7 @@ class Benchmark::Report # formatting rules. sig do params( - label: String, + label: Object, format: T.untyped, blk: T.proc.void ) diff --git a/rbi/stdlib/bigdecimal.rbi b/rbi/stdlib/bigdecimal.rbi index 425b2bf744..b405f3aad2 100644 --- a/rbi/stdlib/bigdecimal.rbi +++ b/rbi/stdlib/bigdecimal.rbi @@ -168,6 +168,11 @@ class BigDecimal < Numeric # Determines what happens when the result of a computation is infinity. See # [`BigDecimal.mode`](https://docs.ruby-lang.org/en/2.7.0/BigDecimal.html#method-c-mode). EXCEPTION_INFINITY = T.let(T.unsafe(nil), Integer) + + # Determines what happens when the result of a computation is not a number (NaN). See + # [`BigDecimal.mode`](https://docs.ruby-lang.org/en/2.7.0/BigDecimal.html#method-c-mode). + EXCEPTION_NaN = T.let(T.unsafe(nil), Integer) + # Determines what happens when the result of a computation is an overflow (a # result too large to be represented). See # [`BigDecimal.mode`](https://docs.ruby-lang.org/en/2.7.0/BigDecimal.html#method-c-mode). diff --git a/rbi/stdlib/bundler.rbi b/rbi/stdlib/bundler.rbi index 02c77c2ae6..a5e74d26f5 100644 --- a/rbi/stdlib/bundler.rbi +++ b/rbi/stdlib/bundler.rbi @@ -4094,6 +4094,182 @@ module Bundler::FileUtils::Verbose def self.uptodate?(new, old_list); end end +class Bundler::GemHelper + sig {returns(T.untyped)} + def allowed_push_host(); end + + sig {returns(T.untyped)} + def already_tagged?(); end + + sig {returns(T.untyped)} + def base(); end + + sig do + params( + built_gem_path: T.untyped, + ) + .returns(T.untyped) + end + def build_checksum(built_gem_path=T.unsafe(nil)); end + + sig {returns(T.untyped)} + def build_gem(); end + + sig {returns(T.untyped)} + def built_gem_path(); end + + sig {returns(T.untyped)} + def clean?(); end + + sig {returns(T.untyped)} + def committed?(); end + + sig {returns(T.untyped)} + def current_branch(); end + + sig {returns(T.untyped)} + def default_remote(); end + + sig {returns(T.untyped)} + def gem_command(); end + + sig {returns(T.untyped)} + def gem_key(); end + + sig {returns(T.untyped)} + def gem_push?(); end + + sig {returns(T.untyped)} + def gem_push_host(); end + + sig {returns(T.untyped)} + def gemspec(); end + + sig do + params( + remote: T.untyped, + ) + .returns(T.untyped) + end + def git_push(remote=T.unsafe(nil)); end + + sig {returns(T.untyped)} + def guard_clean(); end + + sig do + params( + base: T.untyped, + name: T.untyped, + ) + .void + end + def initialize(base=T.unsafe(nil), name=T.unsafe(nil)); end + + sig {returns(T.untyped)} + def install(); end + + sig do + params( + built_gem_path: T.untyped, + local: T.untyped, + ) + .returns(T.untyped) + end + def install_gem(built_gem_path=T.unsafe(nil), local=T.unsafe(nil)); end + + sig {returns(T.untyped)} + def name(); end + + sig do + params( + path: T.untyped, + ) + .returns(T.untyped) + end + def rubygem_push(path); end + + sig do + params( + cmd: T.untyped, + block: T.untyped, + ) + .returns(T.untyped) + end + def sh(cmd, &block); end + + sig do + params( + cmd: T.untyped, + ) + .returns(T.untyped) + end + def sh_with_input(cmd); end + + sig do + params( + cmd: T.untyped, + block: T.untyped, + ) + .returns(T.untyped) + end + def sh_with_status(cmd, &block); end + + sig {returns(T.untyped)} + def spec_path(); end + + sig do + params( + tag_prefix: T.untyped, + ) + .returns(T.untyped) + end + def tag_prefix=(tag_prefix); end + + sig {returns(T.untyped)} + def tag_version(); end + + sig {returns(T.untyped)} + def version(); end + + sig {returns(T.untyped)} + def version_tag(); end + + sig {returns(T.untyped)} + def self.instance(); end + + sig do + params( + instance: T.untyped, + ) + .returns(T.untyped) + end + def self.instance=(instance); end + + sig do + params( + opts: T.untyped, + ) + .returns(T.untyped) + end + def self.install_tasks(opts=T.unsafe(nil)); end + + sig do + params( + tag_prefix: T.untyped, + ) + .returns(T.untyped) + end + def self.tag_prefix=(tag_prefix); end + + sig do + params( + block: T.untyped, + ) + .returns(T.untyped) + end + def self.gemspec(&block); end +end + module Bundler::GemHelpers GENERICS = ::T.let(nil, T.untyped) GENERIC_CACHE = ::T.let(nil, T.untyped) @@ -6558,7 +6734,7 @@ end class Bundler::Molinillo::ResolverError < StandardError end -# Provides information about specifcations and dependencies to the resolver, +# Provides information about specifications and dependencies to the resolver, # allowing the {Resolver} class to remain generic while still providing power # and flexibility. # diff --git a/rbi/stdlib/date.rbi b/rbi/stdlib/date.rbi index e72c1a9dfd..15ca08bfc3 100644 --- a/rbi/stdlib/date.rbi +++ b/rbi/stdlib/date.rbi @@ -444,9 +444,6 @@ class Date sig {returns(T.self_type)} def succ(); end - sig {returns(T.untyped)} - def to_utc_time(); end - # Returns the Julian day number. This is a whole number, which is adjusted by # the offset as the local time. # diff --git a/rbi/stdlib/date_time.rbi b/rbi/stdlib/date_time.rbi index 7f5ef1c1a4..ea65ed3d79 100644 --- a/rbi/stdlib/date_time.rbi +++ b/rbi/stdlib/date_time.rbi @@ -523,9 +523,6 @@ class DateTime < Date sig {returns(T.untyped)} def blank?(); end - sig {returns(T.untyped)} - def to_utc_time(); end - sig do params( locale: T.untyped, diff --git a/rbi/stdlib/digest.rbi b/rbi/stdlib/digest.rbi index dbef08c626..03ab8d84fc 100644 --- a/rbi/stdlib/digest.rbi +++ b/rbi/stdlib/digest.rbi @@ -222,9 +222,9 @@ class Digest::Class # [`Digest.hexencode`](https://docs.ruby-lang.org/en/2.7.0/Digest.html#method-c-hexencode)([`Digest::Class.new(*parameters)`](https://docs.ruby-lang.org/en/2.7.0/Digest/Instance.html#method-i-new).digest(string)). sig do params( - _: ::T.untyped, + _: String, ) - .returns(::T.untyped) + .returns(String) end def self.hexdigest(*_); end end @@ -324,9 +324,9 @@ module Digest::Instance # the process. sig do params( - _: ::T.untyped, + _: String, ) - .returns(::T.untyped) + .returns(String) end def hexdigest(*_); end diff --git a/rbi/stdlib/etc.rbi b/rbi/stdlib/etc.rbi index 170e24e81b..a6d997d8e3 100644 --- a/rbi/stdlib/etc.rbi +++ b/rbi/stdlib/etc.rbi @@ -569,6 +569,15 @@ class Etc::Group < Struct extend T::Generic Elem = type_member {{fixed: T.untyped}} end + + sig { returns(Integer) } + def gid; end + sig { returns(T::Array[String]) } + def mem; end + sig { returns(String) } + def name; end + sig { returns(String) } + def passwd; end end # [`Passwd`](https://docs.ruby-lang.org/en/2.6.0/Etc.html#Passwd) diff --git a/rbi/stdlib/fileutils.rbi b/rbi/stdlib/fileutils.rbi index fdf11fefa7..9085d9380f 100644 --- a/rbi/stdlib/fileutils.rbi +++ b/rbi/stdlib/fileutils.rbi @@ -121,14 +121,14 @@ module FileUtils # ``` sig do params( - src: T.any(String, Pathname), + src: T.any(File, String, Pathname, T::Array[T.any(File, String, Pathname)]), dest: T.any(String, Pathname), preserve: T.nilable(T::Boolean), noop: T.nilable(T::Boolean), verbose: T.nilable(T::Boolean), dereference_root: T::Boolean, remove_destination: T.nilable(T::Boolean) - ).returns(T::Array[String]) + ).returns(T.nilable(T::Array[String])) end module_function def cp_r(src, dest, preserve: nil, noop: nil, verbose: nil, dereference_root: true, remove_destination: nil); end @@ -461,7 +461,7 @@ module FileUtils preserve: T.nilable(T::Boolean), noop: T.nilable(T::Boolean), verbose: T.nilable(T::Boolean) - ).void + ).returns(T.nilable(T::Array[T.any(File, String, Pathname)])) end module_function def cp(src, dest, preserve: nil, noop: nil, verbose: nil); end @@ -543,13 +543,13 @@ module FileUtils # ``` sig do params( - src: T.any(String, Pathname, T::Array[T.any(String, Pathname)]), + src: T.any(File, String, Pathname, T::Array[T.any(File, String, Pathname)]), dest: T.any(String, Pathname), noop: T.nilable(T::Boolean), verbose: T.nilable(T::Boolean), dereference_root: T::Boolean, remove_destination: T::Boolean - ).void + ).returns(T.nilable(T::Array[T.any(File, String, Pathname)])) end module_function def cp_lr(src, dest, noop: nil, verbose: nil, dereference_root: true, remove_destination: false); end @@ -744,13 +744,13 @@ module FileUtils # [`move`](https://docs.ruby-lang.org/en/2.6.0/FileUtils.html#method-c-move) sig do params( - src: T.any(String, Pathname, T::Array[T.any(String, Pathname)]), + src: T.any(File, String, Pathname, T::Array[T.any(File, String, Pathname)]), dest: T.any(String, Pathname), force: T.nilable(T::Boolean), noop: T.nilable(T::Boolean), verbose: T.nilable(T::Boolean), secure: T.nilable(T::Boolean) - ).returns(T.nilable(Integer)) + ).returns(T.nilable(T.any(Integer, T::Array[T.any(File, String, Pathname)]))) end module_function def mv(src, dest, force: nil, noop: nil, verbose: nil, secure: nil); end diff --git a/rbi/stdlib/irb.rbi b/rbi/stdlib/irb.rbi index 65f07e9b44..42fc67df59 100644 --- a/rbi/stdlib/irb.rbi +++ b/rbi/stdlib/irb.rbi @@ -443,6 +443,9 @@ module IRB # Quits irb def self.irb_exit(irb, ret); end + sig { params(ap_path: T.nilable(String), argv: T::Array[String]).void } + def self.setup(ap_path, argv: ::ARGV); end + # Initializes [`IRB`](https://docs.ruby-lang.org/en/2.7.0/IRB.html) and # creates a new Irb.irb object at the `TOPLEVEL_BINDING` def self.start(ap_path = _); end @@ -1682,3 +1685,37 @@ class IRB::WorkSpace # `IRB.conf[:__MAIN__]` def main; end end + +module IRB::Command + class << self + attr_reader :commands + + def register(name, command_class); end + end +end + +class IRB::Command::Base + class << self + def category(category = nil); end + + def description(description = nil); end + + def help_message(help_message = nil); end + end +end + +module IRB::HelperMethod + class << self + attr_reader :helper_methods + + def register(name, helper_class); end + + def all_helper_methods_info; end + end +end + +class IRB::HelperMethod::Base + class << self + def description(description = nil); end + end +end diff --git a/rbi/stdlib/json.rbi b/rbi/stdlib/json.rbi index 2abc300bd2..9e2b174415 100644 --- a/rbi/stdlib/json.rbi +++ b/rbi/stdlib/json.rbi @@ -1032,3 +1032,96 @@ end # This exception is raised if a parser error occurs. class JSON::ParserError < JSON::JSONError end + +class Array + include JSON::Ext::Generator::GeneratorMethods::Array +end + +class FalseClass + include JSON::Ext::Generator::GeneratorMethods::FalseClass +end + +class Float + include JSON::Ext::Generator::GeneratorMethods::Float +end + +class Hash + include JSON::Ext::Generator::GeneratorMethods::Hash +end + +class Integer + include JSON::Ext::Generator::GeneratorMethods::Integer +end + +class NilClass + include JSON::Ext::Generator::GeneratorMethods::NilClass +end + +class Object + include JSON::Ext::Generator::GeneratorMethods::Object +end + +class String + include JSON::Ext::Generator::GeneratorMethods::String +end + +class TrueClass + include JSON::Ext::Generator::GeneratorMethods::TrueClass +end + +# source://json//lib/json/add/exception.rb#6 +class Exception + # Methods Exception#as_json and +Exception.json_create+ may be used + # to serialize and deserialize a \Exception object; + # see Marshal[https://docs.ruby-lang.org/en/master/Marshal.html]. + # + # \Method Exception#as_json serializes +self+, + # returning a 2-element hash representing +self+: + # + # require 'json/add/exception' + # x = Exception.new('Foo').as_json # => {"json_class"=>"Exception", "m"=>"Foo", "b"=>nil} + # + # \Method +JSON.create+ deserializes such a hash, returning a \Exception object: + # + # Exception.json_create(x) # => # + # + # source://json//lib/json/add/exception.rb#29 + sig do + params( + _arg0: ::T.untyped, + ) + .returns(::T.untyped) + end + def as_json(*_arg0); end + + # Returns a JSON string representing +self+: + # + # require 'json/add/exception' + # puts Exception.new('Foo').to_json + # + # Output: + # + # {"json_class":"Exception","m":"Foo","b":null} + # + # source://json//lib/json/add/exception.rb#46 + sig do + params( + args: ::T.untyped, + ) + .returns(::T.untyped) + end + def to_json(*args); end + + class << self + # See #as_json. + # + # source://json//lib/json/add/exception.rb#9 + sig do + params( + object: ::T.untyped, + ) + .returns(::T.untyped) + end + def json_create(object); end + end +end diff --git a/rbi/stdlib/net.rbi b/rbi/stdlib/net.rbi index 396dffe708..bedeada342 100644 --- a/rbi/stdlib/net.rbi +++ b/rbi/stdlib/net.rbi @@ -1882,6 +1882,14 @@ class Net::HTTP < Net::Protocol # object. def lock(path, body, initheader=T.unsafe(nil)); end + def max_retries(); end + + # Maximum number of times to retry an idempotent request in case of + # Net::ReadTimeout, IOError, EOFError, Errno::ECONNRESET, Errno::ECONNABORTED, + # Errno::EPIPE, OpenSSL::SSL::SSLError, Timeout::Error. + # Should be a non-negative integer number. Zero means no retries. The default value is 1. + def max_retries=(retries); end + # Sends a MKCOL request to the `path` and gets a response, as an HTTPResponse # object. def mkcol(path, body=T.unsafe(nil), initheader=T.unsafe(nil)); end diff --git a/rbi/stdlib/nkf.rbi b/rbi/stdlib/nkf.rbi index f830025df3..ac774d78f9 100644 --- a/rbi/stdlib/nkf.rbi +++ b/rbi/stdlib/nkf.rbi @@ -191,7 +191,7 @@ # stream. # # -MQ -# : Perfome quoted encoding. +# : Perform quoted encoding. # # # ### -l @@ -319,7 +319,7 @@ # nkf can be used as UTF converter. (In other words, without this and -x option, # nkf doesn't save some characters) # -# When nkf convert string which related to path, you should use this opion. +# When nkf convert string which related to path, you should use this option. # # ### --cap-input # diff --git a/rbi/stdlib/objspace.rbi b/rbi/stdlib/objspace.rbi index 5ad8e42cb2..1452d99ae7 100644 --- a/rbi/stdlib/objspace.rbi +++ b/rbi/stdlib/objspace.rbi @@ -154,6 +154,73 @@ module ObjectSpace # Clear recorded tracing information. def self.trace_object_allocations_clear; end + + # Counts objects size (in bytes) for each type. + # Note that this information is incomplete. You need to deal with this information as only a HINT. Especially, total size of T_DATA may be wrong. + # It returns a hash as: + # {:TOTAL=>1461154, :T_CLASS=>158280, :T_MODULE=>20672, :T_STRING=>527249, ...} + # If the optional argument, result_hash, is given, it is overwritten and returned. This is intended to avoid probe effect. + # The contents of the returned hash is implementation defined. It may be changed in future. + # This method is only expected to work with C Ruby. + sig { params(result_hash: T.nilable(T::Hash[T.untyped, T.untyped])).returns(T::Hash[T.untyped, T.untyped]) } + sig { returns(T::Hash[Symbol, Integer])} + def self.count_objects_size(result_hash=nil); end + + # Counts objects for each T_IMEMO type. + # This method is only for MRI developers interested in performance and memory usage of Ruby programs. + # It returns a hash as: + # {:imemo_ifunc=>8, + # :imemo_svar=>7, + # :imemo_cref=>509, + # :imemo_memo=>1, + # :imemo_throw_data=>1} + # If the optional argument, result_hash, is given, it is overwritten and returned. This is intended to avoid probe effect. + # The contents of the returned hash is implementation specific and may change in the future. + # In this version, keys are symbol objects. + # This method is only expected to work with C Ruby. + sig { params(result_hash: T.nilable(T::Hash[T.untyped, T.untyped])).returns(T::Hash[T.untyped, T.untyped]) } + sig { returns(T::Hash[Symbol, Integer])} + def self.count_imemo_objects(result_hash=nil); end + + # Counts objects for each T_DATA type. + # This method is only for MRI developers interested in performance and memory usage of Ruby programs. + # It returns a hash as: + # {RubyVM::InstructionSequence=>504, :parser=>5, :barrier=>6, + # :mutex=>6, Proc=>60, RubyVM::Env=>57, Mutex=>1, Encoding=>99, + # ThreadGroup=>1, Binding=>1, Thread=>1, RubyVM=>1, :iseq=>1, + # Random=>1, ARGF.class=>1, Data=>1, :autoload=>3, Time=>2} + # # T_DATA objects existing at startup on r32276. + # If the optional argument, result_hash, is given, it is overwritten and returned. This is intended to avoid probe effect. + # The contents of the returned hash is implementation specific and may change in the future. + # In this version, keys are Class object or Symbol object. + # If object is kind of normal (accessible) object, the key is Class object. If object is not a kind of normal (internal) object, the key is symbol name, registered by rb_data_type_struct. + # This method is only expected to work with C Ruby. + sig { params(result_hash: T.nilable(T::Hash[T.untyped, T.untyped])).returns(T::Hash[T.untyped, T.untyped]) } + sig { returns(T::Hash[T.any(T::Class[T.anything], Symbol), Integer])} + def self.count_tdata_objects(result_hash=nil); end + + # Return consuming memory size of obj. + # Note that the return size is incomplete. You need to deal with this information as only a HINT. Especially, the size of T_DATA may not be correct. + # This method is only expected to work with C Ruby. + # From Ruby 2.2, ::memsize_of(obj) returns a memory size includes sizeof(RVALUE). + sig { params(klass: Object).returns(Integer) } + def self.memsize_of(klass); end + + # Return consuming memory size of all living objects. + # If klass (should be Class object) is given, return the total memory size of instances of the given class. + # Note that the returned size is incomplete. You need to deal with this information as only a HINT. Especially, the size of T_DATA may not be correct. + # Note that this method does NOT return total malloc’ed memory size. + # This method can be defined by the following Ruby code: + # def memsize_of_all klass = false + # total = 0 + # ObjectSpace.each_object{|e| + # total += ObjectSpace.memsize_of(e) if klass == false || e.kind_of?(klass) + # } + # total + # end + # This method is only expected to work with C Ruby. + sig { params(klass: T::Class[T.anything]).returns(Integer) } + def self.memsize_of_all(klass=nil); end end # An @@ -212,3 +279,88 @@ class ObjectSpace::WeakMap < Object def values; end end + +# An +# [`ObjectSpace::WeakKeyMap`](https://docs.ruby-lang.org/en/3.3/ObjectSpace/WeakKeyMap.html) +# is a key-value map that holds weak references to its keys, so they can be garbage collected +# when there is no more references. +# +# Unlike ObjectSpace::WeakMap: +# - references to values are strong, so they aren’t garbage collected while they are in the map; +# - keys are compared by value (using Object#eql?), not by identity; +# - only garbage-collectable objects can be used as keys. +# +# map = ObjectSpace::WeakKeyMap.new +# val = Time.new(2023, 12, 7) +# key = "name" +# map[key] = val +# +# # Value is fetched by equality: the instance of string "name" is +# # different here, but it is equal to the key +# map["name"] #=> 2023-12-07 00:00:00 +0200 +# +# val = nil +# GC.start +# # There is no more references to `val`, yet the pair isn't +# # garbage-collected. +# map["name"] #=> 2023-12-07 00:00:00 +0200 +# +# key = nil +# GC.start +# # There is no more references to `key`, key and value are +# # garbage-collected. +# +# map["name"] #=> nil +# +# (Note that GC.start is used here only for demonstrational purposes and might not always lead to demonstrated results.) +# +# The collection is especially useful for implementing caches of lightweight value objects, so that only one copy of +# each value representation would be stored in memory, but the copies that aren’t used would be garbage-collected. +# +# CACHE = ObjectSpace::WeakKeyMap +# +# def make_value(**) +# val = ValueObject.new(**) +# if (existing = @cache.getkey(val)) +# # if the object with this value exists, we return it +# existing +# else +# # otherwise, put it in the cache +# @cache[val] = true +# val +# end +# end +# +# This will result in make_value returning the same object for same set of attributes always, but the values that +# aren’t needed anymore woudn’t be sitting in the cache forever. +class ObjectSpace::WeakKeyMap < Object + + # Returns the value associated with the given key if found + # If key is not found, returns nil + def [](_); end + + # Associates the given value with the given key + # The reference to key is weak, so when there is no other reference to key it may be garbage collected + # If the given key exists, replaces its value with the given value; the ordering is not affected + def []=(_,_); end + + # Removes all map entries; returns self. + sig {returns(T.self_type)} + def clear; end + + # Deletes the entry for the given key and returns its associated value + # If no block is given and key is found, deletes the entry and returns the associated value + def delete(_); end + + # Returns the existing equal key if it exists, otherwise returns nil. + # This might be useful for implementing caches, so that only one copy of some object would be used everywhere in the program + def getkey(_); end + + # Returns a new String containing informations about the map + sig {returns(String)} + def inspect; end + + # Returns true if key is a key in self, otherwise false. + sig {params(key: T.any).returns(T::Boolean)} + def key?(key); end +end diff --git a/rbi/stdlib/openssl.rbi b/rbi/stdlib/openssl.rbi index b2737404d3..140550a45a 100644 --- a/rbi/stdlib/openssl.rbi +++ b/rbi/stdlib/openssl.rbi @@ -612,7 +612,7 @@ module OpenSSL sig {returns(::T.untyped)} def self.debug(); end - # Turns on or off debug mode. With debug mode, all erros added to the + # Turns on or off debug mode. With debug mode, all errors added to the # [`OpenSSL`](https://docs.ruby-lang.org/en/2.7.0/OpenSSL.html) error queue # will be printed to stderr. sig do @@ -649,6 +649,21 @@ module OpenSSL .returns(::T.untyped) end def self.fips_mode=(fips_mode); end + + # Constant time memory comparison for fixed length strings, such as results + # of HMAC calculations. + # + # Returns +true+ if the strings are identical, +false+ if they are of the same + # length but not identical. If the length is different, +ArgumentError+ is + # raised. + sig { params(a: String, b: String).returns(T::Boolean) } + def self.fixed_length_secure_compare(a, b); end + + # Constant time memory comparison. Inputs are hashed using SHA-256 to mask + # the length of the secret. Returns +true+ if the strings are identical, + # +false+ otherwise. + sig { params(a: String, b: String).returns(T::Boolean) } + def self.secure_compare(a, b); end end # Abstract Syntax Notation One (or ASN.1) is a notation syntax to describe data @@ -4427,6 +4442,10 @@ class OpenSSL::HMAC sig {returns(::T.untyped)} def hexdigest(); end + # Returns the authentication code as a Base64-encoded string. + sig {returns(::T.untyped)} + def base64digest(); end + sig do params( arg0: ::T.untyped, @@ -4539,6 +4558,28 @@ class OpenSSL::HMAC .returns(::T.untyped) end def self.hexdigest(arg0, arg1, arg2); end + + # Returns the authentication code as a Base64-encoded string. The *digest* + # parameter specifies the digest algorithm to use. This may be a + # [`String`](https://docs.ruby-lang.org/en/2.7.0/String.html) representing the + # algorithm name or an instance of + # [`OpenSSL::Digest`](https://docs.ruby-lang.org/en/2.7.0/OpenSSL/Digest.html). + # + # === Example + # key = 'key' + # data = 'The quick brown fox jumps over the lazy dog' + # + # hmac = OpenSSL::HMAC.base64digest('SHA1', key, data) + # #=> "3nybhbi3iqa8ino29wqQcBydtNk=" + sig do + params( + arg0: ::T.untyped, + arg1: ::T.untyped, + arg2: ::T.untyped, + ) + .returns(::T.untyped) + end + def self.base64digest(arg0, arg1, arg2); end end # Document-class: @@ -4869,7 +4910,7 @@ end # single_response = basic_response.find_response(certificate_id) # # unless single_response -# raise 'basic_response does not have the status for the certificiate' +# raise 'basic_response does not have the status for the certificate' # end # ``` # @@ -4994,7 +5035,7 @@ class OpenSSL::OCSP::BasicResponse # revocation, and must be one of OpenSSL::OCSP::REVOKED\_STATUS\_\* constants. # *revocation\_time* is the time when the certificate is revoked. # - # *this\_update* and *next\_update* indicate the time at which ths status is + # *this\_update* and *next\_update* indicate the time at which the status is # verified to be correct and the time at or before which newer information # will be available, respectively. *next\_update* is optional. # diff --git a/rbi/stdlib/optparse.rbi b/rbi/stdlib/optparse.rbi index 165196873f..6bad990823 100644 --- a/rbi/stdlib/optparse.rbi +++ b/rbi/stdlib/optparse.rbi @@ -460,7 +460,7 @@ class OptionParser sig {params(args: T.untyped).returns(T.untyped)} def inc(*args); end - sig {params(banner: T.untyped, width: T.untyped, indent: T.untyped, blk: T.untyped).void} + sig {params(banner: T.untyped, width: T.untyped, indent: T.untyped, blk: T.nilable(T.proc.params(opts: OptionParser).void)).void} def initialize(banner = nil, width = 32, indent = ' ' * 4, &blk); end sig {returns(T.untyped)} @@ -794,8 +794,33 @@ class OptionParser # Add option switch and handler. See # [`make_switch`](https://docs.ruby-lang.org/en/2.7.0/OptionParser.html#method-i-make_switch) # for an explanation of parameters. + sig do + params( + type: T.any(T.class_of(TrueClass), T.class_of(FalseClass)), + opts: T.untyped, + block: T.nilable(T.proc.params(arg0: T::Boolean).void) + ) + .returns(T.untyped) + end + sig do + params( + type: T.any(T.class_of(Shellwords), T.class_of(Array)), + opts: T.untyped, + block: T.nilable(T.proc.params(arg0: T::Array[String]).void) + ) + .returns(T.untyped) + end + sig do + type_parameters(:Type) + .params( + type: T::Class[T.type_parameter(:Type)], + opts: T.untyped, + block: T.nilable(T.proc.params(arg0: T.type_parameter(:Type)).void) + ) + .returns(T.untyped) + end sig {params(opts: T.untyped, block: T.untyped).returns(T.untyped)} - def on(*opts, &block); end + def on(type=T.unsafe(nil), *opts, &block); end # Also aliased as: # [`def_head_option`](https://docs.ruby-lang.org/en/2.7.0/OptionParser.html#method-i-def_head_option) diff --git a/rbi/stdlib/pathname.rbi b/rbi/stdlib/pathname.rbi index ed26c6c8cc..4debb8abad 100644 --- a/rbi/stdlib/pathname.rbi +++ b/rbi/stdlib/pathname.rbi @@ -971,7 +971,7 @@ class Pathname < Object ) .returns(Integer) end - def mkdir(p1); end + def mkdir(p1=T.unsafe(nil)); end # Creates a full path, including any intermediate directories that don't yet # exist. @@ -1181,9 +1181,15 @@ class Pathname < Object # Recursively deletes a directory, including all directories beneath it. # # See - # [`FileUtils.rm_r`](https://docs.ruby-lang.org/en/2.7.0/FileUtils.html#method-c-rm_r) - sig {returns(Integer)} - def rmtree(); end + # [`FileUtils.rm_rf`](https://docs.ruby-lang.org/en/2.7.0/FileUtils.html#method-c-rm_rf) + sig do + params( + noop: T.nilable(T::Boolean), + verbose: T.nilable(T::Boolean), + secure: T.nilable(T::Boolean) + ).void + end + def rmtree(noop: nil, verbose: nil, secure: nil); end # Predicate method for root directories. Returns `true` if the pathname # consists of consecutive slashes. @@ -1383,13 +1389,20 @@ class Pathname < Object # [`File.write`](https://docs.ruby-lang.org/en/2.7.0/IO.html#method-c-write). sig do params( - arg0: String, + arg0: Object, offset: Integer, - open_args: Integer, + external_encoding: T.any(String, Encoding), + internal_encoding: T.any(String, Encoding), + encoding: T.any(String, Encoding), + textmode: BasicObject, + binmode: BasicObject, + autoclose: BasicObject, + mode: String, + perm: Integer ) .returns(Integer) end - def write(arg0, offset=T.unsafe(nil), open_args=T.unsafe(nil)); end + def write(arg0, offset=T.unsafe(nil), external_encoding: T.unsafe(nil), internal_encoding: T.unsafe(nil), encoding: T.unsafe(nil), textmode: T.unsafe(nil), binmode: T.unsafe(nil), autoclose: T.unsafe(nil), mode: T.unsafe(nil), perm: T.unsafe(nil)); end # See # [`FileTest.zero?`](https://docs.ruby-lang.org/en/2.7.0/FileTest.html#method-i-zero-3F). diff --git a/rbi/stdlib/psych.rbi b/rbi/stdlib/psych.rbi index b19512eabe..09fad9248a 100644 --- a/rbi/stdlib/psych.rbi +++ b/rbi/stdlib/psych.rbi @@ -318,7 +318,6 @@ module Psych sig do params( yaml: T.any(String, StringIO, IO), - legacy_filename: Object, permitted_classes: T::Array[T::Class[T.anything]], permitted_symbols: T::Array[Symbol], aliases: T::Boolean, @@ -326,10 +325,11 @@ module Psych fallback: T.untyped, symbolize_names: T::Boolean, freeze: T::Boolean, + strict_integer: T::Boolean, ) .returns(T.untyped) end - def self.load(yaml, legacy_filename = T.unsafe(nil), permitted_classes: [Symbol], permitted_symbols: [], aliases: false, filename: T.unsafe(nil), fallback: T.unsafe(nil), symbolize_names: T.unsafe(nil), freeze: T.unsafe(nil)); end + def self.load(yaml, permitted_classes: [Symbol], permitted_symbols: [], aliases: false, filename: nil, fallback: nil, symbolize_names: false, freeze: false, strict_integer: false); end ### # Safely load the yaml string in `yaml`. By default, only the following @@ -392,10 +392,6 @@ module Psych sig do params( yaml: T.any(String, StringIO, IO), - legacy_permitted_classes: Object, - legacy_permitted_symbols: Object, - legacy_aliases: Object, - legacy_filename: Object, permitted_classes: T::Array[T::Class[T.anything]], permitted_symbols: T::Array[Symbol], aliases: T::Boolean, @@ -403,10 +399,11 @@ module Psych fallback: T.untyped, symbolize_names: T::Boolean, freeze: T::Boolean, + strict_integer: T::Boolean, ) .returns(T.untyped) end - def self.safe_load(yaml, legacy_permitted_classes = T.unsafe(nil), legacy_permitted_symbols = T.unsafe(nil), legacy_aliases = T.unsafe(nil), legacy_filename = T.unsafe(nil), permitted_classes: T.unsafe(nil), permitted_symbols: T.unsafe(nil), aliases: T.unsafe(nil), filename: T.unsafe(nil), fallback: T.unsafe(nil), symbolize_names: T.unsafe(nil), freeze: T.unsafe(nil)); end + def self.safe_load(yaml, permitted_classes: T.unsafe(nil), permitted_symbols: T.unsafe(nil), aliases: T.unsafe(nil), filename: T.unsafe(nil), fallback: T.unsafe(nil), symbolize_names: T.unsafe(nil), freeze: T.unsafe(nil), strict_integer: T.unsafe(nil)); end ### # Parse a [`YAML`](https://docs.ruby-lang.org/en/2.7.0/YAML.html) string in @@ -440,13 +437,11 @@ module Psych sig do params( yaml: T.any(String, StringIO, IO), - legacy_filename: Object, filename: T.nilable(String), - fallback: T.untyped, ) .returns(T.untyped) end - def self.parse(yaml, legacy_filename = T.unsafe(nil), filename: T.unsafe(nil), fallback: T.unsafe(nil)); end + def self.parse(yaml, filename: nil); end ### # Parse a file at `filename`. Returns the @@ -516,13 +511,12 @@ module Psych sig do params( yaml: T.any(String, StringIO, IO), - legacy_filename: Object, filename: T.nilable(String), block: T.nilable(T.proc.params(node: Psych::Nodes::Document).void), ) .returns(Psych::Nodes::Stream) end - def self.parse_stream(yaml, legacy_filename = T.unsafe(nil), filename: T.unsafe(nil), &block); end + def self.parse_stream(yaml, filename: T.unsafe(nil), &block); end ### # Dump Ruby object `o` to a @@ -616,13 +610,14 @@ module Psych sig do params( yaml: T.any(String, StringIO, IO), - legacy_filename: Object, filename: T.nilable(String), fallback: T.untyped, + kwargs: T.untyped, + block: T.nilable(T.proc.params(node: T.untyped).void), ) .returns(T::Array[T.untyped]) end - def self.load_stream(yaml, legacy_filename = T.unsafe(nil), filename: T.unsafe(nil), fallback: T.unsafe(nil)); end + def self.load_stream(yaml, filename: nil, fallback: [], **kwargs, &block); end ### # Load the document contained in `filename`. Returns the yaml contained in @@ -773,7 +768,7 @@ end class Psych::DisallowedClass < Psych::Exception - def initialize(klass_name); end + def initialize(action, klass_name); end end class Psych::Emitter < Psych::Handler @@ -952,7 +947,7 @@ class Psych::Handler # # `value` is the string value of the scalar `anchor` is an associated anchor # or nil `tag` is an associated tag or nil `plain` is a boolean value `quoted` - # is a boolean value `style` is an integer idicating the string style + # is a boolean value `style` is an integer indicating the string style # # See the constants in # [`Psych::Nodes::Scalar`](https://docs.ruby-lang.org/en/2.7.0/Psych/Nodes/Scalar.html) @@ -1464,7 +1459,7 @@ class Psych::Nodes::Node # # Also aliased as: # [`transform`](https://docs.ruby-lang.org/en/2.7.0/Psych/Nodes/Node.html#method-i-transform) - def to_ruby(); end + def to_ruby(symbolize_names: T.unsafe(nil), freeze: T.unsafe(nil), strict_integer: T.unsafe(nil)); end # Alias for: # [`yaml`](https://docs.ruby-lang.org/en/2.7.0/Psych/Nodes/Node.html#method-i-yaml) @@ -1472,7 +1467,7 @@ class Psych::Nodes::Node # Alias for: # [`to_ruby`](https://docs.ruby-lang.org/en/2.7.0/Psych/Nodes/Node.html#method-i-to_ruby) - def transform(); end + def transform(symbolize_names: T.unsafe(nil), freeze: T.unsafe(nil), strict_integer: T.unsafe(nil)); end # Convert this node to # [`YAML`](https://docs.ruby-lang.org/en/2.7.0/YAML.html). @@ -1754,7 +1749,7 @@ class Psych::Parser # See [`Psych::Parser`](https://docs.ruby-lang.org/en/2.7.0/Psych/Parser.html) # and # [`Psych::Parser#handler`](https://docs.ruby-lang.org/en/2.7.0/Psych/Parser.html#attribute-i-handler) - def parse(*arg0); end + def parse(yaml, path = T.unsafe(nil)); end end class Psych::Parser::Mark @@ -1783,7 +1778,7 @@ class Psych::ScalarScanner def class_loader(); end - def initialize(class_loader); end + def initialize(class_loader, strict_integer: T.unsafe(nil)); end # Parse and return an int from `string` def parse_int(string); end @@ -1983,7 +1978,7 @@ class Psych::Visitors::ToRuby < Psych::Visitors::Visitor def class_loader(); end - def initialize(ss, class_loader); end + def initialize(ss, class_loader, symbolize_names: T.unsafe(nil), freeze: T.unsafe(nil)); end def visit_Psych_Nodes_Alias(o); end @@ -1997,7 +1992,7 @@ class Psych::Visitors::ToRuby < Psych::Visitors::Visitor def visit_Psych_Nodes_Stream(o); end - def self.create(); end + def self.create(symbolize_names: T.unsafe(nil), freeze: T.unsafe(nil), strict_integer: T.unsafe(nil)); end end class Psych::Visitors::Visitor diff --git a/rbi/stdlib/racc.rbi b/rbi/stdlib/racc.rbi index eac0e56450..58bc8cd8a0 100644 --- a/rbi/stdlib/racc.rbi +++ b/rbi/stdlib/racc.rbi @@ -154,7 +154,7 @@ end # -v, --verbose # : verbose mode. create `filename`.output file, like yacc's y.output file. # -g, --debug -# : add debug code to parser class. To display debuggin information, use this +# : add debug code to parser class. To display debugging information, use this # '-g' option and set @yydebug true in parser class. # -E, --embedded # : Output parser which doesn't need runtime files (racc/parser.rb). diff --git a/rbi/stdlib/rdoc.rbi b/rbi/stdlib/rdoc.rbi index bd06d04329..f28c237532 100644 --- a/rbi/stdlib/rdoc.rbi +++ b/rbi/stdlib/rdoc.rbi @@ -2184,7 +2184,7 @@ end # A PO entry in PO class RDoc::Generator::POT::POEntry - # Creates a PO entry for `msgid`. Other valus can be specified by `options`. + # Creates a PO entry for `msgid`. Other values can be specified by `options`. def self.new(msgid, options = _); end # The comment content extracted from source file @@ -8143,7 +8143,7 @@ class RDoc::RDoc # Removes a siginfo handler and replaces the previous def remove_siginfo_handler; end - # Removes file extensions known to be unparseable from `files` and TAGS files + # Removes file extensions known to be unparsable from `files` and TAGS files # for emacs and vim. def remove_unparseable(files); end diff --git a/rbi/stdlib/rubygems.rbi b/rbi/stdlib/rubygems.rbi index bda79daf4c..00ad8620fb 100644 --- a/rbi/stdlib/rubygems.rbi +++ b/rbi/stdlib/rubygems.rbi @@ -2634,9 +2634,14 @@ class Gem::Version < Object sig do params( - version: String, - ) - .void + version: T.any( + String, + Integer, + Float, # Technically, any `to_s`able object would work. + Gem::Version, # Will return the same version object + NilClass # Deprecated, and will issue a warning + ) + ).void end def initialize(version); end @@ -2644,17 +2649,21 @@ class Gem::Version < Object def _split_segments(); end + sig { returns(String) } def _version(); end # A recommended version for use with a ~> Requirement. + sig { returns(String) } def approximate_recommendation(); end # Return a new version object where the next to the last revision number is # one greater (e.g., 5.3.1 => 5.4). # # Pre-release (alpha) parts, e.g, 5.3.1.b.2 => 5.4, are ignored. + sig { returns(Gem::Version) } def bump(); end + sig { returns(T::Array[String]) } def canonical_segments(); end def encode_with(coder); end @@ -2663,27 +2672,34 @@ class Gem::Version < Object # eql? to another version if it's specified to the same precision. # [`Version`](https://docs.ruby-lang.org/en/2.7.0/Gem/Version.html) "1.0" is # not the same as version "1". + sig { params(other: T.untyped).returns(T::Boolean) } def eql?(other); end def init_with(coder); end # Dump only the raw version string, not the complete object. It's a string for # backwards (RubyGems 1.3.5 and earlier) compatibility. + sig { returns(T::Array[T.untyped]) } def marshal_dump(); end # Load custom marshal format. It's a string for backwards (RubyGems 1.3.5 and # earlier) compatibility. + sig { params(array: T::Array[T.untyped]).void } def marshal_load(array); end # A version is considered a prerelease if it contains a letter. + sig { returns(T::Boolean) } def prerelease?(); end # The release for this version (e.g. 1.2.0.a -> 1.2.0). Non-prerelease # versions return themselves. + sig { returns(Gem::Version) } def release(); end + sig { returns(T::Array[String]) } def segments(); end + sig { returns(T::Array[String]) } def to_yaml_properties(); end # A string representation of this @@ -2691,11 +2707,13 @@ class Gem::Version < Object # # Also aliased as: # [`to_s`](https://docs.ruby-lang.org/en/2.7.0/Gem/Version.html#method-i-to_s) + sig { returns(String) } def version(); end def yaml_initialize(tag, map); end # True if the `version` string matches RubyGems' requirements. + sig { params(version: T.untyped).returns(T::Boolean) } def self.correct?(version); end # Factory method to create a @@ -2710,12 +2728,24 @@ class Gem::Version < Object # ver2 = Version.create(ver1) # -> (ver1) # ver3 = Version.create(nil) # -> nil # ``` + sig { params(input: T.any(NilClass, String, Gem::Version)).returns(T.nilable(Gem::Version)) } def self.create(input); end # Constructs a # [`Version`](https://docs.ruby-lang.org/en/2.7.0/Gem/Version.html) from the # `version` string. A version string is a series of digits or ASCII letters # separated by dots. + sig do + params( + version: T.any( + String, + Integer, + Float, # Technically, any `to_s`able object would work. + Gem::Version, # Will return the same version object + NilClass # Deprecated, and will issue a warning + ) + ).returns(Gem::Version) + end def self.new(version); end end @@ -6196,7 +6226,7 @@ end class Gem::Resolver::Molinillo::ResolverError < StandardError end -# Provides information about specifcations and dependencies to the resolver, +# Provides information about specifications and dependencies to the resolver, # allowing the {Resolver} class to remain generic while still providing power # and flexibility. # @@ -7044,6 +7074,10 @@ class Gem::Security::DIGEST_ALGORITHM < OpenSSL::Digest def self.hexdigest(data); end end +# [`Gem::Security`](https://docs.ruby-lang.org/en/2.7.0/Gem/Security.html) +# default exception type +class Gem::Security::Exception < Gem::Exception; end + class Gem::Security::Policy include Gem::UserInteraction @@ -7481,6 +7515,18 @@ module Gem::Util def self.traverse_parents(directory, &block); end end +# The `BUNDLED_GEMS` (undocumented) module was introduced in Ruby 3.3. +module Gem::BUNDLED_GEMS + SINCE = ::T.let(nil, T::Hash[String, String]) + EXACT = ::T.let(nil, T::Hash[String, T::Boolean]) + PREFIXED = ::T.let(nil, T::Hash[String, T::Boolean]) + WARNED = ::T.let(nil, T::Hash[String, T::Boolean]) + LIBDIR = ::T.let(nil, String) + ARCHDIR = ::T.let(nil, String) + DLEXT = ::T.let(nil, Regexp) + LIBEXT = ::T.let(nil, Regexp) +end + module Kernel # Use # [`Kernel#gem`](https://docs.ruby-lang.org/en/2.7.0/Kernel.html#method-i-gem) diff --git a/rbi/stdlib/set.rbi b/rbi/stdlib/set.rbi index e6d2829016..1222fc46a6 100644 --- a/rbi/stdlib/set.rbi +++ b/rbi/stdlib/set.rbi @@ -642,13 +642,27 @@ class Set < Object sig {returns(T.nilable(T.self_type))} def flatten!(); end + # Creates a new set containing the elements of the given + # enumerable object. + # + # If a block is given, the elements of enum are preprocessed by + # the given block. + # + # ```ruby + # Set.new([1, 2]) #=> # + # Set.new([1, 2, 1]) #=> # + # Set.new([1, 'c', :s]) #=> # + # Set.new(1..5) #=> # + # Set.new([1, 2, 3]) { |x| x * x } #=> # + # ``` sig do - params( - enum: T.nilable(T::Enumerable[BasicObject]), + type_parameters(:U).params( + enum: T.nilable(T::Enumerable[T.type_parameter(:U)]), + blk: T.nilable(T.proc.params(arg0: T.type_parameter(:U)).returns(Elem)) ) .void end - def initialize(enum=nil); end + def initialize(enum=nil, &blk); end # Returns true if the set and the given enumerable have at least # one @@ -663,7 +677,7 @@ class Set < Object # ``` sig do params( - set: T::Set[Elem], + set: T::Enumerable[Elem], ) .returns(T::Boolean) end diff --git a/rbi/stdlib/shellwords.rbi b/rbi/stdlib/shellwords.rbi index 3a0aae8392..69ae654d9a 100644 --- a/rbi/stdlib/shellwords.rbi +++ b/rbi/stdlib/shellwords.rbi @@ -212,3 +212,37 @@ module Shellwords sig { params(str: String).returns(T::Array[String]) } def self.split(str); end end + +class String + # call-seq: + # str.shellsplit => array + # + # Splits +str+ into an array of tokens in the same way the UNIX + # Bourne shell does. + # + # See Shellwords.shellsplit for details. + sig { returns(T::Array[String]) } + def shellsplit; end + + # call-seq: + # str.shellescape => string + # + # Escapes +str+ so that it can be safely used in a Bourne shell + # command line. + # + # See Shellwords.shellescape for details. + sig { returns(String) } + def shellescape; end +end + +class Array + # call-seq: + # array.shelljoin => string + # + # Builds a command line string from an argument list +array+ joining + # all elements escaped for the Bourne shell and separated by a space. + # + # See Shellwords.shelljoin for details. + sig { returns(String) } + def shelljoin; end +end diff --git a/rbi/stdlib/singleton.rbi b/rbi/stdlib/singleton.rbi index 2951b0cb35..72f2e64adc 100644 --- a/rbi/stdlib/singleton.rbi +++ b/rbi/stdlib/singleton.rbi @@ -110,6 +110,9 @@ module Singleton sig {returns(T.attached_class)} def instance; end + sig { params(klass: T::Class[T.anything]).void } + def self.__init__(klass); end + private sig {void} diff --git a/rbi/stdlib/socket.rbi b/rbi/stdlib/socket.rbi index 0a22b33bdb..341edf8021 100644 --- a/rbi/stdlib/socket.rbi +++ b/rbi/stdlib/socket.rbi @@ -1820,6 +1820,8 @@ class Socket < BasicSocket MSG_DONTROUTE = ::T.let(nil, ::T.untyped) # This message should be non-blocking MSG_DONTWAIT = ::T.let(nil, ::T.untyped) + # [Data completes connection](https://ruby-doc.org/stdlib-3.0.0/libdoc/socket/rdoc/Socket.html#MSG_EOF) + MSG_EOF = ::T.let(nil, ::T.untyped) # [`Data`](https://docs.ruby-lang.org/en/2.7.0/Data.html) completes record MSG_EOR = ::T.let(nil, ::T.untyped) # Fetch message from error queue diff --git a/rbi/stdlib/zlib.rbi b/rbi/stdlib/zlib.rbi index f801731049..1776400142 100644 --- a/rbi/stdlib/zlib.rbi +++ b/rbi/stdlib/zlib.rbi @@ -860,7 +860,7 @@ class Zlib::GzipReader < ::Zlib::GzipFile # documentation for a description. def readlines(*_); end - # Reads at most *maxlen* bytes from the gziped stream but it blocks only if + # Reads at most *maxlen* bytes from the gzipped stream but it blocks only if # *gzipreader* has no data immediately available. If the optional *outbuf* # argument is present, it must reference a # [`String`](https://docs.ruby-lang.org/en/2.7.0/String.html), which will @@ -1282,7 +1282,7 @@ class Zlib::ZStream # Returns true if the stream is closed. def closed?; end - # Guesses the type of the data which have been inputed into the stream. The + # Guesses the type of the data which have been inputted into the stream. The # returned value is either `BINARY`, `ASCII`, or `UNKNOWN`. def data_type; end diff --git a/rbi/tools/generate_procs.cc b/rbi/tools/generate_procs.cc index 9cf6be7840..64d2ca4826 100644 --- a/rbi/tools/generate_procs.cc +++ b/rbi/tools/generate_procs.cc @@ -46,6 +46,11 @@ void emitProc(ofstream &out, int arity) { int main(int argc, char **argv) { ofstream rb(argv[1], ios::trunc); rb << "# typed: true" << '\n'; + rb << '\n'; + rb << "# DO NOT EDIT!" << '\n'; + rb << "# This file is autogenerated. Regenerate with:" << '\n'; + rb << "# bazel run //rbi:update_procs_rbi" << '\n'; + rb << '\n'; for (int arity = 0; arity <= MAX_PROC_ARITY; ++arity) { emitProc(rb, arity); } diff --git a/rbi/tools/sync-rdoc.rb b/rbi/tools/sync-rdoc.rb index 910e6ef0c1..a0748ba9a9 100755 --- a/rbi/tools/sync-rdoc.rb +++ b/rbi/tools/sync-rdoc.rb @@ -480,6 +480,14 @@ class SyncRDoc opts = RDoc::Options.new opts.parse(['--all']) opts.setup_generator("darkfish") + + $LOAD_PATH.each do |path| + darkfish_dir = File.join(path, 'rdoc/generator/template/darkfish/') + next unless File.directory?(darkfish_dir) + opts.template_dir = darkfish_dir + break + end + opts end end @@ -531,10 +539,10 @@ class SyncRDoc def process_file!(file) to_replace = [] - puts file.path DocParser.new(file).each_doc do |path, def_node, doc_range, indentation| next if SKIP.any? {|s| s.is_a?(String) ? path == s : path =~ s} - next unless ONLY_IN_FILE[path] == file.path + only_in_file = ONLY_IN_FILE[path] + next if only_in_file && only_in_file != file.path namespace, separator, name = path.rpartition(/::|\.|\#/) code_obj = case separator when "" diff --git a/resolver/GlobalPass.cc b/resolver/GlobalPass.cc index 23634360f9..9ba9a10f37 100644 --- a/resolver/GlobalPass.cc +++ b/resolver/GlobalPass.cc @@ -91,6 +91,22 @@ void reportRedeclarationError(core::GlobalState &gs, core::ClassOrModuleRef pare e.setHeader("Type `{}` declared by parent `{}` must be re-declared in `{}`", name.show(gs), parent.show(gs), sym.show(gs)); e.addErrorLine(parentTypeMember.data(gs)->loc(), "`{}` declared in parent here", name.show(gs)); + auto symIsSingletonClass = true; + auto maybeDefinedOn = sym.data(gs)->attachedClass(gs); + if (!maybeDefinedOn.exists()) { + symIsSingletonClass = false; + // implies we _are_ the attached class + maybeDefinedOn = sym.data(gs)->lookupSingletonClass(gs); + } + if (maybeDefinedOn.exists()) { + auto didYouMeanMember = maybeDefinedOn.data(gs)->findMember(gs, name); + if (didYouMeanMember.exists() && didYouMeanMember.isTypeMember()) { + e.addErrorNote("Did you mean to define `{}` as a `{}` instead?", name.show(gs), + symIsSingletonClass ? "type_template" : "type_member"); + e.addErrorLine(didYouMeanMember.loc(gs), "A `{}` with the same name is defined here", + symIsSingletonClass ? "type_member" : "type_template"); + } + } } } } @@ -100,7 +116,7 @@ bool resolveTypeMember(core::GlobalState &gs, core::ClassOrModuleRef parent, cor core::ClassOrModuleRef sym, vector>> &typeAliases) { core::NameRef name = parentTypeMember.data(gs)->name; - core::SymbolRef my = sym.data(gs)->findMemberNoDealias(gs, name); + core::SymbolRef my = sym.data(gs)->findMemberNoDealias(name); if (!my.exists()) { reportRedeclarationError(gs, parent, parentTypeMember, sym); diff --git a/resolver/resolver.cc b/resolver/resolver.cc index f2236a232d..ffe6a17f9d 100644 --- a/resolver/resolver.cc +++ b/resolver/resolver.cc @@ -1,4 +1,5 @@ #include "core/errors/resolver.h" +#include "absl/strings/escaping.h" #include "ast/Helpers.h" #include "ast/Trees.h" #include "ast/ast.h" @@ -7,6 +8,7 @@ #include "core/Error.h" #include "core/Names.h" #include "core/StrictLevel.h" +#include "core/TypeErrorDiagnostics.h" #include "core/core.h" #include "core/errors/internal.h" #include "core/lsp/TypecheckEpochManager.h" @@ -140,6 +142,10 @@ class ResolveConstantsWalk { // Increments to track whether we're at a class top level or whether we're inside a block/method body. int loadScopeDepth_; + bool loadTimeScope() { + return loadScopeDepth_ == 0; + } + struct ConstantResolutionItem { shared_ptr scope; ast::ConstantLit *out; @@ -251,7 +257,7 @@ class ResolveConstantsWalk { // We don't want to rely on existing information in the symbol table for the // fast path in LSP, but we do need to explicitly look through type template // static fields to find the field on the singleton class. - auto lookup = scope->scope.asClassOrModuleRef().data(ctx)->findMemberNoDealias(ctx, name); + auto lookup = scope->scope.asClassOrModuleRef().data(ctx)->findMemberNoDealias(name); if (lookup.isStaticField(ctx)) { if (lookup.asFieldRef().data(ctx)->isClassAlias()) { auto dealiased = lookup.dealias(ctx); @@ -340,29 +346,33 @@ class ResolveConstantsWalk { } } - static core::SymbolRef resolveConstant(core::Context ctx, const shared_ptr &nesting, - const ast::UnresolvedConstantLit &c, bool &resolutionFailed) { + static core::SymbolRef resolveConstant(core::Context ctx, ConstantResolutionItem &job) { + auto &c = ast::cast_tree_nonnull(job.out->original); if (ast::isa_tree(c.scope)) { - core::SymbolRef result = resolveLhs(ctx, nesting, c.cnst); + auto result = resolveLhs(ctx, job.scope, c.cnst); return result; } if (auto *id = ast::cast_tree(c.scope)) { auto sym = id->symbol; - if (sym.exists() && sym.isTypeAlias(ctx) && !resolutionFailed) { + if (!sym.exists()) { + // Still waiting for scope to be resolved. Don't mark resolutionFailed yet, just + // return noSymbol so that this job is picked up on the next time through the loop. + return core::Symbols::noSymbol(); + } + + if (sym.isTypeAlias(ctx) && !job.resolutionFailed) { if (auto e = ctx.beginError(c.loc, core::errors::Resolver::ConstantInTypeAlias)) { e.setHeader("Resolving constants through type aliases is not supported"); } - resolutionFailed = true; + job.resolutionFailed = true; return core::Symbols::noSymbol(); } - if (!sym.exists()) { - return core::Symbols::noSymbol(); - } - core::SymbolRef resolved = id->symbol.dealias(ctx); + + auto resolved = id->symbol.dealias(ctx); core::SymbolRef result; if (resolved.isClassOrModule()) { - result = resolved.asClassOrModuleRef().data(ctx)->findMemberNoDealias(ctx, c.cnst); + result = resolved.asClassOrModuleRef().data(ctx)->findMemberNoDealias(c.cnst); } // Private constants are allowed to be resolved, when there is no scope set (the scope is checked above), @@ -379,19 +389,19 @@ class ResolveConstantsWalk { return result; } - if (!resolutionFailed) { + if (!job.resolutionFailed) { if (auto e = ctx.beginError(c.loc, core::errors::Resolver::DynamicConstant)) { e.setHeader("Dynamic constant references are unsupported"); } } - resolutionFailed = true; + job.resolutionFailed = true; return core::Symbols::noSymbol(); } static const int MAX_SUGGESTION_COUNT = 10; struct PackageStub { - core::NameRef packageId; + core::packages::MangledName packageId; vector fullName; PackageStub(const core::packages::PackageInfo &info) @@ -680,12 +690,11 @@ class ResolveConstantsWalk { // We have failed to resolve the constant. We'll need to report the error and stub it so that we can proceed static void constantResolutionFailed(core::GlobalState &gs, core::FileRef file, ConstantResolutionItem &job, const ImportStubs &importStubs, int &suggestionCount) { - auto &original = ast::cast_tree_nonnull(job.out->original); core::Context ctx(gs, core::Symbols::root(), file); bool singlePackageRbiGeneration = ctx.state.singlePackageImports.has_value(); - auto resolved = resolveConstant(ctx.withOwner(job.scope->scope), job.scope, original, job.resolutionFailed); + auto resolved = resolveConstant(ctx, job); if (resolved.exists() && resolved.isTypeAlias(ctx)) { auto resolvedField = resolved.asFieldRef(); if (resolvedField.data(ctx)->resultType == nullptr) { @@ -708,7 +717,7 @@ class ResolveConstantsWalk { } if (job.resolutionFailed) { // we only set this when a job has failed for other reasons and we've already reported an error, and - // continuining on will only redundantly report that we can't resolve the constant, so bail early here + // continuing on will only redundantly report that we can't resolve the constant, so bail early here job.out->symbol = core::Symbols::untyped(); return; } @@ -727,6 +736,7 @@ class ResolveConstantsWalk { bool alreadyReported = false; job.out->resolutionScopes = make_unique(); + auto &original = ast::cast_tree_nonnull(job.out->original); if (auto *id = ast::cast_tree(original.scope)) { auto originalScope = id->symbol.dealias(ctx); if (originalScope == core::Symbols::StubModule()) { @@ -829,36 +839,30 @@ class ResolveConstantsWalk { } } - static bool resolveConstantJob(core::Context ctx, const shared_ptr &nesting, ast::ConstantLit *out, - bool &resolutionFailed, const bool possibleGenericType) { - if (isAlreadyResolved(ctx, *out)) { - if (possibleGenericType) { + static bool resolveConstantJob(core::Context ctx, ConstantResolutionItem &job) { + if (isAlreadyResolved(ctx, *job.out)) { + if (job.possibleGenericType) { return false; } return true; } - auto &original = ast::cast_tree_nonnull(out->original); - auto resolved = resolveConstant(ctx.withOwner(nesting->scope), nesting, original, resolutionFailed); + auto resolved = resolveConstant(ctx, job); if (!resolved.exists()) { return false; } if (resolved.isTypeAlias(ctx)) { auto resolvedField = resolved.asFieldRef(); if (resolvedField.data(ctx)->resultType != nullptr) { - out->symbol = resolved; + job.out->symbol = resolved; return true; } return false; } - out->symbol = resolved; + job.out->symbol = resolved; return true; } - static bool resolveJob(core::Context ctx, ConstantResolutionItem &job) { - return resolveConstantJob(ctx, job.scope, job.out, job.resolutionFailed, job.possibleGenericType); - } - static bool resolveConstantResolutionItems(const core::GlobalState &gs, vector> &jobs, WorkerPool &workers) { @@ -885,7 +889,7 @@ class ResolveConstantsWalk { auto origSize = job.items.size(); auto fileIt = remove_if(job.items.begin(), job.items.end(), - [&](ConstantResolutionItem &item) -> bool { return resolveJob(ictx, item); }); + [&](ConstantResolutionItem &item) -> bool { return resolveConstantJob(ictx, item); }); job.items.erase(fileIt, job.items.end()); retries += origSize - job.items.size(); if (!job.items.empty()) { @@ -915,25 +919,7 @@ class ResolveConstantsWalk { } static bool resolveTypeAliasJob(core::MutableContext ctx, TypeAliasResolutionItem &job) { - core::TypeMemberRef enclosingTypeMember; - core::ClassOrModuleRef enclosingClass = job.lhs.enclosingClass(ctx); - while (enclosingClass != core::Symbols::root()) { - auto typeMembers = enclosingClass.data(ctx)->typeMembers(); - if (!typeMembers.empty()) { - enclosingTypeMember = typeMembers[0]; - break; - } - enclosingClass = enclosingClass.data(ctx)->owner; - } auto &rhs = *job.rhs; - if (enclosingTypeMember.exists()) { - if (auto e = ctx.beginError(rhs.loc(), core::errors::Resolver::TypeAliasInGenericClass)) { - e.setHeader("Type aliases are not allowed in generic classes"); - e.addErrorLine(enclosingTypeMember.data(ctx)->loc(), "Here is enclosing generic member"); - } - job.lhs.setResultType(ctx, core::Types::untyped(job.lhs)); - return true; - } if (isFullyResolved(ctx, rhs)) { // this todo will be resolved during ResolveTypeMembersAndFieldsWalk below job.lhs.setResultType(ctx, core::make_type(core::Symbols::todo())); @@ -1009,7 +995,9 @@ class ResolveConstantsWalk { return core::Symbols::StubMixin(); } - static bool resolveAncestorJob(core::MutableContext ctx, AncestorResolutionItem &job, bool lastRun) { + static bool resolveAncestorJob(core::MutableContext ctx, AncestorResolutionItem &job, + const UnorderedSet suppressPayloadSuperclassRedefinitionFor, + bool lastRun) { auto ancestorSym = job.ancestor->symbol; if (!ancestorSym.exists()) { if (!lastRun && !job.isSuperclass && !job.mixinIndex.has_value()) { @@ -1078,9 +1066,23 @@ class ResolveConstantsWalk { job.klass.data(ctx)->superClass() == resolvedClass) { job.klass.data(ctx)->setSuperClass(resolvedClass); } else { - if (auto e = ctx.beginError(job.ancestor->loc, core::errors::Resolver::RedefinitionOfParents)) { - e.setHeader("Parent of class `{}` redefined from `{}` to `{}`", job.klass.show(ctx), - job.klass.data(ctx)->superClass().show(ctx), resolvedClass.show(ctx)); + auto fileIsPayload = ctx.file.data(ctx).isPayload(); + auto klassDefinedInPayload = absl::c_any_of( + job.klass.data(ctx)->locs(), [&](auto &loc) { return loc.file().data(ctx).isPayload(); }); + auto allowsPayloadParentOverride = suppressPayloadSuperclassRedefinitionFor.contains(job.klass); + + if (!allowsPayloadParentOverride) { + if (auto e = ctx.beginError(job.ancestor->loc, core::errors::Resolver::RedefinitionOfParents)) { + e.setHeader("Parent of class `{}` redefined from `{}` to `{}`", job.klass.show(ctx), + job.klass.data(ctx)->superClass().show(ctx), resolvedClass.show(ctx)); + if (!fileIsPayload && klassDefinedInPayload) { + e.addErrorNote( + "Pass `{}` at the command line or in the `{}` file to silence this error.\n" + " Only use this to work around Ruby or gem upgrade incompatibilities.", + fmt::format("--suppress-payload-superclass-redefinition-for={}", job.klass.show(ctx)), + "sorbet/config"); + } + } } } } else { @@ -1170,16 +1172,19 @@ class ResolveConstantsWalk { // Get the fake property holding the mixes auto mixMethod = ownerKlass.data(gs)->findMethod(gs, core::Names::mixedInClassMethods()); + auto loc = core::Loc{todo.file, send->loc}; if (!mixMethod.exists()) { // We never stored a mixin in this symbol // Create a the fake property that will hold the mixed in modules - mixMethod = gs.enterMethodSymbol(core::Loc{todo.file, send->loc}, ownerKlass, - core::Names::mixedInClassMethods(), core::Loc::none()); + mixMethod = gs.enterMethodSymbol(loc, ownerKlass, core::Names::mixedInClassMethods(), core::Loc::none()); + mixMethod.data(gs)->resultType = core::make_type(vector{}); // Create a dummy block argument to satisfy sanitycheck during GlobalState::expandNames auto &arg = gs.enterMethodArgumentSymbol(core::Loc::none(), mixMethod, core::Names::blkArg()); arg.flags.isBlock = true; + } else { + mixMethod.data(gs)->addLoc(gs, loc); } auto type = core::make_type(idSymbol); @@ -1288,7 +1293,7 @@ class ResolveConstantsWalk { void transformAncestor(core::Context ctx, core::ClassOrModuleRef klass, ast::ExpressionPtr &ancestor, bool isInclude, bool isSuperclass = false) { - if (auto *constScope = ast::cast_tree(ancestor)) { + if (ast::isa_tree(ancestor)) { auto scopeTmp = nesting_; if (isSuperclass) { nesting_ = nesting_->parent; @@ -1341,12 +1346,11 @@ class ResolveConstantsWalk { auto loc = c->loc; auto out = ast::make_expression(loc, core::Symbols::noSymbol(), std::move(tree)); auto *constant = ast::cast_tree(out); - bool resolutionFailed = false; - const bool possibleGenericType = false; - if (resolveConstantJob(ctx, nesting_, constant, resolutionFailed, possibleGenericType)) { + ConstantResolutionItem job{nesting_, constant}; + if (resolveConstantJob(ctx, job)) { categoryCounterInc("resolve.constants.nonancestor", "firstpass"); - if (loadScopeDepth_ == 0 && (!constant->symbol.isClassOrModule() || - constant->symbol.asClassOrModuleRef().data(ctx)->isDeclared())) { + if (this->loadTimeScope() && (!constant->symbol.isClassOrModule() || + constant->symbol.asClassOrModuleRef().data(ctx)->isDeclared())) { // While Sorbet treats class A::B; end like an implicit definition of A, it's actually a // reference of A--Ruby will require a proper definition of A elsewhere. Long term, // Sorbet should be taught to emit errors when these references are not actually defined, @@ -1360,8 +1364,6 @@ class ResolveConstantsWalk { checkReferenceOrder(ctx, constant->symbol, *c, firstDefinitionLocs); } } else { - ConstantResolutionItem job{nesting_, constant}; - job.resolutionFailed = resolutionFailed; todo_.emplace_back(std::move(job)); } tree = std::move(out); @@ -1409,7 +1411,7 @@ class ResolveConstantsWalk { // Populate local first definitions table for out-of-order reference checking. // We only do this when we know we're going to need to consult the first definition loc and - // we know the information in the symbol table is insufficient. This avoids reundant memory + // we know the information in the symbol table is insufficient. This avoids redundant memory // storage overhead. // // In particular, `firstDefinitionLocs` does not store anything if this symbol is defined in @@ -1562,13 +1564,13 @@ class ResolveConstantsWalk { // Populate local first definitions table for out-of-order reference checking. // We only do this when we know we're going to need to consult the first definition loc and - // we know the information in the symbol table is insufficient. This avoids reundant memory + // we know the information in the symbol table is insufficient. This avoids redundant memory // storage overhead. // // In particular, `firstDefinitionLocs` does not store anything if this symbol is defined in // more than one non-RBI file or if it's only defined once in this file. // Otherwise, it stores the loc of the first definition of the symbol in this file. - if (!ctx.file.data(ctx).isRBI() && loadScopeDepth_ == 0 && + if (!ctx.file.data(ctx).isRBI() && this->loadTimeScope() && id->symbol.isOnlyDefinedInFile(ctx.state, ctx.file)) { auto defLoc = id->symbol.loc(ctx); @@ -1659,7 +1661,7 @@ class ResolveConstantsWalk { // possible generic types. ConstantResolutionItem job{nesting_, recvAsConstantLit}; job.possibleGenericType = true; - if (!resolveJob(ctx, job)) { + if (!resolveConstantJob(ctx, job)) { todo_.emplace_back(std::move(job)); } } @@ -1699,6 +1701,29 @@ class ResolveConstantsWalk { return depth; } + static core::ClassOrModuleRef resolvePayloadSuperclassClassName(const core::GlobalState &gs, + string_view className) { + if (className.empty()) { + return core::Symbols::noClassOrModule(); + } + + auto current = core::Symbols::root(); + for (string_view part : absl::StrSplit(className, "::")) { + auto partName = gs.lookupNameConstant(part); + if (!partName.exists()) { + return core::Symbols::noClassOrModule(); + } + + auto next = current.data(gs)->findMember(gs, partName); + if (!next.exists() || !next.isClassOrModule()) { + return core::Symbols::noClassOrModule(); + } + current = next.asClassOrModuleRef(); + } + + return current; + } + struct ResolveWalkResult { vector> todo_; vector> todoAncestors_; @@ -1711,6 +1736,23 @@ class ResolveConstantsWalk { static vector resolveConstants(core::GlobalState &gs, vector trees, WorkerPool &workers) { + UnorderedSet suppressPayloadSuperclassRedefinitionFor; + for (string_view className : gs.suppressPayloadSuperclassRedefinitionFor) { + auto klass = resolvePayloadSuperclassClassName(gs, className); + if (!klass.exists()) { + if (auto e = gs.beginError(core::Loc::none(), core::errors::Resolver::StubConstant)) { + e.setHeader("Unable to resolve constant `{}` provided via `{}`", className, + "--suppress-payload-superclass-redefinition-for"); + e.addErrorNote("Double check that the provided class name exists, or delete this option\n" + " (which will be either from the command line args or from the `{}` file).", + "sorbet/config"); + } + continue; + } + + suppressPayloadSuperclassRedefinitionFor.emplace(klass); + } + Timer timeit(gs.tracer(), "resolver.resolve_constants"); const core::GlobalState &igs = gs; auto resultq = make_shared>(trees.size()); @@ -1820,11 +1862,12 @@ class ResolveConstantsWalk { // We try to resolve most ancestors second because this makes us much more likely to resolve // everything else. long retries = 0; - auto f = [&gs, &retries](ResolveItems &job) -> bool { + auto f = [&gs, &retries, &suppressPayloadSuperclassRedefinitionFor]( + ResolveItems &job) -> bool { core::MutableContext ctx(gs, core::Symbols::root(), job.file); const auto origSize = job.items.size(); auto g = [&](AncestorResolutionItem &item) -> bool { - auto resolved = resolveAncestorJob(ctx, item, false); + auto resolved = resolveAncestorJob(ctx, item, suppressPayloadSuperclassRedefinitionFor, false); return resolved; }; auto fileIt = remove_if(job.items.begin(), job.items.end(), std::move(g)); @@ -1979,9 +2022,9 @@ class ResolveConstantsWalk { for (auto &job : todoAncestors) { core::MutableContext ctx(gs, core::Symbols::root(), job.file); for (auto &item : job.items) { - auto resolved = resolveAncestorJob(ctx, item, true); + auto resolved = resolveAncestorJob(ctx, item, suppressPayloadSuperclassRedefinitionFor, true); if (!resolved) { - resolved = resolveAncestorJob(ctx, item, true); + resolved = resolveAncestorJob(ctx, item, suppressPayloadSuperclassRedefinitionFor, true); ENFORCE(resolved); } } @@ -2047,7 +2090,8 @@ class ResolveTypeMembersAndFieldsWalk { struct ResolveMethodAliasItem { core::FileRef file; core::ClassOrModuleRef owner; - core::LocOffsets loc; + core::LocOffsets funLoc; + core::LocOffsets fromNameLoc; core::LocOffsets toNameLoc; // FIXME[alias-support]: Add a fromNameLoc field here core::NameRef toName; @@ -2166,11 +2210,11 @@ class ResolveTypeMembersAndFieldsWalk { } auto allowSelfType = true; auto allowRebind = false; - auto allowTypeMember = true; + auto typeMember = TypeSyntaxArgs::TypeMember::Allowed; auto allowUnspecifiedTypeParameter = !lastTry; auto ctx = core::Context(gs, job.owner.enclosingClass(gs), job.file); auto type = TypeSyntax::getResultType(ctx, job.cast->typeExpr, emptySig, - TypeSyntaxArgs{allowSelfType, allowRebind, allowTypeMember, + TypeSyntaxArgs{allowSelfType, allowRebind, typeMember, allowUnspecifiedTypeParameter, core::Symbols::noSymbol()}); if (type == core::Types::todo()) { return false; @@ -2217,8 +2261,10 @@ class ResolveTypeMembersAndFieldsWalk { // class or body, rather then nested in some block if (job.atTopLevel && ctx.owner.isClassOrModule()) { // Declaring a class instance variable - } else if (job.atTopLevel && ctx.owner.name(ctx) == core::Names::initialize()) { - // Declaring a instance variable + } else if ((ctx.owner.name(ctx) == core::Names::initialize() && job.atTopLevel) || + // allow test_each(...) { before { @x = ... } } + (ctx.owner.name(ctx) == core::Names::beforeAngles())) { + // Declaring a instance variable in either `initialize` or a `before do` block. } else if (ctx.owner.isMethod() && ctx.owner.asMethodRef().data(ctx)->owner.data(ctx)->isSingletonClass(ctx) && !core::Types::isSubType(ctx, core::Types::nilClass(), castType)) { @@ -2246,30 +2292,41 @@ class ResolveTypeMembersAndFieldsWalk { // This was previously entered by namer and we are now resolving the type. priorField.data(ctx)->resultType = castType; return; - } else if (core::Types::equiv(ctx, priorField.data(ctx)->resultType, castType)) { + } + + auto priorFieldResultType = priorField.data(ctx)->resultType; + if (priorFieldResultType == nullptr || castType == nullptr) [[unlikely]] { + const auto &file = ctx.file.data(ctx); + fatalLogger->error( + R"(msg="Bad core::Types::equiv in resolveField" path="{}" priorField="{}" resultTypeNull="{}" castTypeNull="{}")", + file.path(), priorField.show(ctx), priorFieldResultType == nullptr ? "true" : "false", + castType == nullptr ? "true" : "false"); + fatalLogger->error("source=\"{}\"", absl::CEscape(file.source())); + } + + if (core::Types::equiv(ctx, priorFieldResultType, castType)) { // We already have a symbol for this field, and it matches what we already saw, so we can short // circuit. return; + } + + // We do some normalization here to ensure that the file / line we report the error on doesn't + // depend on the order that we traverse files nor the order we traverse within a file. + auto priorLoc = priorField.data(ctx)->loc(); + core::Loc reportOn; + core::Loc errorLine; + core::Loc thisLoc = core::Loc(job.file, uid->loc); + if (thisLoc.file() == priorLoc.file()) { + reportOn = thisLoc.beginPos() < priorLoc.beginPos() ? thisLoc : priorLoc; + errorLine = thisLoc.beginPos() < priorLoc.beginPos() ? priorLoc : thisLoc; } else { - // We do some normalization here to ensure that the file / line we report the error on doesn't - // depend on the order that we traverse files nor the order we traverse within a file. - auto priorLoc = priorField.data(ctx)->loc(); - core::Loc reportOn; - core::Loc errorLine; - core::Loc thisLoc = core::Loc(job.file, uid->loc); - if (thisLoc.file() == priorLoc.file()) { - reportOn = thisLoc.beginPos() < priorLoc.beginPos() ? thisLoc : priorLoc; - errorLine = thisLoc.beginPos() < priorLoc.beginPos() ? priorLoc : thisLoc; - } else { - reportOn = thisLoc.file() < priorLoc.file() ? thisLoc : priorLoc; - errorLine = thisLoc.file() < priorLoc.file() ? priorLoc : thisLoc; - } + reportOn = thisLoc.file() < priorLoc.file() ? thisLoc : priorLoc; + errorLine = thisLoc.file() < priorLoc.file() ? priorLoc : thisLoc; + } - if (auto e = ctx.state.beginError(reportOn, core::errors::Resolver::DuplicateVariableDeclaration)) { - e.setHeader("Redeclaring variable `{}` with mismatching type", uid->name.show(ctx)); - e.addErrorLine(errorLine, "Previous declaration is here:"); - } - return; + if (auto e = ctx.state.beginError(reportOn, core::errors::Resolver::DuplicateVariableDeclaration)) { + e.setHeader("Redeclaring variable `{}` with mismatching type", uid->name.show(ctx)); + e.addErrorLine(errorLine, "Previous declaration is here:"); } } @@ -2396,12 +2453,11 @@ class ResolveTypeMembersAndFieldsWalk { ParsedSig emptySig; auto allowSelfType = true; auto allowRebind = false; - auto allowTypeMember = false; + auto typeMember = TypeSyntaxArgs::TypeMember::BannedInTypeMember; auto allowUnspecifiedTypeParameter = false; - core::TypePtr resTy = - TypeSyntax::getResultType(ctx, value, emptySig, - TypeSyntaxArgs{allowSelfType, allowRebind, allowTypeMember, - allowUnspecifiedTypeParameter, lhs}); + core::TypePtr resTy = TypeSyntax::getResultType( + ctx, value, emptySig, + TypeSyntaxArgs{allowSelfType, allowRebind, typeMember, allowUnspecifiedTypeParameter, lhs}); switch (key->asSymbol().rawId()) { case core::Names::fixed().rawId(): @@ -2428,7 +2484,8 @@ class ResolveTypeMembersAndFieldsWalk { // If the parent bounds exists, validate the new bounds against those of the parent. if (parentType != nullptr) { auto parentData = parentMember.data(ctx); - if (!core::Types::isSubType(ctx, parentType->lowerBound, memberType->lowerBound)) { + core::ErrorSection::Collector errorDetailsCollector; + if (!core::Types::isSubType(ctx, parentType->lowerBound, memberType->lowerBound, errorDetailsCollector)) { auto errLoc = lowerBoundTypeLoc.exists() ? lowerBoundTypeLoc : ctx.locAt(rhs->loc); if (auto e = ctx.state.beginError(errLoc, core::errors::Resolver::ParentTypeBoundsMismatch)) { e.setHeader("The `{}` type bound `{}` must be {} the parent's `{}` type bound `{}` " @@ -2439,6 +2496,8 @@ class ResolveTypeMembersAndFieldsWalk { rhs->fun.show(ctx), data->name.show(ctx)); e.addErrorLine(parentMember.data(ctx)->loc(), "`{}` defined in parent here", parentMember.show(ctx)); + core::TypeErrorDiagnostics::explainTypeMismatch(ctx, e, errorDetailsCollector, + parentType->lowerBound, memberType->lowerBound); if (lowerBoundTypeLoc.exists()) { auto replacementType = ctx.state.suggestUnsafe.has_value() ? core::Types::untypedUntracked() : parentType->lowerBound; @@ -2446,7 +2505,8 @@ class ResolveTypeMembersAndFieldsWalk { } } } - if (!core::Types::isSubType(ctx, memberType->upperBound, parentType->upperBound)) { + core::ErrorSection::Collector errorDetailsCollector2; + if (!core::Types::isSubType(ctx, memberType->upperBound, parentType->upperBound, errorDetailsCollector2)) { auto errLoc = upperBoundTypeLoc.exists() ? upperBoundTypeLoc : ctx.locAt(rhs->loc); if (auto e = ctx.state.beginError(errLoc, core::errors::Resolver::ParentTypeBoundsMismatch)) { e.setHeader("The `{}` type bound `{}` must be {} the parent's `{}` type bound `{}` " @@ -2457,6 +2517,8 @@ class ResolveTypeMembersAndFieldsWalk { rhs->fun.show(ctx), data->name.show(ctx)); e.addErrorLine(parentMember.data(ctx)->loc(), "`{}` defined in parent here", parentMember.show(ctx)); + core::TypeErrorDiagnostics::explainTypeMismatch(ctx, e, errorDetailsCollector2, + parentType->upperBound, memberType->upperBound); if (upperBoundTypeLoc.exists()) { auto replacementType = ctx.state.suggestUnsafe.has_value() ? core::Types::untypedUntracked() : parentType->upperBound; @@ -2468,10 +2530,13 @@ class ResolveTypeMembersAndFieldsWalk { // Ensure that the new lower bound is a subtype of the upper bound. // This will be a no-op in the case that the type member is fixed. - if (!core::Types::isSubType(ctx, memberType->lowerBound, memberType->upperBound)) { + core::ErrorSection::Collector errorDetailsCollector; + if (!core::Types::isSubType(ctx, memberType->lowerBound, memberType->upperBound, errorDetailsCollector)) { if (auto e = ctx.beginError(rhs->loc, core::errors::Resolver::InvalidTypeMemberBounds)) { e.setHeader("The `{}` type bound `{}` is not a subtype of the `{}` type bound `{}` for `{}`", "lower", memberType->lowerBound.show(ctx), "upper", memberType->upperBound.show(ctx), lhs.show(ctx)); + core::TypeErrorDiagnostics::explainTypeMismatch(ctx, e, errorDetailsCollector, memberType->upperBound, + memberType->lowerBound); } } @@ -2535,10 +2600,10 @@ class ResolveTypeMembersAndFieldsWalk { auto allowSelfType = true; auto allowRebind = false; - auto allowTypeMember = true; + auto typeMember = TypeSyntaxArgs::TypeMember::BannedInTypeAlias; auto allowUnspecifiedTypeParameter = false; lhs.setResultType(ctx, TypeSyntax::getResultType(ctx, block->body, ParsedSig{}, - TypeSyntaxArgs{allowSelfType, allowRebind, allowTypeMember, + TypeSyntaxArgs{allowSelfType, allowRebind, typeMember, allowUnspecifiedTypeParameter, lhs})); } @@ -2570,7 +2635,7 @@ class ResolveTypeMembersAndFieldsWalk { } static void resolveMethodAlias(core::MutableContext ctx, const ResolveMethodAliasItem &job) { - core::MethodRef toMethod = ctx.owner.asClassOrModuleRef().data(ctx)->findMethodNoDealias(ctx, job.toName); + core::MethodRef toMethod = ctx.owner.asClassOrModuleRef().data(ctx)->findMethodNoDealias(job.toName); if (toMethod.exists()) { toMethod = toMethod.data(ctx)->dealiasMethod(ctx); } @@ -2579,13 +2644,17 @@ class ResolveTypeMembersAndFieldsWalk { if (auto e = ctx.beginError(job.toNameLoc, core::errors::Resolver::BadAliasMethod)) { e.setHeader("Can't make method alias from `{}` to non existing method `{}`", job.fromName.show(ctx), job.toName.show(ctx)); + auto funLoc = ctx.locAt(job.funLoc); + if (ctx.state.suggestUnsafe && funLoc.exists() && funLoc.source(ctx) == "alias_method") { + e.replaceWith("Insert `T.unsafe(self).`", funLoc.copyWithZeroLength(), "T.unsafe(self)."); + } } toMethod = core::Symbols::Sorbet_Private_Static_badAliasMethodStub(); } - core::MethodRef fromMethod = ctx.owner.asClassOrModuleRef().data(ctx)->findMethodNoDealias(ctx, job.fromName); + core::MethodRef fromMethod = ctx.owner.asClassOrModuleRef().data(ctx)->findMethodNoDealias(job.fromName); if (fromMethod.exists() && fromMethod.data(ctx)->dealiasMethod(ctx) != toMethod) { - if (auto e = ctx.beginError(job.loc, core::errors::Resolver::BadAliasMethod)) { + if (auto e = ctx.beginError(job.fromNameLoc, core::errors::Resolver::BadAliasMethod)) { auto dealiased = fromMethod.data(ctx)->dealiasMethod(ctx); if (fromMethod == dealiased) { e.setHeader("Redefining the existing method `{}` as a method alias", fromMethod.show(ctx)); @@ -2611,8 +2680,9 @@ class ResolveTypeMembersAndFieldsWalk { return; } - // FIXME[alias-support]: Use job.fromNameLoc here for the last argument. - auto alias = ctx.state.enterMethodSymbol(ctx.locAt(job.loc), job.owner, job.fromName, ctx.locAt(job.loc)); + auto loc = ctx.locAt(job.fromNameLoc); + auto alias = ctx.state.enterMethodSymbol(loc, job.owner, job.fromName, loc); + alias.data(ctx)->addLoc(ctx, loc); alias.data(ctx)->resultType = core::make_type(core::SymbolRef(toMethod)); // Add a fake keyword argument to remember the toName (for fast path hashing). @@ -2716,36 +2786,6 @@ class ResolveTypeMembersAndFieldsWalk { return; } - core::TypePtr packageType = nullptr; - optional packageLoc; - if (send.hasKwArgs()) { - // this means we got the third package arg - auto *key = ast::cast_tree(send.getKwKey(0)); - if (!key || !key->isSymbol() || key->asSymbol() != ctx.state.lookupNameUTF8("package")) { - return; - } - - auto *packageNode = ast::cast_tree(send.getKwValue(0)); - packageLoc = std::optional{send.getKwValue(0).loc()}; - if (packageNode == nullptr) { - if (auto e = ctx.beginError(send.getKwValue(0).loc(), core::errors::Resolver::LazyResolve)) { - e.setHeader("`{}` only accepts string literals", method); - } - return; - } - - if (!core::isa_type(packageNode->value) || - core::cast_type_nonnull(packageNode->value).literalKind != - core::NamedLiteralType::LiteralTypeKind::String) { - // Infer will report a type error - return; - } - packageType = packageNode->value; - } - // if we got no keyword args, then package should be null, and - // if we got keyword args, then package should be non-null - ENFORCE((!send.hasKwArgs() && !packageType) || (send.hasKwArgs() && packageType)); - auto name = literal.asName(); auto shortName = name.shortName(ctx); if (shortName.empty()) { @@ -2759,42 +2799,6 @@ class ResolveTypeMembersAndFieldsWalk { // below, we'll check to find out whether the first part is `""` or not, which means we're testing whether // the string did or did not begin with `::`. vector parts = absl::StrSplit(shortName, "::"); - if (packageType) { - // there's a little bit of a complexity here: we want - // `non_forcing_is_a?("C::D", package: "A::B")` to be - // looking for, more or less, `A::B::C::D`. We want this - // _regardless_ of whether we're in packaged mode or not, - // in fact: in non-packaged mode, we should treat this as - // looking for `::A::B::C::D`, and in packaged mode, we're - // looking for `A::B::C::D` nested inside the desugared - // `PkgRegistry::Pkg_A_B` namespace. - if (parts.front() == "") { - if (auto e = ctx.beginError(stringLoc, core::errors::Resolver::LazyResolve)) { - e.setHeader("The string given to `{}` should not be an absolute constant reference if a " - "package name is also provided", - method); - } - return; - } - auto package = core::cast_type_nonnull(packageType); - auto name = package.asName().shortName(ctx); - vector pkgParts = absl::StrSplit(name, "::"); - // add the initial empty string to mimic the leading `::` - if (ctx.state.packageDB().empty()) { - pkgParts.insert(pkgParts.begin(), ""); - } - // and now add the rest of `parts` to it - pkgParts.insert(pkgParts.end(), parts.begin(), parts.end()); - // and then treat this new vector as the parts to walk over - parts = move(pkgParts); - // the path down below tries to find out if `packageType` - // is null to find out whether it should look up a package - // or not, so if we're not in package mode set - // `packageType` to null - if (ctx.state.packageDB().empty()) { - packageType = nullptr; - } - } core::SymbolRef current; for (auto part : parts) { @@ -2802,17 +2806,15 @@ class ResolveTypeMembersAndFieldsWalk { current = core::Symbols::root(); // First iteration - if (!packageType) { - if (part != "") { - if (auto e = ctx.beginError(stringLoc, core::errors::Resolver::LazyResolve)) { - e.setHeader("The string given to `{}` must be an absolute constant reference that " - "starts with `{}`", - method, "::"); - } - return; + if (part != "") { + if (auto e = ctx.beginError(stringLoc, core::errors::Resolver::LazyResolve)) { + e.setHeader("The string given to `{}` must be an absolute constant reference that " + "starts with `{}`", + method, "::"); } - continue; + return; } + continue; } auto member = ctx.state.lookupNameConstant(part); @@ -2999,8 +3001,15 @@ class ResolveTypeMembersAndFieldsWalk { // make sure that it's even possible to be valid. auto *cnst = ast::cast_tree(cast->typeExpr); ENFORCE(cnst != nullptr, "Rewriter should always use const for typeExpr, which should now be resolved"); - if (!cnst->symbol.isClassOrModule() || cnst->symbol.asClassOrModuleRef().data(ctx)->flags.isModule || - cnst->symbol.asClassOrModuleRef().data(ctx)->typeArity(ctx) > 0) { + + // Dealias to mimic how type_syntax parsing will work for constant lit node (e.g., want + // to allow class aliases). + auto dealiased = cnst->symbol.dealias(ctx); + + // Don't want the dealias to apply to the type alias, because MyTypeAlias.new is a runtime error. + if (cnst->symbol.isTypeAlias(ctx) || !dealiased.isClassOrModule() || + dealiased.asClassOrModuleRef().data(ctx)->flags.isModule || + dealiased.asClassOrModuleRef().data(ctx)->typeArity(ctx) > 0) { // The rewriter was over-eager in attempting to infer type `A` for `A.new` because // `A` was not a class (or was a generic class, and thus generated the wrong annotation). // Get rid of the cast, replace it with the original arg. @@ -3115,7 +3124,8 @@ class ResolveTypeMembersAndFieldsWalk { todoMethodAliasItems_.emplace_back(ResolveMethodAliasItem{ ctx.file, owner, - send.loc, + send.funLoc, + send.getPosArg(0).loc(), send.getPosArg(1).loc(), toName, fromName, @@ -3662,6 +3672,8 @@ class ResolveSignaturesWalk { } arg.type = std::move(spec->type); + // Passing in a noOp collector even though this call is used for error reporting, + // because it's unlikely we'll add more details to a subtype check for T.nilable(Proc) if (isBlkArg && !core::Types::isSubType(ctx, arg.type, core::Types::nilableProcClass())) { if (auto e = ctx.state.beginError(spec->nameLoc, core::errors::Resolver::InvalidMethodSignature)) { e.setHeader("Block argument type must be either `{}` or a `{}` type (and possibly nilable)", @@ -3815,7 +3827,7 @@ class ResolveSignaturesWalk { [&](ast::Send &send) { if (TypeSyntax::isSig(ctx, send)) { if (!lastSigs.empty()) { - if (!ctx.permitOverloadDefinitions(ctx.file)) { + if (!ctx.file.data(ctx).permitOverloadDefinitions()) { if (auto e = ctx.beginError(lastSigs[0]->loc, core::errors::Resolver::OverloadNotAllowed)) { e.setHeader("Unused type annotation. No method def before next annotation"); e.addErrorLine(ctx.locAt(send.loc), "Type annotation that will be used instead"); @@ -3840,14 +3852,14 @@ class ResolveSignaturesWalk { [&](ast::MethodDef &mdef) { if (debug_mode) { bool hasSig = !lastSigs.empty(); - bool rewriten = mdef.flags.isRewriterSynthesized; + bool rewritten = mdef.flags.isRewriterSynthesized; bool isRBI = ctx.file.data(ctx).isRBI(); if (hasSig) { categoryCounterInc("method.sig", "true"); } else { categoryCounterInc("method.sig", "false"); } - if (rewriten) { + if (rewritten) { categoryCounterInc("method.dsl", "true"); } else { categoryCounterInc("method.dsl", "false"); @@ -3857,7 +3869,7 @@ class ResolveSignaturesWalk { } else { categoryCounterInc("method.rbi", "false"); } - if (hasSig && !isRBI && !rewriten) { + if (hasSig && !isRBI && !rewritten) { counterInc("types.sig.human"); } } @@ -3891,7 +3903,7 @@ class ResolveSignaturesWalk { lastSig->loc, parseSig(ctx, sigOwner, *lastSig, mdef)}); } else { - bool isOverloaded = ctx.permitOverloadDefinitions(ctx.file); + bool isOverloaded = ctx.file.data(ctx).permitOverloadDefinitions(); InlinedVector sigs; for (auto &lastSig : lastSigs) { auto sig = parseSig(ctx, sigOwner, *lastSig, mdef); @@ -3985,8 +3997,10 @@ class ResolveSignaturesWalk { i++; core::MethodRef overloadSym; if (isOverloaded) { - overloadSym = - ctx.state.enterNewMethodOverload(ctx.locAt(sig.loc), mdef.symbol, originalName, i, sig.argsToKeep); + auto loc = ctx.locAt(sig.loc); + overloadSym = ctx.state.enterNewMethodOverload(loc, mdef.symbol, originalName, i, sig.argsToKeep); + overloadSym.data(ctx)->addLoc(ctx, loc); + overloadSym.data(ctx)->setMethodVisibility(mdef.symbol.data(ctx)->methodVisibility()); overloadSym.data(ctx)->intrinsicOffset = mdef.symbol.data(ctx)->intrinsicOffset; if (i != sigs.size() - 1) { @@ -4089,8 +4103,8 @@ class ResolveSignaturesWalk { } auto self = ast::MK::Self(mdef.loc); - mdef.rhs = ast::MK::Send(mdef.loc, std::move(self), core::Names::super(), mdef.loc.copyWithZeroLength(), - numPosArgs, std::move(args)); + mdef.rhs = ast::MK::Send(mdef.loc, std::move(self), core::Names::untypedSuper(), + mdef.loc.copyWithZeroLength(), numPosArgs, std::move(args)); } else if (mdef.symbol.enclosingClass(ctx).data(ctx)->flags.isInterface) { if (auto e = ctx.beginError(mdef.loc, core::errors::Resolver::ConcreteMethodInInterface)) { e.setHeader("All methods in an interface must be declared abstract"); @@ -4358,9 +4372,8 @@ ast::ParsedFilesOrCancelled Resolver::run(core::GlobalState &gs, vector trees, - bool ranIncrementalNamer) { - auto workers = WorkerPool::create(0, gs.tracer()); - trees = ResolveConstantsWalk::resolveConstants(gs, std::move(trees), *workers); + bool ranIncrementalNamer, WorkerPool &workers) { + trees = ResolveConstantsWalk::resolveConstants(gs, std::move(trees), workers); // NOTE: Linearization does not need to be recomputed as we do not mutate mixins() during incremental resolve. verifyLinearizationComputed(gs); // (verifyLinearizationComputed vs finalizeAncestors is currently the only difference between @@ -4373,8 +4386,8 @@ ast::ParsedFilesOrCancelled Resolver::runIncremental(core::GlobalState &gs, vect if (ranIncrementalNamer) { Resolver::finalizeSymbols(gs); } - auto rtmafResult = ResolveTypeMembersAndFieldsWalk::run(gs, std::move(trees), *workers); - auto result = resolveSigs(gs, std::move(rtmafResult.trees), *workers); + auto rtmafResult = ResolveTypeMembersAndFieldsWalk::run(gs, std::move(trees), workers); + auto result = resolveSigs(gs, std::move(rtmafResult.trees), workers); ResolveTypeMembersAndFieldsWalk::resolvePendingCastItems(gs, rtmafResult.todoResolveCastItems); sanityCheck(gs, result); // This check is FAR too slow to run on large codebases, especially with sanitizers on. diff --git a/resolver/resolver.h b/resolver/resolver.h index 1a8fcdb2e6..e83434b2cb 100644 --- a/resolver/resolver.h +++ b/resolver/resolver.h @@ -22,7 +22,7 @@ class Resolver final { * These two versions are explicitly instantiated in resolver.cc */ static ast::ParsedFilesOrCancelled runIncremental(core::GlobalState &gs, std::vector trees, - bool ranIncrementalNamer); + bool ranIncrementalNamer, WorkerPool &workers); // used by autogen only static std::vector runConstantResolution(core::GlobalState &gs, std::vector trees, diff --git a/resolver/type_syntax/type_syntax.cc b/resolver/type_syntax/type_syntax.cc index c2c108f5ae..3ed88daab0 100644 --- a/resolver/type_syntax/type_syntax.cc +++ b/resolver/type_syntax/type_syntax.cc @@ -31,7 +31,7 @@ ParsedSig TypeSyntax::parseSigTop(core::Context ctx, const ast::Send &sigSend, c auto args = TypeSyntaxArgs{ /* allowSelfType */ true, /* allowRebind */ false, - /* allowTypeMember */ true, + TypeSyntaxArgs::TypeMember::Allowed, /* allowUnspecifiedTypeParameter */ false, blameSymbol, }; @@ -568,7 +568,7 @@ optional parseSigWithSelfTypeParams(core::Context ctx, const ast::Sen return sig; } -// This function recurses through an OrType, and accumlates all the class names, +// This function recurses through an OrType, and accumulates all the class names, // wrapped in T.class_of, and checks if the type is only made up of Classes and OrTypes bool recurseOrType(core::Context ctx, core::TypePtr type, std::vector &v) { if (auto *o = core::cast_type(type)) { @@ -650,6 +650,16 @@ core::ClassOrModuleRef sendLooksLikeBadTypeApplication(core::Context ctx, const return klass; } +void maybeSuggestTClass(core::Context ctx, core::ErrorBuilder &e, core::LocOffsets sendLoc, core::LocOffsets argLoc) { + e.addErrorNote("You may wish to use `{}`, which doesn't have this restriction.\n" + " For more information, see https://sorbet.org/docs/class-of#tclass-vs-tclass_of", + "T::Class"); + if (sendLoc.exists() && !sendLoc.empty() && argLoc.exists() && !argLoc.empty()) { + e.replaceWith("Use `T::Class` instead", ctx.locAt(sendLoc), "T::Class[{}]", + ctx.locAt(argLoc).source(ctx).value()); + } +} + optional parseTClassOf(core::Context ctx, const ast::Send &send, const ParsedSig &sig, TypeSyntaxArgs args) { if (send.numPosArgs() != 1 || send.hasKwArgs()) { @@ -673,6 +683,7 @@ optional parseTClassOf(core::Context ctx, const ast::Sen e.replaceWith("Distribute `T.class_of`", ctx.locAt(send.loc), "{}", autocorrect); } else { e.setHeader("`{}` needs a class or module as its argument", "T.class_of"); + maybeSuggestTClass(ctx, e, send.loc, send.getPosArg(0).loc()); } } return core::Symbols::untyped(); @@ -681,12 +692,14 @@ optional parseTClassOf(core::Context ctx, const ast::Sen if (maybeAliased.isTypeAlias(ctx)) { if (auto e = ctx.beginError(send.loc, core::errors::Resolver::InvalidTypeDeclaration)) { e.setHeader("T.class_of can't be used with a T.type_alias"); + maybeSuggestTClass(ctx, e, send.loc, obj->loc); } return core::Symbols::untyped(); } if (maybeAliased.isTypeMember()) { if (auto e = ctx.beginError(send.loc, core::errors::Resolver::InvalidTypeDeclaration)) { e.setHeader("T.class_of can't be used with a T.type_member"); + maybeSuggestTClass(ctx, e, send.loc, obj->loc); } return core::Symbols::untyped(); } @@ -694,6 +707,7 @@ optional parseTClassOf(core::Context ctx, const ast::Sen if (sym.isStaticField(ctx)) { if (auto e = ctx.beginError(send.loc, core::errors::Resolver::InvalidTypeDeclaration)) { e.setHeader("T.class_of can't be used with a constant field"); + maybeSuggestTClass(ctx, e, send.loc, obj->loc); } return core::Symbols::untyped(); } @@ -1110,72 +1124,90 @@ optional getResultTypeAndBindWithSelfTypeParamsImpl(core bool isTypeTemplate = symOwner->isSingletonClass(ctx); - if (args.allowTypeMember) { - bool ctxIsSingleton = ctxOwnerData->isSingletonClass(ctx); - - // Check if we're processing a type within the class that - // defines this type member by comparing the singleton class of - // the context, and the singleton class of the type member's - // owner. - core::SymbolRef symOwnerSingleton = - isTypeTemplate ? symData->owner : symOwner->lookupSingletonClass(ctx); - core::SymbolRef ctxSingleton = ctxIsSingleton ? ctx.owner : ctxOwnerData->lookupSingletonClass(ctx); - bool usedOnSourceClass = symOwnerSingleton == ctxSingleton; - - // For this to be a valid use of a member or template type, this - // must: - // - // 1. be used in the context of the class that defines it - // 2. if it's a type_template type, be used in a singleton - // method - // 3. if it's a type_member type, be used in an instance method - if (usedOnSourceClass && ((isTypeTemplate && ctxIsSingleton) || !(isTypeTemplate || ctxIsSingleton))) { - // At this point, we maake a skolemized variable that will be unwrapped at the end of type - // parsing using Types::unwrapSkolemVariables. The justification for this is that type - // constructors like `Types::any` do not expect to see bound variables, and will panic. - result.type = core::make_type(sym); - } else { - if (auto e = ctx.beginError(i.loc, core::errors::Resolver::TypeMemberScopeMismatch)) { - string typeSource = isTypeTemplate ? "type_template" : "type_member"; - string typeStr = usedOnSourceClass ? symData->name.show(ctx) : sym.show(ctx); - - if (usedOnSourceClass) { - // Autocorrects here are awkward, because we want to offer the autocorrect at the - // definition of the type_member or type_template and we only have access to the use of - // the type_member or type_template here. We could examine the source and attempt to - // identify the location for the autocorrect, but that gets messy. - // - // Plus, it's not absolutely clear that the definition is really at fault: it might be - // that the user is using the wrong type_member/type_template constant for the given - // context, or they need to change the definition of the method this use is associated - // with. Try to give them enough of a hint to decide what to do on their own. - if (ctxIsSingleton) { - e.setHeader("`{}` type `{}` used in a singleton method definition", typeSource, - typeStr); - e.addErrorLine(symData->loc(), "`{}` defined here", typeStr); - e.addErrorNote("Only a `{}` can be used in a singleton method definition.", - "type_template"); + switch (args.typeMember) { + case TypeSyntaxArgs::TypeMember::Allowed: { + bool ctxIsSingleton = ctxOwnerData->isSingletonClass(ctx); + + // Check if we're processing a type within the class that + // defines this type member by comparing the singleton class of + // the context, and the singleton class of the type member's + // owner. + core::SymbolRef symOwnerSingleton = + isTypeTemplate ? symData->owner : symOwner->lookupSingletonClass(ctx); + core::SymbolRef ctxSingleton = ctxIsSingleton ? ctx.owner : ctxOwnerData->lookupSingletonClass(ctx); + bool usedOnSourceClass = symOwnerSingleton == ctxSingleton; + + // For this to be a valid use of a member or template type, this + // must: + // + // 1. be used in the context of the class that defines it + // 2. if it's a type_template type, be used in a singleton + // method + // 3. if it's a type_member type, be used in an instance method + if (usedOnSourceClass && + ((isTypeTemplate && ctxIsSingleton) || !(isTypeTemplate || ctxIsSingleton))) { + // At this point, we make a skolemized variable that will be unwrapped at the end of type + // parsing using Types::unwrapSkolemVariables. The justification for this is that type + // constructors like `Types::any` do not expect to see bound variables, and will panic. + result.type = core::make_type(sym); + } else { + if (auto e = ctx.beginError(i.loc, core::errors::Resolver::TypeMemberScopeMismatch)) { + string typeSource = isTypeTemplate ? "type_template" : "type_member"; + string typeStr = usedOnSourceClass ? symData->name.show(ctx) : sym.show(ctx); + + if (usedOnSourceClass) { + // Autocorrects here are awkward, because we want to offer the autocorrect at the + // definition of the type_member or type_template and we only have access to the use of + // the type_member or type_template here. We could examine the source and attempt to + // identify the location for the autocorrect, but that gets messy. + // + // Plus, it's not absolutely clear that the definition is really at fault: it might be + // that the user is using the wrong type_member/type_template constant for the given + // context, or they need to change the definition of the method this use is associated + // with. Try to give them enough of a hint to decide what to do on their own. + if (ctxIsSingleton) { + e.setHeader("`{}` type `{}` used in a singleton method definition", typeSource, + typeStr); + e.addErrorLine(symData->loc(), "`{}` defined here", typeStr); + e.addErrorNote("Only a `{}` can be used in a singleton method definition.", + "type_template"); + } else { + e.setHeader("`{}` type `{}` used in an instance method definition", typeSource, + typeStr); + e.addErrorLine(symData->loc(), "`{}` defined here", typeStr); + e.addErrorNote("Only a `{}` can be used in an instance method definition.", + "type_member"); + } } else { - e.setHeader("`{}` type `{}` used in an instance method definition", typeSource, - typeStr); - e.addErrorLine(symData->loc(), "`{}` defined here", typeStr); - e.addErrorNote("Only a `{}` can be used in an instance method definition.", - "type_member"); + e.setHeader("`{}` type `{}` used outside of the class definition", typeSource, typeStr); + e.addErrorLine(symData->loc(), "{} defined here", typeStr); } - } else { - e.setHeader("`{}` type `{}` used outside of the class definition", typeSource, typeStr); - e.addErrorLine(symData->loc(), "{} defined here", typeStr); } + result.type = core::Types::untypedUntracked(); } - result.type = core::Types::untypedUntracked(); - } - } else { - // a type member has occurred in a context that doesn't allow them - if (auto e = ctx.beginError(i.loc, core::errors::Resolver::InvalidTypeDeclaration)) { - auto flavor = isTypeTemplate ? "type_template"sv : "type_member"sv; - e.setHeader("`{}` `{}` is not allowed in this context", flavor, sym.show(ctx)); + break; } - result.type = core::Types::untypedUntracked(); + case TypeSyntaxArgs::TypeMember::BannedInTypeAlias: + if (auto e = ctx.beginError(i.loc, core::errors::Resolver::TypeAliasToTypeMember)) { + const auto &owner = tm.data(ctx)->owner.asClassOrModuleRef().data(ctx); + auto memTem = owner->attachedClass(ctx).exists() ? "type_template" : "type_member"; + e.setHeader("Defining a `{}` to a generic `{}` is not allowed", "type_alias", memTem); + e.addErrorLine(tm.data(ctx)->loc(), "`{}` defined as a `{}` here", tm.data(ctx)->name.show(ctx), + memTem); + e.addErrorNote("Type aliases to type members and type templates are not allowed: aliases\n" + " can be referenced from both instance and singleton class methods\n" + " whereas type members can only be referenced from one or the other."); + } + result.type = core::Types::untypedUntracked(); + break; + case TypeSyntaxArgs::TypeMember::BannedInTypeMember: + // a type member has occurred in a context that doesn't allow them + if (auto e = ctx.beginError(i.loc, core::errors::Resolver::InvalidTypeDeclaration)) { + auto flavor = isTypeTemplate ? "type_template"sv : "type_member"sv; + e.setHeader("`{}` `{}` is not allowed in this context", flavor, sym.show(ctx)); + } + result.type = core::Types::untypedUntracked(); + break; } } else if (sym.isStaticField(ctx)) { if (auto e = ctx.beginError(i.loc, core::errors::Resolver::InvalidTypeDeclaration)) { diff --git a/resolver/type_syntax/type_syntax.h b/resolver/type_syntax/type_syntax.h index 69708a9e65..c8d7049fe2 100644 --- a/resolver/type_syntax/type_syntax.h +++ b/resolver/type_syntax/type_syntax.h @@ -62,26 +62,32 @@ struct ParsedSig { }; struct TypeSyntaxArgs { - const bool allowSelfType = false; - const bool allowRebind = false; - const bool allowTypeMember = false; - const bool allowUnspecifiedTypeParameter = false; + const bool allowSelfType; + const bool allowRebind; + enum class TypeMember { + Allowed, + BannedInTypeMember, + BannedInTypeAlias, + }; + const TypeMember typeMember; + const bool allowUnspecifiedTypeParameter; const core::SymbolRef untypedBlame; + TypeSyntaxArgs(bool allowSelfType, bool allowRebind, TypeMember typeMember, bool allowUnspecifiedTypeParameter, + core::SymbolRef untypedBlame) + : allowSelfType(allowSelfType), allowRebind(allowRebind), typeMember(typeMember), + allowUnspecifiedTypeParameter(allowUnspecifiedTypeParameter), untypedBlame(untypedBlame) {} + TypeSyntaxArgs withoutRebind() const { - return TypeSyntaxArgs{allowSelfType, false, allowTypeMember, allowUnspecifiedTypeParameter, untypedBlame}; + return TypeSyntaxArgs{allowSelfType, false, typeMember, allowUnspecifiedTypeParameter, untypedBlame}; } TypeSyntaxArgs withRebind() const { - return TypeSyntaxArgs{allowSelfType, true, allowTypeMember, allowUnspecifiedTypeParameter, untypedBlame}; + return TypeSyntaxArgs{allowSelfType, true, typeMember, allowUnspecifiedTypeParameter, untypedBlame}; } TypeSyntaxArgs withoutSelfType() const { - return TypeSyntaxArgs{false, allowRebind, allowTypeMember, allowUnspecifiedTypeParameter, untypedBlame}; - } - - TypeSyntaxArgs withoutTypeMember() const { - return TypeSyntaxArgs{allowSelfType, allowRebind, false, allowUnspecifiedTypeParameter, untypedBlame}; + return TypeSyntaxArgs{false, allowRebind, typeMember, allowUnspecifiedTypeParameter, untypedBlame}; } }; diff --git a/rewriter/ClassNew.cc b/rewriter/ClassNew.cc index bcef28100b..f00103bf7d 100644 --- a/rewriter/ClassNew.cc +++ b/rewriter/ClassNew.cc @@ -24,7 +24,7 @@ vector ClassNew::run(core::MutableContext ctx, ast::Assign * vector empty; if (ctx.state.runningUnderAutogen) { - // This is not safe to run under autogen, because we'd be outputing + // This is not safe to run under autogen, because we'd be outputting // autoloader files that predeclare the class and cause "warning: // already initialized constant" errors return empty; diff --git a/rewriter/ClassNew.h b/rewriter/ClassNew.h index a6b8c3297c..4801661727 100644 --- a/rewriter/ClassNew.h +++ b/rewriter/ClassNew.h @@ -5,7 +5,7 @@ namespace sorbet::rewriter { /** - * This class actually contains three different rewriters depending on the kind of `Class.new` we ecounter: + * This class actually contains three different rewriters depending on the kind of `Class.new` we encounter: * * Assignments to a constant literal such as * diff --git a/rewriter/Command.cc b/rewriter/Command.cc index a3298fbfb2..79d66019f0 100644 --- a/rewriter/Command.cc +++ b/rewriter/Command.cc @@ -41,7 +41,8 @@ void Command::run(core::MutableContext ctx, ast::ClassDef *klass) { } int i = 0; - ast::MethodDef *call; + ast::MethodDef *call = nullptr; + ast::ExpressionPtr *callptr = nullptr; for (auto &stat : klass->rhs) { auto *mdef = ast::cast_tree(stat); @@ -54,6 +55,7 @@ void Command::run(core::MutableContext ctx, ast::ClassDef *klass) { i = &stat - &klass->rhs.front(); call = mdef; + callptr = &stat; break; } // If we didn't find a `call` method, or if it was the first statement (and @@ -84,10 +86,23 @@ void Command::run(core::MutableContext ctx, ast::ClassDef *klass) { ast::MethodDef::Flags flags; flags.isSelfMethod = true; flags.discardDef = true; - auto selfCall = ast::MK::SyntheticMethod(call->loc, call->loc, core::LocOffsets::none(), call->name, - std::move(newArgs), + auto selfCall = ast::MK::SyntheticMethod(call->loc, call->declLoc, core::LocOffsets::none(), call->name, std::move(newArgs), ast::MK::RaiseTypedUnimplemented(call->declLoc), flags); + // We are now in the weird situation where we have an actual method that + // the user has written, but we have a synthetic method that lives at the + // same location. If we try to find all references from the actual + // method, there are no calls to it, which will frustrate the user. Erase + // the location(s) on the non-synthetic method so that LSP only sees the + // synthetic method. + auto hiddenCall = ast::MK::Method(call->loc.copyWithZeroLength(), call->declLoc.copyWithZeroLength(), + call->nameLoc.copyWithZeroLength(), call->name, std::move(call->args), + std::move(call->rhs), call->flags); + + // We need to make sure we assign into `callptr` prior to inserting into + // `klass->rhs`, otherwise our pointer might not be live anymore. + *callptr = std::move(hiddenCall); + klass->rhs.insert(klass->rhs.begin() + i + 1, sig->deepCopy()); klass->rhs.insert(klass->rhs.begin() + i + 2, std::move(selfCall)); } diff --git a/rewriter/Flatten.cc b/rewriter/Flatten.cc index f9297cf439..fb55c448e1 100644 --- a/rewriter/Flatten.cc +++ b/rewriter/Flatten.cc @@ -313,7 +313,7 @@ class FlattenWalk { if (curMethodSet().stack.empty()) { return; } - // if this class is dirrectly nested inside a method, we want to steal it + // if this class is directly nested inside a method, we want to steal it auto md = methods.popScope(); ENFORCE(md); diff --git a/rewriter/Initializer.cc b/rewriter/Initializer.cc index 7e451a2436..cdf58b9c0c 100644 --- a/rewriter/Initializer.cc +++ b/rewriter/Initializer.cc @@ -124,13 +124,7 @@ const ast::Send *findParams(const ast::Send *send) { // this function checks if the signature of the initialize method is using returns(Something) // instead of void and provides an auto-correct option void checkSigReturnType(core::MutableContext ctx, const ast::Send *send) { - auto originalSendLoc = send->loc; - core::NameRef funAfterReturns; - - // try to find the invocation to returns. Save the source code of the invocation - // immediately after returns() so that we can have the exact length it occupies while (send && send->fun != core::Names::returns()) { - funAfterReturns = send->fun; send = ast::cast_tree(send->recv); } @@ -140,46 +134,13 @@ void checkSigReturnType(core::MutableContext ctx, const ast::Send *send) { return; } - if (auto e = ctx.beginError(originalSendLoc, core::errors::Rewriter::InitializeReturnType)) { + auto errLoc = (send->funLoc.exists() && !send->funLoc.empty()) ? send->funLoc : send->loc; + if (auto e = ctx.beginError(errLoc, core::errors::Rewriter::InitializeReturnType)) { e.setHeader("The {} method should always return {}", "initialize", "void"); - - auto loc = core::Loc(ctx.file, originalSendLoc); - auto original = string(loc.source(ctx).value()); - unsigned long returnsStart = original.find("returns"); - unsigned long returnsLength, afterReturnsPosition; - string replacement; - string statementAfterReturns = ""; - if (funAfterReturns.exists()) { - statementAfterReturns = funAfterReturns.toString(ctx); - } - - // If there are no statements after returns(), we can use the length of the block to find the length - // we need to replace. If there are statements after it, we need to find the exact length using the next - // statement and remember to add a dot or else it will produce invalid code - returnsLength = original.length() - returnsStart + 1; - - if (statementAfterReturns.empty()) { - replacement = original.replace(returnsStart, returnsLength, "void"); - } else { - afterReturnsPosition = original.find(statementAfterReturns, returnsStart); - - // If there is a line break between returns() and the next statement, change the returns() entry and - // re-join the string with the line breaks. Otherwise, everything is on the same line and we can replace - // directly without worrying about line breaks - vector lines = absl::StrSplit(original.substr(returnsStart, afterReturnsPosition), "\n"); - - if (lines.size() > 1) { - lines[0] = "void"; - replacement = original.replace(returnsStart, returnsLength, - fmt::format("{}", fmt::join(lines.begin(), lines.end(), "\n"))); - } else { - returnsLength = original.find(statementAfterReturns, returnsStart) - returnsStart; - replacement = original.replace(returnsStart, returnsLength, "void."); - } + auto replacementLoc = core::Loc(ctx.file, send->funLoc.beginPos(), send->loc.endPos()); + if (replacementLoc.exists() && !replacementLoc.empty()) { + e.replaceWith("Replace `.returns` with `.void`", replacementLoc, "void"); } - - e.addAutocorrect(core::AutocorrectSuggestion{fmt::format("Replace `{}` with `{}`", original, replacement), - {core::AutocorrectSuggestion::Edit{loc, replacement}}}); } } @@ -229,7 +190,7 @@ void Initializer::run(core::MutableContext ctx, ast::MethodDef *methodDef, ast:: const auto *restArg = ast::cast_tree(arg); if (restArg == nullptr) { argKindMap[ast::MK::arg2Name(arg)] = ArgKind::Plain; - } else if (const auto *kwRestArg = ast::cast_tree(restArg->expr)) { + } else if (ast::isa_tree(restArg->expr)) { argKindMap[ast::MK::arg2Name(arg)] = ArgKind::KeywordRestArg; } else { ENFORCE(ast::isa_tree(restArg->expr)); diff --git a/rewriter/Initializer.h b/rewriter/Initializer.h index b414b11b2c..0a0277f8a6 100644 --- a/rewriter/Initializer.h +++ b/rewriter/Initializer.h @@ -21,7 +21,7 @@ namespace sorbet::rewriter { * which allows us to get some instance variable types 'for free' even before we have started inference. This only * applies to methods named `initialize` with `sig`s of the appropriate shape, and to types that we can currently copy * (i.e. we skip `T.type_parameter` types), and can be easily bypassed by having something other than just a single - * local paramter on its on on the right-hand side. e.g. even something like this + * local parameter on its on on the right-hand side. e.g. even something like this * * sig {params(x: Integer).void} * def initialize(x) diff --git a/rewriter/InterfaceWrapper.cc b/rewriter/InterfaceWrapper.cc deleted file mode 100644 index 5faa971cf1..0000000000 --- a/rewriter/InterfaceWrapper.cc +++ /dev/null @@ -1,40 +0,0 @@ -#include "rewriter/InterfaceWrapper.h" -#include "ast/Helpers.h" -#include "ast/ast.h" -#include "core/Context.h" -#include "core/Names.h" -#include "core/core.h" -#include "core/errors/rewriter.h" -#include "rewriter/Util.h" -#include "rewriter/rewriter.h" - -using namespace std; - -namespace sorbet::rewriter { -ast::ExpressionPtr InterfaceWrapper::run(core::MutableContext ctx, ast::Send *send) { - if (ctx.state.runningUnderAutogen) { - return nullptr; - } - - if (send->fun != core::Names::wrapInstance()) { - return nullptr; - } - - if (!ast::isa_tree(send->recv)) { - if (auto e = ctx.beginError(send->recv.loc(), core::errors::Rewriter::BadWrapInstance)) { - e.setHeader("Unsupported wrap_instance() on a non-constant-literal"); - } - return nullptr; - } - - if (send->numPosArgs() != 1) { - if (auto e = ctx.beginError(send->loc, core::errors::Rewriter::BadWrapInstance)) { - e.setHeader("Wrong number of arguments to `{}`. Expected: `{}`, got: `{}`", "wrap_instance", 0, - send->numPosArgs()); - } - return nullptr; - } - - return ast::MK::Let(send->loc, move(send->getPosArg(0)), move(send->recv)); -} -} // namespace sorbet::rewriter diff --git a/rewriter/InterfaceWrapper.h b/rewriter/InterfaceWrapper.h deleted file mode 100644 index 6d46e3aae4..0000000000 --- a/rewriter/InterfaceWrapper.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef SORBET_REWRITER_INTERFACE_WRAPPER_H -#define SORBET_REWRITER_INTERFACE_WRAPPER_H -#include "ast/ast.h" - -namespace sorbet::rewriter { - -/** - * This class desugars `wrap_instance` into a simple cast: - * - * SomeClass.wrap_instance(obj) - * - * => - * - * T.let(obj, SomeClass) - * - * This suffices to: - * - * (a) check that `obj : SomeClass` - * (b) statically prevent calling any methods on `obj` not defined on `SomeClass` - * - * which mirrors the runtime behavior. - */ -class InterfaceWrapper final { -public: - static ast::ExpressionPtr run(core::MutableContext ctx, ast::Send *send); - - InterfaceWrapper() = delete; -}; - -} // namespace sorbet::rewriter - -#endif diff --git a/rewriter/Minitest.cc b/rewriter/Minitest.cc index 69a17ecf7a..102e71acd4 100644 --- a/rewriter/Minitest.cc +++ b/rewriter/Minitest.cc @@ -108,8 +108,20 @@ class ConstantMover { }; ast::ExpressionPtr addSigVoid(ast::ExpressionPtr expr) { - return ast::MK::InsSeq1(expr.loc(), ast::MK::SigVoid(expr.loc(), {}), std::move(expr)); + core::LocOffsets declLoc; + if (auto *mdef = ast::cast_tree(expr)) { + declLoc = mdef->declLoc; + } else { + ENFORCE(false, "Added a sig to something that wasn't a method def"); + declLoc = expr.loc(); + } + return ast::MK::InsSeq1(expr.loc(), ast::MK::SigVoid(declLoc, {}), std::move(expr)); } + +core::LocOffsets declLocForSendWithBlock(const ast::Send &send) { + return send.loc.copyWithZeroLength().join(send.block()->loc.copyWithZeroLength()); +} + } // namespace ast::ExpressionPtr recurse(core::MutableContext ctx, bool isClass, ast::ExpressionPtr body); @@ -182,20 +194,34 @@ ast::ExpressionPtr getIteratee(ast::ExpressionPtr &exp) { } } +ast::ExpressionPtr prepareTestEachBody(core::MutableContext ctx, core::NameRef eachName, ast::ExpressionPtr body, + ast::MethodDef::ARGS_store &args, ast::InsSeq::STATS_store destructuringStmts, + ast::ExpressionPtr &iteratee); + // this applies to each statement contained within a `test_each`: if it's an `it`-block, then convert it appropriately, // otherwise flag an error about it ast::ExpressionPtr runUnderEach(core::MutableContext ctx, core::NameRef eachName, - const ast::InsSeq::STATS_store &destructuringStmts, ast::ExpressionPtr stmt, + ast::InsSeq::STATS_store &destructuringStmts, ast::ExpressionPtr stmt, ast::MethodDef::ARGS_store &args, ast::ExpressionPtr &iteratee) { // this statement must be a send if (auto *send = ast::cast_tree(stmt)) { // the send must be a call to `it` with a single argument (the test name) and a block with no arguments - if (send->fun == core::Names::it() && send->numPosArgs() == 1 && send->hasBlock() && - send->block()->args.size() == 0) { - // we use this for the name of our test - auto &arg0 = send->getPosArg(0); - auto argString = to_s(ctx, arg0); - auto name = ctx.state.enterNameUTF8(""); + if ((send->fun == core::Names::it() && send->numPosArgs() == 1 && send->hasBlock() && + send->block()->args.size() == 0) || + ((send->fun == core::Names::before() || send->fun == core::Names::after()) && send->numPosArgs() == 0 && + send->hasBlock() && send->block()->args.size() == 0)) { + core::NameRef name; + auto arg0Loc = core::LocOffsets::none(); + if (send->fun == core::Names::before()) { + name = core::Names::beforeAngles(); + } else if (send->fun == core::Names::after()) { + name = core::Names::afterAngles(); + } else { + // we use this for the name of our test + arg0Loc = send->getPosArg(0).loc(); + auto argString = to_s(ctx, send->getPosArg(0)); + name = ctx.state.enterNameUTF8(""); + } // pull constants out of the block ConstantMover constantMover; @@ -217,20 +243,28 @@ ast::ExpressionPtr runUnderEach(core::MutableContext ctx, core::NameRef eachName body = ast::MK::InsSeq(body.loc(), std::move(stmts), std::move(body)); } - auto blk = ast::MK::Block(send->loc, move(body), std::move(new_args)); + auto blk = ast::MK::Block(send->block()->loc, move(body), std::move(new_args)); auto each = ast::MK::Send0Block(send->loc, iteratee.deepCopy(), core::Names::each(), send->loc.copyWithZeroLength(), move(blk)); // put that into a method def named the appropriate thing - auto method = - addSigVoid(ast::MK::SyntheticMethod0(send->loc, send->loc, arg0.loc(), move(name), move(each))); + auto declLoc = declLocForSendWithBlock(*send); + auto method = addSigVoid(ast::MK::SyntheticMethod0(send->loc, declLoc, arg0Loc, move(name), move(each))); // add back any moved constants return constantMover.addConstantsToExpression(send->loc, move(method)); + } else if (send->fun == core::Names::describe() && send->numPosArgs() == 1 && send->hasBlock() && + send->block()->args.size() == 0) { + return prepareTestEachBody(ctx, eachName, std::move(send->block()->body), args, + std::move(destructuringStmts), iteratee); } } // if any of the above tests were not satisfied, then mark this statement as being invalid here if (auto e = ctx.beginError(stmt.loc(), core::errors::Rewriter::BadTestEach)) { - e.setHeader("Only valid `{}`-blocks can appear within `{}`", "it", eachName.show(ctx)); + e.setHeader("Only valid `{}`, `{}`, `{}`, and `{}` blocks can appear within `{}`", "it", "before", "after", + "describe", eachName.show(ctx)); + e.addErrorNote("For other things, like constant and variable assignments," + " hoist them to constants or methods defined outside the `{}` block.", + eachName.show(ctx)); } return stmt; @@ -334,7 +368,7 @@ ast::ExpressionPtr runSingle(core::MutableContext ctx, bool isClass, ast::Send * auto iteratee = getIteratee(send->getPosArg(0)); // and then reconstruct the send but with a modified body return ast::MK::Send( - send->loc, ast::MK::Self(send->loc), send->fun, send->funLoc, 1, + send->loc, ast::MK::Self(send->recv.loc()), send->fun, send->funLoc, 1, ast::MK::SendArgs( move(send->getPosArg(0)), ast::MK::Block(block->loc, @@ -355,11 +389,12 @@ ast::ExpressionPtr runSingle(core::MutableContext ctx, bool isClass, ast::Send * } if (send->numPosArgs() == 0 && (send->fun == core::Names::before() || send->fun == core::Names::after())) { - auto name = send->fun == core::Names::after() ? core::Names::afterAngles() : core::Names::initialize(); + auto name = send->fun == core::Names::after() ? core::Names::afterAngles() : core::Names::beforeAngles(); ConstantMover constantMover; ast::TreeWalk::apply(ctx, constantMover, block->body); - auto method = addSigVoid(ast::MK::SyntheticMethod0(send->loc, send->loc, send->funLoc, name, - prepareBody(ctx, isClass, std::move(block->body)))); + auto declLoc = declLocForSendWithBlock(*send); + auto method = addSigVoid( + ast::MK::SyntheticMethod0(send->loc, declLoc, send->funLoc, name, prepareBody(ctx, isClass, std::move(block->body)))); return constantMover.addConstantsToExpression(send->loc, move(method)); } @@ -383,13 +418,15 @@ ast::ExpressionPtr runSingle(core::MutableContext ctx, bool isClass, ast::Send * rhs.emplace_back(prepareBody(ctx, bodyIsClass, std::move(block->body))); auto name = ast::MK::UnresolvedConstant(arg.loc(), ast::MK::EmptyTree(), ctx.state.enterNameConstant("")); - return ast::MK::Class(send->loc, send->loc, std::move(name), std::move(ancestors), std::move(rhs)); + auto declLoc = declLocForSendWithBlock(*send); + return ast::MK::Class(send->loc, declLoc, std::move(name), std::move(ancestors), std::move(rhs)); } else if (send->fun == core::Names::it()) { ConstantMover constantMover; ast::TreeWalk::apply(ctx, constantMover, block->body); auto name = ctx.state.enterNameUTF8(""); const bool bodyIsClass = false; - auto method = addSigVoid(ast::MK::SyntheticMethod0(send->loc, send->loc, arg.loc(), std::move(name), + auto declLoc = declLocForSendWithBlock(*send); + auto method = addSigVoid(ast::MK::SyntheticMethod0(send->loc, declLoc, arg.loc(), std::move(name), prepareBody(ctx, bodyIsClass, std::move(block->body)))); method = ast::MK::InsSeq1(send->loc, send->getPosArg(0).deepCopy(), move(method)); return constantMover.addConstantsToExpression(send->loc, move(method)); diff --git a/rewriter/ModuleFunction.cc b/rewriter/ModuleFunction.cc index 196ee9955d..95f4d26794 100644 --- a/rewriter/ModuleFunction.cc +++ b/rewriter/ModuleFunction.cc @@ -34,7 +34,7 @@ void ModuleFunction::run(core::MutableContext ctx, ast::ClassDef *cdef) { replaceNodes[stat.get()] = run(ctx, send, prevStat); } } - } else if (auto defn = ast::cast_tree(stat)) { + } else if (ast::isa_tree(stat)) { // if we've already seen a bare `module_function` call, then every subsequent method definition needs to get // rewritten appropriately if (moduleFunctionActive) { diff --git a/rewriter/ModuleFunction.h b/rewriter/ModuleFunction.h index e87d65cb5f..7d090ecfca 100644 --- a/rewriter/ModuleFunction.h +++ b/rewriter/ModuleFunction.h @@ -16,7 +16,7 @@ namespace sorbet::rewriter { * def self.foo(x, y, ...); end * * possibly replicating the sig for the method if possible. If it is used with a string or symbol, then it instead - * desugars into an untyped empty method for the purposes of fowarding, so + * desugars into an untyped empty method for the purposes of forwarding, so * * module function def :foo * diff --git a/rewriter/Prop.cc b/rewriter/Prop.cc index a820a4643d..7d5efc39bf 100644 --- a/rewriter/Prop.cc +++ b/rewriter/Prop.cc @@ -110,6 +110,7 @@ struct PropInfo { ast::ExpressionPtr default_; core::NameRef computedByMethodName; core::LocOffsets computedByMethodNameLoc; + ast::ExpressionPtr foreignKwLit; ast::ExpressionPtr foreign; ast::ExpressionPtr enum_; ast::ExpressionPtr ifunset; @@ -312,6 +313,7 @@ optional parseProp(core::MutableContext ctx, const ast::Send *send) { auto [fk, foreignTree] = ASTUtil::extractHashValue(ctx, *rules, core::Names::foreign()); if (foreignTree != nullptr) { ret.foreign = move(foreignTree); + ret.foreignKwLit = move(fk); if (auto body = ASTUtil::thunkBody(ctx, ret.foreign)) { ret.foreign = std::move(body); } else { @@ -334,6 +336,18 @@ optional parseProp(core::MutableContext ctx, const ast::Send *send) { if (ifunset != nullptr) { ret.ifunset = std::move(ifunset); } + + if (send->fun == core::Names::merchantTokenProp()) { + auto [_nameKey, nameValue] = ASTUtil::extractHashValue(ctx, *rules, core::Names::name()); + if (nameValue != nullptr) { + if (auto lit = ast::cast_tree(nameValue)) { + if (lit->isSymbol()) { + ret.name = lit->asSymbol(); + ret.nameLoc = nameValue.loc(); + } + } + } + } } if (ret.default_ == nullptr && isTNilable(ret.type)) { @@ -493,7 +507,15 @@ vector processProp(core::MutableContext ctx, PropInfo &ret, auto arg = ast::MK::KeywordArgWithDefault(nameLoc, core::Names::allowDirectMutation(), ast::MK::Nil(loc)); ast::MethodDef::Flags fkFlags; fkFlags.discardDef = true; - auto fkMethodDef = ast::MK::SyntheticMethod1(loc, loc, nameLoc, fkMethod, std::move(arg), + + core::LocOffsets methodLoc; + if (ret.foreignKwLit != nullptr) { + methodLoc = ret.foreignKwLit.loc(); + } else { + methodLoc = loc; + } + + auto fkMethodDef = ast::MK::SyntheticMethod1(loc, methodLoc, nameLoc, fkMethod, std::move(arg), ast::MK::RaiseTypedUnimplemented(loc), fkFlags); nodes.emplace_back(std::move(fkMethodDef)); @@ -588,7 +610,7 @@ vector mkTypedInitialize(core::MutableContext ctx, core::Loc // to get the correct handling. ast::ExpressionPtr maybeSuper; if (absl::c_any_of(props, [](const auto &prop) { return prop.enum_ != nullptr; })) { - maybeSuper = ast::MK::ZSuper(klassDeclLoc); + maybeSuper = ast::MK::ZSuper(klassDeclLoc, core::Names::untypedSuper()); } else { maybeSuper = ast::MK::Nil(klassDeclLoc); } diff --git a/rewriter/Regexp.cc b/rewriter/Regexp.cc deleted file mode 100644 index 9ff603f2f1..0000000000 --- a/rewriter/Regexp.cc +++ /dev/null @@ -1,36 +0,0 @@ -#include "rewriter/Regexp.h" -#include "ast/Helpers.h" -#include "ast/ast.h" -#include "core/Context.h" -#include "core/Names.h" -#include "core/core.h" -#include "rewriter/rewriter.h" - -using namespace std; - -namespace sorbet::rewriter { - -vector Regexp::run(core::MutableContext ctx, ast::Assign *asgn) { - auto lhs = ast::cast_tree(asgn->lhs); - if (lhs == nullptr) { - return {}; - } - - auto send = ast::cast_tree(asgn->rhs); - if (send == nullptr || send->fun != core::Names::new_()) { - return {}; - } - - auto recv = ast::cast_tree(send->recv); - if (recv == nullptr || recv->symbol != core::Symbols::Regexp()) { - return {}; - } - - vector stats; - auto type = ast::MK::Constant(send->loc, core::Symbols::Regexp()); - auto newRhs = ast::MK::Let(send->loc, std::move(asgn->rhs), std::move(type)); - stats.emplace_back(ast::MK::Assign(asgn->loc, std::move(asgn->lhs), std::move(newRhs))); - return stats; -} - -}; // namespace sorbet::rewriter diff --git a/rewriter/Regexp.h b/rewriter/Regexp.h deleted file mode 100644 index 4826789f0f..0000000000 --- a/rewriter/Regexp.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef SORBET_REWRITER_REGEXP_H -#define SORBET_REWRITER_REGEXP_H -#include "ast/ast.h" - -namespace sorbet::rewriter { - -/** - * This class desugars things of the form - * - * A = Regexp.new(...) - * - * into - * - * A = T.let(Regexp.new(...), Regexp) - * - */ -class Regexp final { -public: - static std::vector run(core::MutableContext ctx, ast::Assign *asgn); - - Regexp() = delete; -}; - -} // namespace sorbet::rewriter - -#endif diff --git a/rewriter/Struct.cc b/rewriter/Struct.cc index 988e9cd620..b96221f454 100644 --- a/rewriter/Struct.cc +++ b/rewriter/Struct.cc @@ -22,30 +22,30 @@ bool isKeywordInitKey(const core::GlobalState &gs, const ast::ExpressionPtr &nod return false; } -bool isMissingInitialize(const core::GlobalState &gs, const ast::Send *send) { - if (!send->hasBlock()) { - return true; - } - - auto block = send->block(); - - if (auto *insSeq = ast::cast_tree(block->body)) { - auto methodDef = ast::cast_tree(insSeq->expr); - - if (methodDef && methodDef->name == core::Names::initialize()) { - return false; - } +// Elem = type_member {{fixed: T.untyped}} +ast::ExpressionPtr elemFixedUntyped(core::LocOffsets loc) { + auto typeMember = ast::MK::Send0(loc, ast::MK::Self(loc), core::Names::typeMember(), loc.copyWithZeroLength()); + ast::cast_tree_nonnull(typeMember) + .setBlock(ast::MK::Block0( + loc, ast::MK::Hash1(loc, ast::MK::Symbol(loc, core::Names::fixed()), ast::MK::Untyped(loc)))); + return ast::MK::Assign(loc, ast::MK::UnresolvedConstant(loc, ast::MK::EmptyTree(), core::Names::Constants::Elem()), + std::move(typeMember)); +} - for (auto &&stat : insSeq->stats) { - methodDef = ast::cast_tree(stat); +void selfScopeToEmptyTree(ast::UnresolvedConstantLit &cnst) { + if (ast::isa_tree(cnst.scope) || ast::isa_tree(cnst.scope)) { + return; + } - if (methodDef && methodDef->name == core::Names::initialize()) { - return false; - } - } + if (cnst.scope.isSelfReference()) { + cnst.scope = ast::MK::EmptyTree(); + return; } - return true; + if (auto scope = ast::cast_tree(cnst.scope)) { + selfScopeToEmptyTree(*scope); + return; + } } } // namespace @@ -138,48 +138,65 @@ vector Struct::run(core::MutableContext ctx, ast::Assign *as argName = ast::make_expression(symLoc, move(argName)); } newArgs.emplace_back(ast::MK::OptionalArg(symLoc, move(argName), ast::MK::Nil(symLoc))); - body.emplace_back(ast::MK::SyntheticMethod0(symLoc, symLoc, symLoc, name, ast::MK::RaiseUnimplemented(loc))); - body.emplace_back(ast::MK::SyntheticMethod1(symLoc, symLoc, symLoc, name.addEq(ctx), - ast::MK::Local(symLoc, name), ast::MK::RaiseUnimplemented(loc))); + + body.emplace_back(ast::MK::Sig0(symLoc.copyWithZeroLength(), ast::MK::Untyped(symLoc.copyWithZeroLength()))); + body.emplace_back(ast::MK::SyntheticMethod0(symLoc, symLoc, symLoc, name, ast::MK::RaiseTypedUnimplemented(loc))); + body.emplace_back(ast::MK::Sig1(symLoc.copyWithZeroLength(), ast::MK::Symbol(symLoc, name), + ast::MK::Untyped(symLoc.copyWithZeroLength()), + ast::MK::Untyped(symLoc.copyWithZeroLength()))); + body.emplace_back(ast::MK::SyntheticMethod1(symLoc, symLoc, symLoc, name.addEq(ctx), ast::MK::Local(symLoc, name), + ast::MK::RaiseTypedUnimplemented(loc))); } - // Elem = type_member {{fixed: T.untyped}} + body.emplace_back(elemFixedUntyped(loc)); + body.emplace_back(ast::MK::SigVoid(loc, std::move(sigArgs))); + body.emplace_back(ast::MK::SyntheticMethod(loc, loc, loc, core::Names::initialize(), std::move(newArgs), + ast::MK::RaiseTypedUnimplemented(loc))); + + vector stats; + + auto structUniqueName = asgn->lhs.deepCopy(); + auto &structUniqueNameUnresolvedConst = ast::cast_tree_nonnull(structUniqueName); + structUniqueNameUnresolvedConst.cnst = + ctx.state.enterNameConstant(ctx.state.freshNameUnique(core::UniqueNameKind::Struct, lhs->cnst, 1)); + + // class Foo$1 < ::Struct { - auto typeMember = ast::MK::Send0(loc, ast::MK::Self(loc), core::Names::typeMember(), loc.copyWithZeroLength()); - ast::cast_tree_nonnull(typeMember) - .setBlock(ast::MK::Block0( - loc, ast::MK::Hash1(loc, ast::MK::Symbol(loc, core::Names::fixed()), ast::MK::Untyped(loc)))); - body.emplace_back( - ast::MK::Assign(loc, ast::MK::UnresolvedConstant(loc, ast::MK::EmptyTree(), core::Names::Constants::Elem()), - std::move(typeMember))); - } + ast::ClassDef::ANCESTORS_store ancestors; + ancestors.emplace_back(ast::MK::UnresolvedConstant(loc, ast::MK::Constant(loc, core::Symbols::root()), + core::Names::Constants::Struct())); - if (isMissingInitialize(ctx, send)) { - body.emplace_back(ast::MK::SigVoid(loc, std::move(sigArgs))); - body.emplace_back(ast::MK::SyntheticMethod(loc, loc, loc, core::Names::initialize(), std::move(newArgs), - ast::MK::RaiseUnimplemented(loc))); + stats.emplace_back( + ast::MK::Class(loc, loc, structUniqueName.deepCopy(), std::move(ancestors), std::move(body))); } - if (auto *block = send->block()) { - // Steal the trees, because the run is going to remove the original send node from the tree anyway. - if (auto *insSeq = ast::cast_tree(block->body)) { - for (auto &&stat : insSeq->stats) { - body.emplace_back(move(stat)); + // class Foo < Foo$1 + { + ast::ClassDef::ANCESTORS_store ancestors; + selfScopeToEmptyTree(structUniqueNameUnresolvedConst); + ancestors.emplace_back(move(structUniqueName)); + + ast::ClassDef::RHS_store body; + + body.emplace_back(elemFixedUntyped(loc)); + + if (auto *block = send->block()) { + // Steal the trees, because the run is going to remove the original send node from the tree anyway. + if (auto *insSeq = ast::cast_tree(block->body)) { + for (auto &&stat : insSeq->stats) { + body.emplace_back(move(stat)); + } + body.emplace_back(move(insSeq->expr)); + } else { + body.emplace_back(move(block->body)); } - body.emplace_back(move(insSeq->expr)); - } else { - body.emplace_back(move(block->body)); + + // NOTE: the code in this block _STEALS_ trees. No _return empty_'s should go after it } - // NOTE: the code in this block _STEALS_ trees. No _return empty_'s should go after it + stats.emplace_back(ast::MK::Class(loc, loc, move(asgn->lhs), std::move(ancestors), std::move(body))); } - ast::ClassDef::ANCESTORS_store ancestors; - ancestors.emplace_back(ast::MK::UnresolvedConstant(loc, ast::MK::Constant(loc, core::Symbols::root()), - core::Names::Constants::Struct())); - - vector stats; - stats.emplace_back(ast::MK::Class(loc, loc, std::move(asgn->lhs), std::move(ancestors), std::move(body))); return stats; } diff --git a/rewriter/Struct.h b/rewriter/Struct.h index d6d2f0d8d0..f471a1b503 100644 --- a/rewriter/Struct.h +++ b/rewriter/Struct.h @@ -7,20 +7,41 @@ namespace sorbet::rewriter { /** * This class desugars things of the form * - * A = Struct.new(:foo, :bar) + * A = Struct.new(:foo, :bar) do + * # ... body ... + * end * * into * - * class A < Struct + * class A$1 < Struct + * sig {returns(T.untyped)} * def foo; end + * + * sig {params(arg: T.untyped).returns(T.untyped)} * def foo=(arg); arg; end + * + * sig {returns(T.untyped)} * def bar; end + * + * sig {params(arg: T.untyped).returns(T.untyped)} * def bar=(arg); arg; end + * * sig {params(foo: BasicObject, bar: BasicObject).returns(A)} - * def self.new(foo=nil, bar=nil) - * T.cast(nil, A) - * end + * def initialize(foo=nil, bar=nil); end + * + * Elem = type_member {{fixed: T.untyped}} * end + * + * class A < A$1 + * Elem = type_member {{fixed: T.untyped}} + * + * # ... body ... + * end + * + * The fake A$1 class mimics how `Struct.new` works at runtime, and allows doing things like + * overriding methods inside `# ... body ...` instead of redefining them (which means `super` will + * simply dispatch the the original method, rather than having to use something like + * `alias_method` to get a reference to the original method before redefinition.) */ class Struct final { public: diff --git a/rewriter/TEnum.cc b/rewriter/TEnum.cc index de4a5523f3..c1cbf83acc 100644 --- a/rewriter/TEnum.cc +++ b/rewriter/TEnum.cc @@ -49,12 +49,11 @@ ast::Send *asEnumsDo(ast::ExpressionPtr &stat) { } } -vector badConst(core::MutableContext ctx, core::LocOffsets headerLoc, core::LocOffsets line1Loc) { +void badConst(core::MutableContext ctx, core::LocOffsets headerLoc, core::LocOffsets line1Loc) { if (auto e = ctx.beginError(headerLoc, core::errors::Rewriter::BadTEnumSyntax)) { e.setHeader("All constants defined on an `{}` must be unique instances of the enum", "T::Enum"); e.addErrorLine(ctx.locAt(line1Loc), "Enclosing definition here"); } - return {}; } ast::Send *findSelfNew(ast::ExpressionPtr &assignRhs) { @@ -100,8 +99,13 @@ ast::Send *findSelfNew(ast::ExpressionPtr &assignRhs) { return findSelfNew(cast->arg); } -vector processStat(core::MutableContext ctx, ast::ClassDef *klass, ast::ExpressionPtr &stat, - FromWhere fromWhere) { +struct ProcessStatResult { + vector stats; + core::TypePtr type; +}; + +std::optional processStat(core::MutableContext ctx, ast::ClassDef *klass, ast::ExpressionPtr &stat, + FromWhere fromWhere) { auto *asgn = ast::cast_tree(stat); if (asgn == nullptr) { return {}; @@ -114,7 +118,8 @@ vector processStat(core::MutableContext ctx, ast::ClassDef * auto *selfNew = findSelfNew(asgn->rhs); if (selfNew == nullptr) { - return badConst(ctx, stat.loc(), klass->loc); + badConst(ctx, stat.loc(), klass->loc); + return {}; } // By this point, we have something that looks like @@ -123,6 +128,18 @@ vector processStat(core::MutableContext ctx, ast::ClassDef * // // So we're good to process this thing as a new T::Enum value. + core::TypePtr serializeType = core::Types::untypedUntracked(); + + if (selfNew->numPosArgs() == 0 && selfNew->onlyPosArgs()) { + serializeType = core::Types::String(); + } else if (selfNew->numPosArgs() == 1) { + if (auto *selfNewArg = ast::cast_tree(selfNew->getPosArg(0))) { + // If the enum has exactly one variant that has a literal passed (ex. "a"), + // then its type will be String("a"), but we want a ClassType as the return type. + serializeType = core::Types::dropLiteral(ctx, selfNewArg->value); + } + } + if (fromWhere != FromWhere::Inside) { if (auto e = ctx.beginError(stat.loc(), core::errors::Rewriter::BadTEnumSyntax)) { e.setHeader("Definition of enum value `{}` must be within the `{}` block for this `{}`", @@ -150,18 +167,22 @@ vector processStat(core::MutableContext ctx, ast::ClassDef * vector result; result.emplace_back(std::move(classDef)); result.emplace_back(std::move(singletonAsgn)); - return result; + + return {{std::move(result), serializeType}}; } -void collectNewStats(core::MutableContext ctx, ast::ClassDef *klass, ast::ExpressionPtr stat, FromWhere fromWhere, - vector &into) { - auto newStats = processStat(ctx, klass, stat, fromWhere); - if (newStats.empty()) { - into.emplace_back(std::move(stat)); - } else { +core::TypePtr collectNewStats(core::MutableContext ctx, ast::ClassDef *klass, ast::ExpressionPtr stat, + FromWhere fromWhere, vector &into) { + auto result = processStat(ctx, klass, stat, fromWhere); + if (result) { + auto [newStats, type] = std::move(*result); for (auto &newStat : newStats) { into.emplace_back(std::move(newStat)); } + return type; + } else { + into.emplace_back(std::move(stat)); + return core::Types::bottom(); } } @@ -185,17 +206,21 @@ void TEnum::run(core::MutableContext ctx, ast::ClassDef *klass) { ast::MK::Constant(loc, core::Symbols::T_Helpers()))); klass->rhs.emplace_back(ast::MK::Send0(loc, ast::MK::Self(loc), core::Names::declareAbstract(), locZero)); klass->rhs.emplace_back(ast::MK::Send0(loc, ast::MK::Self(loc), core::Names::declareSealed(), locZero)); + core::TypePtr serializeReturnType = core::Types::bottom(); for (auto &stat : oldRHS) { if (auto enumsDo = asEnumsDo(stat)) { auto *block = enumsDo->block(); vector newStats; if (auto insSeq = ast::cast_tree(block->body)) { for (auto &stat : insSeq->stats) { - collectNewStats(ctx, klass, std::move(stat), FromWhere::Inside, newStats); + auto type = collectNewStats(ctx, klass, std::move(stat), FromWhere::Inside, newStats); + serializeReturnType = core::Types::any(ctx, serializeReturnType, type); } - collectNewStats(ctx, klass, std::move(insSeq->expr), FromWhere::Inside, newStats); + auto type = collectNewStats(ctx, klass, std::move(insSeq->expr), FromWhere::Inside, newStats); + serializeReturnType = core::Types::any(ctx, serializeReturnType, type); } else { - collectNewStats(ctx, klass, std::move(block->body), FromWhere::Inside, newStats); + auto type = collectNewStats(ctx, klass, std::move(block->body), FromWhere::Inside, newStats); + serializeReturnType = core::Types::any(ctx, serializeReturnType, type); } ast::InsSeq::STATS_store insSeqStats; @@ -213,5 +238,22 @@ void TEnum::run(core::MutableContext ctx, ast::ClassDef *klass) { } } } + if (core::isa_type(serializeReturnType) && !serializeReturnType.isUntyped() && + !serializeReturnType.isBottom()) { + auto serializeReturnTypeClass = core::cast_type_nonnull(serializeReturnType); + ast::ExpressionPtr return_type_ast = ast::MK::Constant(klass->declLoc, serializeReturnTypeClass.symbol); + auto sig = ast::MK::Sig0(klass->declLoc, std::move(return_type_ast)); + auto method = ast::MK::SyntheticMethod0(klass->loc, klass->declLoc, klass->loc, core::Names::serialize(), + ast::MK::RaiseTypedUnimplemented(klass->declLoc)); + ast::Send::ARGS_store nargs; + ast::Send::Flags flags; + flags.isPrivateOk = true; + auto visibility = ast::MK::Send(klass->declLoc, ast::MK::Self(klass->declLoc), core::Names::public_(), + klass->declLoc, 0, std::move(nargs), flags); + + klass->rhs.emplace_back(std::move(visibility)); + klass->rhs.emplace_back(std::move(sig)); + klass->rhs.emplace_back(std::move(method)); + } } }; // namespace sorbet::rewriter diff --git a/rewriter/TestCase.cc b/rewriter/TestCase.cc index 9fec885ced..39be7fdbed 100644 --- a/rewriter/TestCase.cc +++ b/rewriter/TestCase.cc @@ -53,7 +53,8 @@ void TestCase::run(core::MutableContext ctx, ast::ClassDef *klass) { auto *send = ast::cast_tree(stat); auto loc = send->loc; auto block = send->block(); - auto method_name = send->fun == core::Names::setup() ? core::Names::initialize() : core::Names::teardown(); + auto method_name = + send->fun == core::Names::setup() ? core::Names::beforeAngles() : core::Names::teardown(); auto method = ast::MK::SyntheticMethod0(loc, loc, send->funLoc, method_name, std::move(block->body)); auto method_with_sig = diff --git a/rewriter/rewriter.cc b/rewriter/rewriter.cc index 1f75a0833a..f8980025d6 100644 --- a/rewriter/rewriter.cc +++ b/rewriter/rewriter.cc @@ -17,7 +17,6 @@ #include "rewriter/Flatten.h" #include "rewriter/HasAttachedClass.h" #include "rewriter/Initializer.h" -#include "rewriter/InterfaceWrapper.h" #include "rewriter/Mattr.h" #include "rewriter/Minitest.h" #include "rewriter/MixinEncryptedProp.h" @@ -25,7 +24,6 @@ #include "rewriter/Private.h" #include "rewriter/Prop.h" #include "rewriter/Rails.h" -#include "rewriter/Regexp.h" #include "rewriter/SigRewriter.h" #include "rewriter/Struct.h" #include "rewriter/TEnum.h" @@ -85,12 +83,6 @@ class Rewriterer { return; } - nodes = Regexp::run(ctx, &assign); - if (!nodes.empty()) { - replaceNodes[stat.get()] = std::move(nodes); - return; - } - // This has to come after the `Class.new` rewriter, because they would otherwise overlap. ConstantAssumeType::run(ctx, &assign); }, @@ -193,11 +185,6 @@ class Rewriterer { return; } - if (auto expr = InterfaceWrapper::run(ctx, send)) { - tree = std::move(expr); - return; - } - if (auto expr = TypeAssertion::run(ctx, send)) { tree = std::move(expr); return; diff --git a/scip_indexer/BUILD b/scip_indexer/BUILD index 4cd6786786..310de4fa77 100644 --- a/scip_indexer/BUILD +++ b/scip_indexer/BUILD @@ -53,6 +53,7 @@ cc_library( "//cfg", "//common", "//core", + "//core/source_generator", "//main/lsp", "//proto", "//sorbet_version", diff --git a/scip_indexer/SCIPIndexer.cc b/scip_indexer/SCIPIndexer.cc index 15789daddc..198c8d82e0 100644 --- a/scip_indexer/SCIPIndexer.cc +++ b/scip_indexer/SCIPIndexer.cc @@ -451,6 +451,27 @@ class SCIPState { return absl::OkStatus(); } + absl::Status saveQualifierReferences(const core::GlobalState &gs, core::FileRef file, + ast::ExpressionPtr &constantLitExpr) { + auto *expr = &constantLitExpr; + while (auto *constantLit = ast::cast_tree(*expr)) { + if (constantLit->symbol.exists() && constantLit->symbol.asClassOrModuleRef().exists()) { + core::Context ctx(gs, constantLit->symbol, file); + auto status = this->saveReference(ctx, GenericSymbolRef::classOrModule(constantLit->symbol), + /*overrideType*/ std::nullopt, constantLit->loc, 0); + if (!status.ok()) { + return status; + } + } + if (auto *unresolved = ast::cast_tree(constantLit->original)) { + expr = &unresolved->scope; + continue; + } + break; + } + return absl::OkStatus(); + } + absl::Status saveReference(const core::Context &ctx, GenericSymbolRef symRef, optional overrideType, core::LocOffsets occLoc, int32_t symbol_roles) { // HACK: Reduce noise due to in snapshots. @@ -1326,6 +1347,29 @@ class SCIPSemanticExtension : public SemanticExtension { } }; + virtual void typecheckClass(const core::GlobalState &gs, core::FileRef file, ast::ClassDef &klass) const override { + if (this->doNothing() || ast::isa_tree(klass.name)) { + return; + } + auto scipState = this->getSCIPState(); + + auto status = scipState->saveDefinition(gs, file, scip_indexer::GenericSymbolRef::classOrModule(klass.symbol), + klass.name.loc()); + ENFORCE(status.ok()); + auto *expr = &klass.name; + if (auto *constantLit = ast::cast_tree(*expr)) { + if (auto *unresolved = ast::cast_tree(constantLit->original)) { + auto status = scipState->saveQualifierReferences(gs, file, unresolved->scope); + ENFORCE(status.ok()); + } + } + + for (auto &ancestorExpr : klass.ancestors) { + auto status = scipState->saveQualifierReferences(gs, file, ancestorExpr); + ENFORCE(status.ok()); + } + } + virtual void typecheck(const core::GlobalState &gs, core::FileRef file, cfg::CFG &cfg, ast::MethodDef &methodDef) const override { if (this->doNothing()) { diff --git a/scip_indexer/SCIPSymbolRef.cc b/scip_indexer/SCIPSymbolRef.cc index e6b9c40b3f..4c58762271 100644 --- a/scip_indexer/SCIPSymbolRef.cc +++ b/scip_indexer/SCIPSymbolRef.cc @@ -12,6 +12,7 @@ #include "common/FileSystem.h" #include "common/sort/sort.h" +#include "core/source_generator/source_generator.h" #include "core/Loc.h" #include "main/lsp/LSPLoop.h" @@ -225,9 +226,9 @@ void GenericSymbolRef::saveDocStrings(const core::GlobalState &gs, core::TypePtr } case Kind::Method: { auto ref = this->selfOrOwner.asMethodRef(); - auto resultType = ref.data(gs)->owner.data(gs)->resultType; - checkType(resultType, fmt::format("result type for {}", ref.showFullName(gs))); - markdown = realmain::lsp::prettyTypeForMethod(gs, ref, resultType, nullptr, nullptr); + auto recvType = ref.data(gs)->owner.data(gs)->resultType; + checkType(recvType, fmt::format("receiver type for {}", ref.showFullName(gs))); + markdown = core::source_generator::prettyTypeForMethod(gs, ref, recvType, nullptr, nullptr, core::ShowOptions()); // FIXME(varun): For some reason, it looks like a bunch of public methods // get marked as private here. Avoid printing misleading info until we fix that. // https://github.com/sourcegraph/scip-ruby/issues/33 diff --git a/sorbet-README.md b/sorbet-README.md index 34e5baf383..66a533b8d2 100644 --- a/sorbet-README.md +++ b/sorbet-README.md @@ -480,14 +480,14 @@ different version number, and also marked `default-arg-value`: This is due to the translation of defaults into the CFG: there is a synthetic conditional that chooses either to initialize the variable from the argument passed at the send, or to the default value when no value is present. -Finding all references works differently in package specification (__package.rb) files. Consider the following: +Finding all references works differently in package specification (`__package.rb`) files. Consider the following: ```ruby class Foo < PackageSpec import Bar ``` -Calling "find all references" on `Bar` in this file will return only references to `Bar` in the `Foo` package. LSP tests +Calling "find all references" on `Bar` in this file will return only references to `Bar` in the `Foo` package. LSP tests have access to `import` and `importusage` assertions that you can use to test this functionality. ```ruby @@ -505,7 +505,7 @@ class Foo < PackageSpec With these annotations, the LSP test will check if "find all references" on `Bar` in `import Bar` statement returns the `Bar.new` usage. -Note that an `import` assertion is dissimilar to a `def` assertion, in that it is in fact a subclass of a `usage` assertion. +Note that an `import` assertion is dissimilar to a `def` assertion, in that it is in fact a subclass of a `usage` assertion. In this case, the `def` corresponding to an `import` is the PackageSpec declaration of the imported package. Calling "find all references" on a PackageSpec declaration will return all imports of the package. @@ -854,6 +854,9 @@ assertion replaces the spacer line, instead of being inserted into the file as a completely new line. Search for `spacer` in some of the `fast_path` tests to see an example. +To craft an update to an RBI file, use `.rbiupdate` instead of `.rbupdate`, +unless you mean to simulate the effect of converting an RBI file to an RB file. + ### LSP recorded tests It is possible to record an LSP session and use it as a test. We are attempting to move away from this form of @@ -1038,7 +1041,7 @@ You are encouraged to play around with various clang-based tools which use the After successfully compiling Sorbet, point your editor to use the `clangd` executable located in - `bazel-sorbet/external/llvm_toolchain_12_0_0/bin/clangd`. + `bazel-sorbet/external/llvm_toolchain_15_0_7/bin/clangd`. - [clang-format] -- Clang-based source code formatter diff --git a/sorbet_version/sorbet_version.c b/sorbet_version/sorbet_version.c index 116ef1915c..b73b552919 100644 --- a/sorbet_version/sorbet_version.c +++ b/sorbet_version/sorbet_version.c @@ -55,6 +55,9 @@ const char sorbet_full_version_string[] = SORBET_VERSION "." QUOTED(STABLE_BUILD " debug_symbols=" STABLE_BUILD_DEBUG_SYMBOLS " clean=" STABLE_BUILD_SCM_CLEAN #ifdef DEBUG_MODE " debug_mode=true" +#endif +#ifdef TRACK_UNTYPED_BLAME_MODE + " untyped_blame=true" #endif ; diff --git a/test/BUILD b/test/BUILD index d6bbb3f927..6b68f1cebc 100644 --- a/test/BUILD +++ b/test/BUILD @@ -1,7 +1,5 @@ load("//tools:clang.bzl", "clang_tool") # todo: this should be decoupled and use the library toolchain, not the compiler one -clang_tool("llvm-diff") # depended on by compiler_tests - clang_tool("llvm-symbolizer") cc_binary( @@ -28,6 +26,7 @@ cc_binary( "//main/autogen", "//main/lsp", "//main/minimize", + "//main/pipeline", "//main/pipeline/semantic_extension:none", "//namer", "//packager", @@ -98,6 +97,7 @@ cc_binary( "//scip_indexer", "//test/helpers", "@com_google_absl//absl/strings", + "@com_google_absl//absl/types:span", "@cxxopts", "@doctest//doctest", "@doctest//doctest:custom_main", @@ -278,25 +278,10 @@ pipeline_tests( "testdata/**/*.rb", "testdata/**/*.exp", ], - exclude = [ - "testdata/compiler/**/*", - "testdata/ruby_benchmark/**/*", - ], ), "PosTests", ) -pipeline_tests( - "test_packager", - glob( - [ - "testdata/packager/**/*.rb", - "testdata/packager/**/*.exp", - ], - ), - "PackagerTests", -) - pipeline_tests( "whitequark_parser_corpus", glob([ @@ -325,10 +310,6 @@ pipeline_tests( "testdata/**/*.rbi", "testdata/**/*.exp", ], - exclude = [ - "testdata/compiler/**/*", - "testdata/ruby_benchmark/**/*", - ], ), "LSPTests", extra_files = ["testdata/lsp/rubyfmt-stub/rubyfmt"], @@ -344,101 +325,31 @@ test_suite( tests = ["whitequark_parser_corpus"], ) -load(":compiler_tests.bzl", "compiler_tests") - -compiler_tests( - "compiler", - glob([ - "testdata/compiler/**/*.rb", - "testdata/ruby_benchmark/**/*.rb", - ]), - tags = ["compiler"], -) - -sh_library( - name = "logging", - srcs = ["logging.sh"], -) - -sh_library( - name = "hermetic_tar", - srcs = ["hermetic_tar.sh"], -) - -sh_binary( - name = "generate_out_file", - srcs = ["generate_out_file.sh"], - data = [ - "patch_require.rb", - "//gems/sorbet-runtime", - "@sorbet_ruby_2_7_for_compiler//:ruby", - ], - deps = [ - ":logging", - "@bazel_tools//tools/bash/runfiles", - ], -) - -sh_binary( - name = "build_extension", - srcs = ["build_extension.sh"], - data = ["//compiler:sorbet"], - tags = ["compiler"], - deps = [ - ":hermetic_tar", - ":logging", - "@bazel_tools//tools/bash/runfiles", - ], -) - -sh_binary( - name = "test_corpus_runner", - srcs = ["test_corpus_runner.sh"], - tags = ["compiler"], - deps = [ - ":logging", - ], -) - -sh_binary( - name = "validate_exp", - srcs = ["validate_exp.sh"], - data = [ - "diff-diff.rb", - "@sorbet_ruby_2_7_for_compiler//:ruby", - ], - tags = ["compiler"], - deps = [ - ":logging", - "@bazel_tools//tools/bash/runfiles", - ], -) - sh_binary( name = "single_package_runner", srcs = ["test_single_package_runner.sh"], data = [ - "rbi_gen_package_runner.rb", - "//gems/sorbet-runtime", + "single_package_runner_cc", "//main:sorbet", - "@sorbet_ruby_2_7//:ruby", ], - # This is to get the test to run on the compiler build job, - # so we can avoid building ruby on the test-static-sanitized job. - tags = ["compiler"], + tags = ["manual"], deps = [ - ":logging", "@bazel_tools//tools/bash/runfiles", ], ) -sh_binary( - name = "filecheck", - testonly = 1, - srcs = ["filecheck.sh"], - data = ["@llvm//:FileCheck"], - tags = ["compiler"], +cc_binary( + name = "single_package_runner_cc", + srcs = ["rbi_gen_package_runner.cc"], + linkstatic = select({ + "//tools/config:linkshared": 0, + "//conditions:default": 1, + }), + tags = ["manual"], + visibility = ["//tools:__pkg__"], deps = [ - ":logging", + "//common", + "@com_google_absl//absl/strings", + "@rapidjson", ], ) diff --git a/test/build_extension.sh b/test/build_extension.sh deleted file mode 100755 index 022dd51512..0000000000 --- a/test/build_extension.sh +++ /dev/null @@ -1,89 +0,0 @@ -#!/bin/bash - -set -euo pipefail - -# --- begin runfiles.bash initialization --- {{{ -# Copy-pasted from Bazel's Bash runfiles library https://github.com/bazelbuild/bazel/blob/defd737761be2b154908646121de47c30434ed51/tools/bash/runfiles/runfiles.bash -if [[ ! -d "${RUNFILES_DIR:-/dev/null}" && ! -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then - if [[ -f "$0.runfiles_manifest" ]]; then - export RUNFILES_MANIFEST_FILE="$0.runfiles_manifest" - elif [[ -f "$0.runfiles/MANIFEST" ]]; then - export RUNFILES_MANIFEST_FILE="$0.runfiles/MANIFEST" - elif [[ -f "$0.runfiles/bazel_tools/tools/bash/runfiles/runfiles.bash" ]]; then - export RUNFILES_DIR="$0.runfiles" - fi -fi -if [[ -f "${RUNFILES_DIR:-/dev/null}/bazel_tools/tools/bash/runfiles/runfiles.bash" ]]; then - # shellcheck disable=SC1091 - source "${RUNFILES_DIR}/bazel_tools/tools/bash/runfiles/runfiles.bash" -elif [[ -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then - # shellcheck disable=SC1090 - source "$(grep -m1 "^bazel_tools/tools/bash/runfiles/runfiles.bash " \ - "$RUNFILES_MANIFEST_FILE" | cut -d ' ' -f 2-)" -else - echo >&2 "ERROR: cannot find @bazel_tools//tools/bash/runfiles:runfiles.bash" - exit 1 -fi -# --- end runfiles.bash initialization --- }}} - -# Find logging and hermetic_tar with rlocation, as this script is run from a genrule - -# shellcheck source=SCRIPTDIR/logging.sh -source "$(rlocation com_stripe_ruby_typer/test/logging.sh)" - -# shellcheck source=SCRIPTDIR/hermetic_tar.sh -source "$(rlocation com_stripe_ruby_typer/test/hermetic_tar.sh)" - -# Argument Parsing ############################################################# - -# Positional arguments -output_dir=$1 -stdout=$2 -exitcode=$3 -shift 3 - -# Sources make up the remaining input -ruby_source=( "$@" ) - -# Environment Setup ############################################################ - -sorbet="$(rlocation com_stripe_ruby_typer/compiler/sorbet)" - -# Main ######################################################################### - -if [ ! -d "$output_dir" ]; then - info "Creating output dir $output_dir" - mkdir "$output_dir" -fi - -indent_and_nest() { - sed -e 's/^/ │/' -} - -echo -info "Input(s):" -i=1 -for file in "${ruby_source[@]}"; do - if [ "$i" -ne "${#ruby_source[@]}" ]; then - info "├─ $file" - else - info "└─ $file" - fi - (( i++ )) -done - -echo -info "Using Sorbet to generate compiled files and llvm ir..." -compiled_out_dir_flag="--compiled-out-dir=$output_dir" -llvm_ir_dir_flag="--llvm-ir-dir=$output_dir" -info "├─ Using $compiled_out_dir_flag" -info "├─ Using $llvm_ir_dir_flag" -set +e -$sorbet --silence-dev-message --no-error-count \ - "$compiled_out_dir_flag" "$llvm_ir_dir_flag" "${ruby_source[@]}" > "$stdout" 2>&1 -echo "$?" > "$exitcode" -set -e - -success "└─ done." - -# vim:fdm=marker diff --git a/test/cli/arity-messages/super-arity-messages.rb b/test/cli/arity-messages/super-arity-messages.rb new file mode 100644 index 0000000000..6c9b369d09 --- /dev/null +++ b/test/cli/arity-messages/super-arity-messages.rb @@ -0,0 +1,26 @@ +# typed: strict + +class Parent + extend T::Sig + + sig {params(x: Integer).void} + def unary1(x) + end + + sig {params(x: Integer).void} + def unary2(x) + super + end +end + +class Child < Parent + sig {void} + def unary1 + super + end + + sig {params(x: Integer, y: String).void} + def unary2(x, y) + super + end +end diff --git a/test/cli/arity-messages/test.out b/test/cli/arity-messages/test.out index 1f22fe8b1a..daf6fbf6bd 100644 --- a/test/cli/arity-messages/test.out +++ b/test/cli/arity-messages/test.out @@ -14,7 +14,7 @@ arity-messages.rb:4: Too many positional arguments provided for method `Object#f arity-messages.rb:5: Unrecognized keyword argument `y` passed for method `Object#foo` https://srb.help/7004 5 |foo(y: 1) - ^^^^^^^^^ + ^^^^ arity-messages.rb:8: Too many positional arguments provided for method `Object#bar`. Expected: `0`, got: `1` https://srb.help/7004 8 |bar(5, x: 8) @@ -74,7 +74,7 @@ arity-messages.rb:18: Too many positional arguments provided for method `Object# arity-messages.rb:21: Unrecognized keyword argument `c` passed for method `Object#qaadr` https://srb.help/7004 21 |qaadr({a: 1}, {b: 2}, {c: 3}) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ^^^^^^ arity-messages.rb:21: Too many positional arguments provided for method `Object#qaadr`. Expected: `0`, got: `2` https://srb.help/7004 21 |qaadr({a: 1}, {b: 2}, {c: 3}) @@ -151,3 +151,58 @@ pluto def paperino(x, y: nil, **kwargs); end paperino(2, y: 3, z: 4) + +-------------------------------------------------------------------------- + +super-arity-messages.rb:12: Method `unary2` does not exist on ancestors of `Parent` https://srb.help/7048 + 12 | super + ^^^^^ + Note: + For help fixing `super` errors: https://sorbet.org/docs/typed-super + +super-arity-messages.rb:19: Not enough arguments provided for method `Parent#unary1`. Expected: `1`, got: `0` https://srb.help/7004 + 19 | super + ^ + super-arity-messages.rb:7: `Parent#unary1` defined here + 7 | def unary1(x) + ^^^^^^^^^^^^^ + +super-arity-messages.rb:24: Too many arguments provided for method `Parent#unary2`. Expected: `1`, got: `2` https://srb.help/7004 + 24 | super + ^ + super-arity-messages.rb:11: `unary2` defined here + 11 | def unary2(x) + ^^^^^^^^^^^^^ +Errors: 3 + +-------------------------------------------------------------------------- + +# typed: strict + +class Parent + extend T::Sig + + sig {params(x: Integer).void} + def unary1(x) + end + + sig {params(x: Integer).void} + def unary2(x) + super + end +end + +class Child < Parent + sig {void} + def unary1 + super + end + + sig {params(x: Integer, y: String).void} + def unary2(x, y) + super + end +end + +-------------------------------------------------------------------------- + diff --git a/test/cli/arity-messages/test.sh b/test/cli/arity-messages/test.sh index 249e55389e..d465991c04 100755 --- a/test/cli/arity-messages/test.sh +++ b/test/cli/arity-messages/test.sh @@ -1,23 +1,32 @@ #!/usr/bin/env bash cwd="$(pwd)" -infile="$cwd/test/cli/arity-messages/arity-messages.rb" +infiles=( + "$cwd/test/cli/arity-messages/arity-messages.rb" + "$cwd/test/cli/arity-messages/super-arity-messages.rb" +) tmp="$(mktemp -d)" -cp "$infile" "$tmp" +cp "${infiles[@]}" "$tmp" cd "$tmp" || exit 1 -if "$cwd/main/sorbet" --silence-dev-message -a arity-messages.rb 2>&1; then - echo "Expected to fail!" - exit 1 -fi +for infile in *.rb; do + if "$cwd/main/sorbet" --silence-dev-message -a "$infile" 2>&1; then + echo "Expected to fail!" + exit 1 + fi -echo -echo -------------------------------------------------------------------------- -echo + echo + echo -------------------------------------------------------------------------- + echo -# Also cat the file, to make that the autocorrect applied -cat arity-messages.rb + # Also cat the file, to make sure that the autocorrect applied + cat "$infile" -rm arity-messages.rb + rm "$infile" + + echo + echo -------------------------------------------------------------------------- + echo +done diff --git a/test/cli/autocorrect-abstract/test.sh b/test/cli/autocorrect-abstract/test.sh index d457f798fb..d0a85fc6dc 100755 --- a/test/cli/autocorrect-abstract/test.sh +++ b/test/cli/autocorrect-abstract/test.sh @@ -17,7 +17,7 @@ echo echo -------------------------------------------------------------------------- echo -# Also cat the file, to make that the autocorrect applied +# Also cat the file, to make sure that the autocorrect applied cat autocorrect-abstract.rb rm autocorrect-abstract.rb diff --git a/test/cli/autocorrect-bare-stdlib-generics/test.sh b/test/cli/autocorrect-bare-stdlib-generics/test.sh index b60e750f03..cdff99b9d2 100755 --- a/test/cli/autocorrect-bare-stdlib-generics/test.sh +++ b/test/cli/autocorrect-bare-stdlib-generics/test.sh @@ -17,7 +17,7 @@ echo echo -------------------------------------------------------------------------- echo -# Also cat the file, to make that the autocorrect applied +# Also cat the file, to make sure that the autocorrect applied cat autocorrect-bare-stdlib-generics.rb rm autocorrect-bare-stdlib-generics.rb diff --git a/test/cli/autocorrect-helpers/test.sh b/test/cli/autocorrect-helpers/test.sh index 75084f396b..3d87326c1d 100755 --- a/test/cli/autocorrect-helpers/test.sh +++ b/test/cli/autocorrect-helpers/test.sh @@ -16,7 +16,7 @@ echo echo -------------------------------------------------------------------------- echo -# Also cat the file, to make that the autocorrect applied +# Also cat the file, to make sure that the autocorrect applied cat autocorrect-helpers.rb rm autocorrect-helpers.rb diff --git a/test/cli/autocorrect-kwargs/autocorrect-kwargs.rb b/test/cli/autocorrect-kwargs/autocorrect-kwargs.rb index 9efd9f3d2d..7b96ca2e37 100644 --- a/test/cli/autocorrect-kwargs/autocorrect-kwargs.rb +++ b/test/cli/autocorrect-kwargs/autocorrect-kwargs.rb @@ -8,7 +8,7 @@ def test1 {foo: 10, bar: "hi"} end -# Wrap curly braces around the keyword args to [] to make the elemnt of the +# Wrap curly braces around the keyword args to [] to make the element of the # array into a shape sig {returns(T::Array[foo: Integer])} def test2 diff --git a/test/cli/autocorrect-kwargs/test.out b/test/cli/autocorrect-kwargs/test.out index 77dc77bb64..d0e58cac78 100644 --- a/test/cli/autocorrect-kwargs/test.out +++ b/test/cli/autocorrect-kwargs/test.out @@ -87,7 +87,7 @@ def test1 {foo: 10, bar: "hi"} end -# Wrap curly braces around the keyword args to [] to make the elemnt of the +# Wrap curly braces around the keyword args to [] to make the element of the # array into a shape sig {returns(T::Array[{foo: Integer}])} def test2 diff --git a/test/cli/autocorrect-kwsplat/test.sh b/test/cli/autocorrect-kwsplat/test.sh index 0ab452f87f..3e62ba522b 100755 --- a/test/cli/autocorrect-kwsplat/test.sh +++ b/test/cli/autocorrect-kwsplat/test.sh @@ -17,7 +17,7 @@ echo echo -------------------------------------------------------------------------- echo -# Also cat the file, to make that the autocorrect applied +# Also cat the file, to make sure that the autocorrect applied cat test.rb rm test.rb diff --git a/test/cli/autocorrect-lazy-type-alias/test.sh b/test/cli/autocorrect-lazy-type-alias/test.sh index cd2574fd29..8ba08089dd 100755 --- a/test/cli/autocorrect-lazy-type-alias/test.sh +++ b/test/cli/autocorrect-lazy-type-alias/test.sh @@ -17,7 +17,7 @@ echo echo -------------------------------------------------------------------------- echo -# Also cat the file, to make that the autocorrect applied +# Also cat the file, to make sure that the autocorrect applied cat autocorrect-lazy-type-alias.rb rm autocorrect-lazy-type-alias.rb diff --git a/test/cli/autocorrect-multi-statement-sigs/test.sh b/test/cli/autocorrect-multi-statement-sigs/test.sh index 55270a0f7b..48deaba606 100755 --- a/test/cli/autocorrect-multi-statement-sigs/test.sh +++ b/test/cli/autocorrect-multi-statement-sigs/test.sh @@ -17,7 +17,7 @@ echo echo -------------------------------------------------------------------------- echo -# Also cat the file, to make that the autocorrect applied +# Also cat the file, to make sure that the autocorrect applied cat autocorrect-multi-statement-sigs.rb rm autocorrect-multi-statement-sigs.rb diff --git a/test/cli/autocorrect-non-final-methods-final-module/test.sh b/test/cli/autocorrect-non-final-methods-final-module/test.sh index 92ecf5c1ec..8bef413174 100755 --- a/test/cli/autocorrect-non-final-methods-final-module/test.sh +++ b/test/cli/autocorrect-non-final-methods-final-module/test.sh @@ -19,7 +19,7 @@ echo echo -------------------------------------------------------------------------- echo -# Cat the file, to make that the autocorrect applied +# Cat the file, to make sure that the autocorrect applied cat autocorrect-non-final-methods-final-module.rb rm autocorrect-non-final-methods-final-module.rb diff --git a/test/cli/autocorrect-override/autocorrect-override.rb b/test/cli/autocorrect-override/autocorrect-override.rb new file mode 100644 index 0000000000..6a560232c7 --- /dev/null +++ b/test/cli/autocorrect-override/autocorrect-override.rb @@ -0,0 +1,25 @@ +# typed: true + +class Parent + extend T::Sig + sig {overridable.void} + def foo; end +end + +module IChild + extend T::Sig + extend T::Helpers + abstract! + + sig {abstract.void} + def bar; end +end + +class Child < Parent + include IChild + sig {void} + def foo; end + + sig {void} + def bar; end +end diff --git a/test/cli/autocorrect-override/test.out b/test/cli/autocorrect-override/test.out new file mode 100644 index 0000000000..da310ebab1 --- /dev/null +++ b/test/cli/autocorrect-override/test.out @@ -0,0 +1,50 @@ +autocorrect-override.rb:21: Method `Child#foo` overrides an overridable method `Parent#foo` but is not declared with `override.` https://srb.help/5051 + 21 | def foo; end + ^^^^^^^ + autocorrect-override.rb:6: defined here + 6 | def foo; end + ^^^^^^^ + Autocorrect: Done + autocorrect-override.rb:20: Inserted `override.` + 20 | sig {void} + ^ + +autocorrect-override.rb:24: Method `Child#bar` implements an abstract method `IChild#bar` but is not declared with `override.` https://srb.help/5051 + 24 | def bar; end + ^^^^^^^ + autocorrect-override.rb:15: defined here + 15 | def bar; end + ^^^^^^^ + Autocorrect: Done + autocorrect-override.rb:23: Inserted `override.` + 23 | sig {void} + ^ +Errors: 2 + +-------------------------------------------------------------------------- + +# typed: true + +class Parent + extend T::Sig + sig {overridable.void} + def foo; end +end + +module IChild + extend T::Sig + extend T::Helpers + abstract! + + sig {abstract.void} + def bar; end +end + +class Child < Parent + include IChild + sig {override.void} + def foo; end + + sig {override.void} + def bar; end +end diff --git a/test/cli/autocorrect-override/test.sh b/test/cli/autocorrect-override/test.sh new file mode 100755 index 0000000000..14b0d850bf --- /dev/null +++ b/test/cli/autocorrect-override/test.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash + +cwd="$(pwd)" +infile="$cwd/test/cli/autocorrect-override/autocorrect-override.rb" + +tmp="$(mktemp -d)" + +( + cp "$infile" "$tmp" + + cd "$tmp" || exit 1 + if "$cwd/main/sorbet" --silence-dev-message -a autocorrect-override.rb 2>&1; then + echo "Expected to fail!" + exit 1 + fi + + echo + echo -------------------------------------------------------------------------- + echo + + # Also cat the file, to make sure that the autocorrect applied + cat autocorrect-override.rb + + rm autocorrect-override.rb +) diff --git a/test/cli/autocorrect-remove-body/test.sh b/test/cli/autocorrect-remove-body/test.sh index 1dc9b08b5d..4428cfd310 100755 --- a/test/cli/autocorrect-remove-body/test.sh +++ b/test/cli/autocorrect-remove-body/test.sh @@ -16,7 +16,7 @@ echo echo -------------------------------------------------------------------------- echo -# Also cat the file, to make that the autocorrect applied +# Also cat the file, to make sure that the autocorrect applied echo ----- rb ----- cat autocorrect-remove-body.rb echo ----- rbi ----- diff --git a/test/cli/autocorrect-strict/test.sh b/test/cli/autocorrect-strict/test.sh index 5f0fe87e0b..ffd66aa3ae 100755 --- a/test/cli/autocorrect-strict/test.sh +++ b/test/cli/autocorrect-strict/test.sh @@ -17,7 +17,7 @@ echo echo -------------------------------------------------------------------------- echo -# Also cat the file, to make that the autocorrect applied +# Also cat the file, to make sure that the autocorrect applied cat autocorrect-strict.rb rm autocorrect-strict.rb diff --git a/test/cli/autocorrect-t-combinator-kwargs/test.out b/test/cli/autocorrect-t-combinator-kwargs/test.out index 80ad83573b..2392214daa 100644 --- a/test/cli/autocorrect-t-combinator-kwargs/test.out +++ b/test/cli/autocorrect-t-combinator-kwargs/test.out @@ -69,7 +69,22 @@ autocorrect-t-combinator-kwargs.rb:10: Unexpected bare `Symbol(:a)` value found autocorrect-t-combinator-kwargs.rb:10: Unexpected bare `Symbol(:b)` value found in type position https://srb.help/7009 10 | any: T.any(Integer, String, a: Integer, b: String), ^ -Errors: 12 + +autocorrect-t-combinator-kwargs.rb:12: Expected `T.any(T::Set[T.anything], T::Array[T.anything])` but found `{a: T.class_of(Integer)}` for argument `values` https://srb.help/7002 + 12 | enum: T.deprecated_enum(a: Integer), + ^^^^^^^^^^ + Expected `T.any(T::Set[T.anything], T::Array[T.anything])` for argument `values` of method `T.deprecated_enum`: + https://github.com/sorbet/sorbet/tree/master/rbi/sorbet/t.rbi#L130: + 130 | sig { params(values: T.any(T::Set[T.anything], T::Array[T.anything])).returns(T.untyped) } + ^^^^^^ + Got `{a: T.class_of(Integer)} (shape of T::Hash[T.untyped, T.untyped])` originating from: + autocorrect-t-combinator-kwargs.rb:12: + 12 | enum: T.deprecated_enum(a: Integer), + ^^^^^^^^^^ + Detailed explanation: + `Hash` does not derive from `Set` + `Hash` does not derive from `Array` +Errors: 13 -------------------------------------------------------------------------- diff --git a/test/cli/autocorrect-t-combinator-kwargs/test.sh b/test/cli/autocorrect-t-combinator-kwargs/test.sh index 803a99a62b..b896b33ad3 100755 --- a/test/cli/autocorrect-t-combinator-kwargs/test.sh +++ b/test/cli/autocorrect-t-combinator-kwargs/test.sh @@ -17,7 +17,7 @@ echo echo -------------------------------------------------------------------------- echo -# Also cat the file, to make that the autocorrect applied +# Also cat the file, to make sure that the autocorrect applied cat autocorrect-t-combinator-kwargs.rb rm autocorrect-t-combinator-kwargs.rb diff --git a/test/cli/autocorrect-useless-cast/test.sh b/test/cli/autocorrect-useless-cast/test.sh index 969ce36b37..6ea4291b64 100755 --- a/test/cli/autocorrect-useless-cast/test.sh +++ b/test/cli/autocorrect-useless-cast/test.sh @@ -18,7 +18,7 @@ tmp="$(mktemp -d)" echo -------------------------------------------------------------------------- echo - # Also cat the file, to make that the autocorrect applied + # Also cat the file, to make sure that the autocorrect applied cat autocorrect-useless-cast.rb rm autocorrect-useless-cast.rb @@ -41,7 +41,7 @@ echo echo -------------------------------------------------------------------------- echo - # Also cat the file, to make that the autocorrect applied + # Also cat the file, to make sure that the autocorrect applied cat autocorrect-useless-cast.rb rm autocorrect-useless-cast.rb diff --git a/test/cli/autocorrect/test.out b/test/cli/autocorrect/test.out index d41be8e4b7..a04c5717b3 100644 --- a/test/cli/autocorrect/test.out +++ b/test/cli/autocorrect/test.out @@ -24,7 +24,7 @@ autocorrect.rb:19: Method `params` does not exist on `T.class_of()` https: autocorrect.rb:4: Method `[]` does not exist on `NilClass` component of `T.nilable(String)` https://srb.help/7003 4 |foo[0] - ^^^^^^ + ^^^ Got `T.nilable(String)` originating from: autocorrect.rb:3: 3 |foo = T.let(nil, T.nilable(String)) @@ -86,18 +86,15 @@ autocorrect.rb:12: Method `bar` does not exist on `NilClass` component of `T.nil 12 |foo.bar ||= "a" ^^^^^^^ -autocorrect.rb:12: Method `bar=` does not exist on `String` component of `T.nilable(String)` https://srb.help/7003 +autocorrect.rb:12: Setter method `bar=` does not exist on `String` component of `T.nilable(String)` https://srb.help/7003 12 |foo.bar ||= "a" ^^^^^^^ Got `T.nilable(String)` originating from: autocorrect.rb:3: 3 |foo = T.let(nil, T.nilable(String)) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - https://github.com/sorbet/sorbet/tree/master/rbi/core/kernel.rbi#LCENSORED: Did you mean: `Kernel#warn` - NN | def warn(*msg); end - ^^^^^^^^^^^^^^ -autocorrect.rb:12: Method `bar=` does not exist on `NilClass` component of `T.nilable(String)` https://srb.help/7003 +autocorrect.rb:12: Setter method `bar=` does not exist on `NilClass` component of `T.nilable(String)` https://srb.help/7003 12 |foo.bar ||= "a" ^^^^^^^ Got `T.nilable(String)` originating from: @@ -129,18 +126,15 @@ autocorrect.rb:13: Method `bar` does not exist on `NilClass` component of `T.nil 13 |foo.bar &&= "a" ^^^^^^^ -autocorrect.rb:13: Method `bar=` does not exist on `String` component of `T.nilable(String)` https://srb.help/7003 +autocorrect.rb:13: Setter method `bar=` does not exist on `String` component of `T.nilable(String)` https://srb.help/7003 13 |foo.bar &&= "a" ^^^^^^^ Got `T.nilable(String)` originating from: autocorrect.rb:3: 3 |foo = T.let(nil, T.nilable(String)) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - https://github.com/sorbet/sorbet/tree/master/rbi/core/kernel.rbi#LCENSORED: Did you mean: `Kernel#warn` - NN | def warn(*msg); end - ^^^^^^^^^^^^^^ -autocorrect.rb:13: Method `bar=` does not exist on `NilClass` component of `T.nilable(String)` https://srb.help/7003 +autocorrect.rb:13: Setter method `bar=` does not exist on `NilClass` component of `T.nilable(String)` https://srb.help/7003 13 |foo.bar &&= "a" ^^^^^^^ Got `T.nilable(String)` originating from: @@ -172,18 +166,15 @@ autocorrect.rb:14: Method `bar` does not exist on `NilClass` component of `T.nil 14 |foo.bar |= "a" ^^^^^^^ -autocorrect.rb:14: Method `bar=` does not exist on `String` component of `T.nilable(String)` https://srb.help/7003 +autocorrect.rb:14: Setter method `bar=` does not exist on `String` component of `T.nilable(String)` https://srb.help/7003 14 |foo.bar |= "a" ^^^^^^^ Got `T.nilable(String)` originating from: autocorrect.rb:3: 3 |foo = T.let(nil, T.nilable(String)) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - https://github.com/sorbet/sorbet/tree/master/rbi/core/kernel.rbi#LCENSORED: Did you mean: `Kernel#warn` - NN | def warn(*msg); end - ^^^^^^^^^^^^^^ -autocorrect.rb:14: Method `bar=` does not exist on `NilClass` component of `T.nilable(String)` https://srb.help/7003 +autocorrect.rb:14: Setter method `bar=` does not exist on `NilClass` component of `T.nilable(String)` https://srb.help/7003 14 |foo.bar |= "a" ^^^^^^^ Got `T.nilable(String)` originating from: @@ -215,18 +206,15 @@ autocorrect.rb:15: Method `bar` does not exist on `NilClass` component of `T.nil 15 |foo.bar &= "a" ^^^^^^^ -autocorrect.rb:15: Method `bar=` does not exist on `String` component of `T.nilable(String)` https://srb.help/7003 +autocorrect.rb:15: Setter method `bar=` does not exist on `String` component of `T.nilable(String)` https://srb.help/7003 15 |foo.bar &= "a" ^^^^^^^ Got `T.nilable(String)` originating from: autocorrect.rb:3: 3 |foo = T.let(nil, T.nilable(String)) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - https://github.com/sorbet/sorbet/tree/master/rbi/core/kernel.rbi#LCENSORED: Did you mean: `Kernel#warn` - NN | def warn(*msg); end - ^^^^^^^^^^^^^^ -autocorrect.rb:15: Method `bar=` does not exist on `NilClass` component of `T.nilable(String)` https://srb.help/7003 +autocorrect.rb:15: Setter method `bar=` does not exist on `NilClass` component of `T.nilable(String)` https://srb.help/7003 15 |foo.bar &= "a" ^^^^^^^ Got `T.nilable(String)` originating from: @@ -240,7 +228,7 @@ autocorrect.rb:15: Method `bar=` does not exist on `NilClass` component of `T.ni autocorrect.rb:17: Method `[]` does not exist on `NilClass` component of `T.nilable(Integer)` https://srb.help/7003 17 |[1].max_by {|l,r| 1}[2] - ^^^^^^^^^^^^^^^^^^^^^^^ + ^^^ Got `T.nilable(Integer)` originating from: autocorrect.rb:17: 17 |[1].max_by {|l,r| 1}[2] diff --git a/test/cli/autocorrect/test.sh b/test/cli/autocorrect/test.sh index 9f43cb0ca9..cf43c6f40f 100755 --- a/test/cli/autocorrect/test.sh +++ b/test/cli/autocorrect/test.sh @@ -17,7 +17,7 @@ echo echo -------------------------------------------------------------------------- echo -# Also cat the file, to make that the autocorrect applied +# Also cat the file, to make sure that the autocorrect applied cat autocorrect.rb rm autocorrect.rb diff --git a/test/cli/autocorrect_and_and/autocorrect_and_and.rb b/test/cli/autocorrect_and_and/autocorrect_and_and.rb index 394608c906..a63f5d2421 100644 --- a/test/cli/autocorrect_and_and/autocorrect_and_and.rb +++ b/test/cli/autocorrect_and_and/autocorrect_and_and.rb @@ -39,3 +39,19 @@ def bar end end + +extend T::Sig + +class Auth + extend T::Sig + sig { returns(T::Boolean) } + def livemode + true + end +end + +sig { returns(T.nilable(Auth))} +def auth +end + +(auth && auth.livemode) diff --git a/test/cli/autocorrect_and_and/test.out b/test/cli/autocorrect_and_and/test.out index 38720a0051..1b848f7d6e 100644 --- a/test/cli/autocorrect_and_and/test.out +++ b/test/cli/autocorrect_and_and/test.out @@ -1,3 +1,28 @@ +autocorrect_and_and.rb:57: Call to method `livemode` after `&&` assumes result type doesn't change https://srb.help/7037 + 57 |(auth && auth.livemode) + ^^^^^^^^ + Got `T.nilable(Auth)` originating from: + autocorrect_and_and.rb:57: + 57 |(auth && auth.livemode) + ^^^^ + Type had been narrowed to `Auth` before `&&`: + autocorrect_and_and.rb:57: + 57 |(auth && auth.livemode) + ^^^^ + autocorrect_and_and.rb:57: + 57 |(auth && auth.livemode) + ^^^^ + Note: + Sorbet never assumes that a method called twice returns the same result both times. + Either factor out a variable or use `&.` + Autocorrect: Done + autocorrect_and_and.rb:57: Deleted + 57 |(auth && auth.livemode) + ^^^^^^^^ + autocorrect_and_and.rb:57: Replaced with `&.` + 57 |(auth && auth.livemode) + ^ + autocorrect_and_and.rb:12: Call to method `even?` after `&&` assumes result type doesn't change https://srb.help/7037 12 | if a.foo && a.foo.even? ^^^^^ @@ -9,6 +34,9 @@ autocorrect_and_and.rb:12: Call to method `even?` after `&&` assumes result type autocorrect_and_and.rb:12: 12 | if a.foo && a.foo.even? ^^^^^ + autocorrect_and_and.rb:12: + 12 | if a.foo && a.foo.even? + ^^^^ Note: Sorbet never assumes that a method called twice returns the same result both times. Either factor out a variable or use `&.` @@ -31,6 +59,9 @@ autocorrect_and_and.rb:17: Call to method `even?` after `&&` assumes result type autocorrect_and_and.rb:16: 16 | if a.foo && ^^^^^ + autocorrect_and_and.rb:16: + 16 | if a.foo && + 17 | a.foo.even? Note: Sorbet never assumes that a method called twice returns the same result both times. Either factor out a variable or use `&.` @@ -53,6 +84,9 @@ autocorrect_and_and.rb:22: Call to method `even?` after `&&` assumes result type autocorrect_and_and.rb:21: 21 | if foo && ^^^ + autocorrect_and_and.rb:21: + 21 | if foo && + 22 | foo.even? Note: Sorbet never assumes that a method called twice returns the same result both times. Either factor out a variable or use `&.` @@ -87,10 +121,13 @@ autocorrect_and_and.rb:36: Call to method `even?` after `&&` assumes result type autocorrect_and_and.rb:35: 35 | if a.foo && a.foo. ^^^^^ + autocorrect_and_and.rb:35: + 35 | if a.foo && a.foo. + ^^^^ Note: Sorbet never assumes that a method called twice returns the same result both times. Either factor out a variable or use `&.` -Errors: 5 +Errors: 6 -------------------------------------------------------------------------- @@ -133,3 +170,19 @@ class A end end + +extend T::Sig + +class Auth + extend T::Sig + sig { returns(T::Boolean) } + def livemode + true + end +end + +sig { returns(T.nilable(Auth))} +def auth +end + +(auth&.livemode) diff --git a/test/cli/autocorrect_and_and/test.sh b/test/cli/autocorrect_and_and/test.sh index cf8af0cc78..ab0255748e 100755 --- a/test/cli/autocorrect_and_and/test.sh +++ b/test/cli/autocorrect_and_and/test.sh @@ -17,7 +17,7 @@ echo echo -------------------------------------------------------------------------- echo -# Also cat the file, to make that the autocorrect applied +# Also cat the file, to make sure that the autocorrect applied cat autocorrect_and_and.rb rm autocorrect_and_and.rb diff --git a/test/cli/autocorrect_array_plus/test.sh b/test/cli/autocorrect_array_plus/test.sh index 00332b4c70..f5d900b244 100755 --- a/test/cli/autocorrect_array_plus/test.sh +++ b/test/cli/autocorrect_array_plus/test.sh @@ -17,7 +17,7 @@ echo echo -------------------------------------------------------------------------- echo -# Also cat the file, to make that the autocorrect applied +# Also cat the file, to make sure that the autocorrect applied cat autocorrect_array_plus.rb rm autocorrect_array_plus.rb diff --git a/test/cli/autocorrect_csend/test.sh b/test/cli/autocorrect_csend/test.sh index 79c507ff8f..b743fde5da 100755 --- a/test/cli/autocorrect_csend/test.sh +++ b/test/cli/autocorrect_csend/test.sh @@ -18,7 +18,7 @@ tmp="$(mktemp -d)" echo -------------------------------------------------------------------------- echo - # Also cat the file, to make that the autocorrect applied + # Also cat the file, to make sure that the autocorrect applied cat autocorrect_csend.rb rm autocorrect_csend.rb @@ -41,7 +41,7 @@ echo echo -------------------------------------------------------------------------- echo - # Also cat the file, to make that the autocorrect applied + # Also cat the file, to make sure that the autocorrect applied cat autocorrect_csend.rb rm autocorrect_csend.rb diff --git a/test/cli/autocorrect_method_return/test.sh b/test/cli/autocorrect_method_return/test.sh index 2c2bfef99a..8725db6dec 100755 --- a/test/cli/autocorrect_method_return/test.sh +++ b/test/cli/autocorrect_method_return/test.sh @@ -18,7 +18,7 @@ tmp="$(mktemp -d)" echo -------------------------------------------------------------------------- echo - # Also cat the file, to make that the autocorrect applied + # Also cat the file, to make sure that the autocorrect applied cat autocorrect_method_return.rb rm autocorrect_method_return.rb @@ -41,7 +41,7 @@ echo echo -------------------------------------------------------------------------- echo - # Also cat the file, to make that the autocorrect applied + # Also cat the file, to make sure that the autocorrect applied cat autocorrect_method_return.rb rm autocorrect_method_return.rb diff --git a/test/cli/autocorrect_op_asgn_must/test.sh b/test/cli/autocorrect_op_asgn_must/test.sh index 200e5d8a85..0a9bd550fa 100755 --- a/test/cli/autocorrect_op_asgn_must/test.sh +++ b/test/cli/autocorrect_op_asgn_must/test.sh @@ -18,7 +18,7 @@ tmp="$(mktemp -d)" echo -------------------------------------------------------------------------- echo - # Also cat the file, to make that the autocorrect applied + # Also cat the file, to make sure that the autocorrect applied cat autocorrect_op_asgn_must.rb rm autocorrect_op_asgn_must.rb @@ -41,7 +41,7 @@ echo echo -------------------------------------------------------------------------- echo - # Also cat the file, to make that the autocorrect applied + # Also cat the file, to make sure that the autocorrect applied cat autocorrect_op_asgn_must.rb rm autocorrect_op_asgn_must.rb diff --git a/test/cli/autogen-constant-cache/test.sh b/test/cli/autogen-constant-cache/test.sh index 6d3f961edf..527fb562aa 100755 --- a/test/cli/autogen-constant-cache/test.sh +++ b/test/cli/autogen-constant-cache/test.sh @@ -5,11 +5,11 @@ cd test/cli/autogen-constant-cache || exit 1 cp foo.rb example.rb echo "Running autogen" -../../../main/sorbet --silence-dev-message -p autogen-msgpack --autogen-version=2 --stop-after=namer --autogen-constant-cache-file cache.msgpack example.rb 2>&1 >/dev/null +../../../main/sorbet --silence-dev-message -p autogen-msgpack --autogen-version=5 --stop-after=namer --autogen-constant-cache-file cache.msgpack example.rb 2>&1 >/dev/null echo echo "Running autogen with no changes: should exit early" -../../../main/sorbet --silence-dev-message -p autogen-msgpack --autogen-version=2 --stop-after=namer --autogen-constant-cache-file cache.msgpack foo.rb --autogen-changed-files example.rb 2>&1 >/dev/null +../../../main/sorbet --silence-dev-message -p autogen-msgpack --autogen-version=5 --stop-after=namer --autogen-constant-cache-file cache.msgpack foo.rb --autogen-changed-files example.rb 2>&1 >/dev/null echo echo "Running autogen with non-constant-related changes: should exit early" @@ -18,7 +18,7 @@ def new_method puts "this method will not change the constant hash" end EOF -../../../main/sorbet --silence-dev-message -p autogen-msgpack --autogen-version=2 --stop-after=namer --autogen-constant-cache-file cache.msgpack example.rb --autogen-changed-files example.rb 2>&1 >/dev/null +../../../main/sorbet --silence-dev-message -p autogen-msgpack --autogen-version=5 --stop-after=namer --autogen-constant-cache-file cache.msgpack example.rb --autogen-changed-files example.rb 2>&1 >/dev/null echo echo "Running autogen with constant-related changes: should re-run" @@ -27,4 +27,4 @@ cat >>example.rb <&1 >/dev/null +../../../main/sorbet --silence-dev-message -p autogen-msgpack --autogen-version=5 --stop-after=namer --autogen-constant-cache-file cache.msgpack example.rb --autogen-changed-files example.rb 2>&1 >/dev/null diff --git a/test/cli/autogen-errors/test.sh b/test/cli/autogen-errors/test.sh index 03ac12ca78..1118fb7732 100755 --- a/test/cli/autogen-errors/test.sh +++ b/test/cli/autogen-errors/test.sh @@ -1,2 +1,2 @@ # shellcheck disable=SC2069 -main/sorbet --silence-dev-message -p autogen-msgpack --autogen-version=2 --stop-after=namer test/cli/autogen-errors/autogen-errors.rb 2>&1 1>/dev/null +main/sorbet --silence-dev-message -p autogen-msgpack --autogen-version=5 --stop-after=namer test/cli/autogen-errors/autogen-errors.rb 2>&1 1>/dev/null diff --git a/test/cli/autogen-path-prefix/test.out b/test/cli/autogen-path-prefix/test.out index bd679ce9f9..992d32fb34 100644 --- a/test/cli/autogen-path-prefix/test.out +++ b/test/cli/autogen-path-prefix/test.out @@ -63,4 +63,4 @@ requires: [] is_defining_ref=1 --- autogen.txt end --- -a545544be12108889c205854a8673f2f4189c396 autogen.msgpack +6dbf3772d0c8000f9c773ab3bd0e5923c27d241a autogen.msgpack diff --git a/test/cli/autogen-path-prefix/test.sh b/test/cli/autogen-path-prefix/test.sh index ca8af802e4..61b9774353 100755 --- a/test/cli/autogen-path-prefix/test.sh +++ b/test/cli/autogen-path-prefix/test.sh @@ -1,7 +1,7 @@ #!/bin/bash set -eu -main/sorbet --silence-dev-message --stop-after namer --autogen-version=4 \ +main/sorbet --silence-dev-message --stop-after namer --autogen-version=5 \ -p autogen:autogen.txt -p autogen-msgpack:autogen.msgpack \ --remove-path-prefix=test/cli/ \ test/cli/autogen-path-prefix \ diff --git a/test/cli/autogen-subclasses-ignore/test.out b/test/cli/autogen-subclasses-ignore/test.out index 59ca97f897..2ee904e51b 100644 --- a/test/cli/autogen-subclasses-ignore/test.out +++ b/test/cli/autogen-subclasses-ignore/test.out @@ -2,4 +2,4 @@ --- autogen-subclasses-ignore-relative --- class Opus::Parent - class Opus::Child + class Opus::Child test/cli/autogen-subclasses-ignore/not-ignored/not-ignored.rb diff --git a/test/cli/autogen-subclasses/child.rb b/test/cli/autogen-subclasses/child.rb new file mode 100644 index 0000000000..20ea3dd2d4 --- /dev/null +++ b/test/cli/autogen-subclasses/child.rb @@ -0,0 +1,7 @@ +# typed: true + +require_relative './parent' + +module Bopus + class Child < Parent; end +end diff --git a/test/cli/autogen-subclasses/child_again.rb b/test/cli/autogen-subclasses/child_again.rb new file mode 100644 index 0000000000..fd7bf682b1 --- /dev/null +++ b/test/cli/autogen-subclasses/child_again.rb @@ -0,0 +1,7 @@ +# typed: true + +require_relative './parent' + +module Bopus + class ChildAgain < Parent; end +end diff --git a/test/cli/autogen-subclasses/child_of_child.rb b/test/cli/autogen-subclasses/child_of_child.rb new file mode 100644 index 0000000000..f0cff7276a --- /dev/null +++ b/test/cli/autogen-subclasses/child_of_child.rb @@ -0,0 +1,7 @@ +# typed: true + +require_relative './child' + +module Bopus + class ChildOfChild < Child; end +end diff --git a/test/cli/autogen-subclasses/parent.rb b/test/cli/autogen-subclasses/parent.rb new file mode 100644 index 0000000000..0387bdc9af --- /dev/null +++ b/test/cli/autogen-subclasses/parent.rb @@ -0,0 +1,5 @@ +# typed: true + +module Bopus + class Parent; end +end diff --git a/test/cli/autogen-subclasses/test.out b/test/cli/autogen-subclasses/test.out index a22a63481e..ebdefa2e47 100644 --- a/test/cli/autogen-subclasses/test.out +++ b/test/cli/autogen-subclasses/test.out @@ -1,10 +1,14 @@ --- autogen-subclasses --- +class Bopus::Parent + class Bopus::Child child.rb + class Bopus::ChildAgain child_again.rb + class Bopus::ChildOfChild child_of_child.rb module MyMixin - class MyClass + class MyClass a.rb module Opus::Mixin - class Opus::Mixed - class Opus::MixedDescendant - module Opus::MixedModule + class Opus::Mixed a.rb + class Opus::MixedDescendant a.rb + module Opus::MixedModule a.rb class Opus::Parent - class Opus::Child - class Opus::DupChild + class Opus::Child a.rb + class Opus::DupChild a.rb diff --git a/test/cli/autogen-subclasses/test.sh b/test/cli/autogen-subclasses/test.sh index 180f533c9a..f0e352362f 100755 --- a/test/cli/autogen-subclasses/test.sh +++ b/test/cli/autogen-subclasses/test.sh @@ -8,4 +8,5 @@ main/sorbet --silence-dev-message --stop-after=namer -p autogen-subclasses \ --autogen-subclasses-parent=Opus::IDontExist \ --autogen-subclasses-parent=Opus::NeverSubclassed \ --autogen-subclasses-parent=MyMixin \ - test/cli/autogen-subclasses/a.rb + --autogen-subclasses-parent=Bopus::Parent \ + test/cli/autogen-subclasses/ diff --git a/test/cli/autogen_print_alias_scopes/alias.rb b/test/cli/autogen_print_alias_scopes/alias.rb new file mode 100644 index 0000000000..ab45c93495 --- /dev/null +++ b/test/cli/autogen_print_alias_scopes/alias.rb @@ -0,0 +1,8 @@ +# typed: strict + +module Foo + Alias = Bar + + # this ref should have name = Foo::OtherAlias::Alias, resolved = Foo::OtherAlias::Alias + DeepAlias = OtherAlias::Alias +end diff --git a/test/cli/autogen_print_alias_scopes/bar.rb b/test/cli/autogen_print_alias_scopes/bar.rb new file mode 100644 index 0000000000..7066e2e858 --- /dev/null +++ b/test/cli/autogen_print_alias_scopes/bar.rb @@ -0,0 +1,5 @@ +# typed: strict + +class Bar + Cnst = 1 +end diff --git a/test/cli/autogen_print_alias_scopes/foo.rb b/test/cli/autogen_print_alias_scopes/foo.rb new file mode 100644 index 0000000000..bd59ead284 --- /dev/null +++ b/test/cli/autogen_print_alias_scopes/foo.rb @@ -0,0 +1,9 @@ +# typed: strict + +module Foo + # this ref should have name = Foo::Alias::Cnst, resolved = Bar::Cnst + Alias::Cnst + + # this ref should have name = Foo::DeepAlias::Cnst, resolved = Bar::Cnst + DeepAlias::Cnst +end diff --git a/test/cli/autogen_print_alias_scopes/other_alias.rb b/test/cli/autogen_print_alias_scopes/other_alias.rb new file mode 100644 index 0000000000..41bbcb1859 --- /dev/null +++ b/test/cli/autogen_print_alias_scopes/other_alias.rb @@ -0,0 +1,10 @@ +# typed: strict + +module Foo + module Other + Alias = Bar + end + + OtherAlias = Other +end + diff --git a/test/cli/autogen_print_alias_scopes/test.out b/test/cli/autogen_print_alias_scopes/test.out new file mode 100644 index 0000000000..9fa975c1d3 --- /dev/null +++ b/test/cli/autogen_print_alias_scopes/test.out @@ -0,0 +1,198 @@ +# ParsedFile: test/cli/autogen_print_alias_scopes/alias.rb +requires: [] +## defs: +[def id=0] + type=module + defines_behavior=0 + is_empty=0 +[def id=1] + type=module + defines_behavior=0 + is_empty=0 + defining_ref=[Foo] +[def id=2] + type=alias + defines_behavior=1 + is_empty=0 + defining_ref=[Alias] + aliased_ref=[Bar] +[def id=3] + type=alias + defines_behavior=1 + is_empty=0 + defining_ref=[DeepAlias] + aliased_ref=[Foo OtherAlias Alias] +## refs: +[ref id=0] + scope=[] + name=[Foo] + nesting=[] + resolved=[Foo] + loc=test/cli/autogen_print_alias_scopes/alias.rb:3 + is_defining_ref=1 +[ref id=1] + scope=[Foo] + name=[Alias] + nesting=[[Foo]] + resolved=[Foo Alias] + loc=test/cli/autogen_print_alias_scopes/alias.rb:4 + is_defining_ref=1 +[ref id=2] + scope=[Foo] + name=[Bar] + nesting=[[Foo]] + resolved=[Bar] + loc=test/cli/autogen_print_alias_scopes/alias.rb:4 + is_defining_ref=0 +[ref id=3] + scope=[Foo] + name=[DeepAlias] + nesting=[[Foo]] + resolved=[Foo DeepAlias] + loc=test/cli/autogen_print_alias_scopes/alias.rb:7 + is_defining_ref=1 +[ref id=4] + scope=[Foo] + name=[Foo OtherAlias Alias] + nesting=[[Foo]] + resolved=[Foo Other Alias] + loc=test/cli/autogen_print_alias_scopes/alias.rb:7 + is_defining_ref=0 +# ParsedFile: test/cli/autogen_print_alias_scopes/bar.rb +requires: [] +## defs: +[def id=0] + type=module + defines_behavior=0 + is_empty=0 +[def id=1] + type=class + defines_behavior=0 + is_empty=0 + defining_ref=[Bar] +[def id=2] + type=casgn + defines_behavior=1 + is_empty=0 + defining_ref=[Cnst] +## refs: +[ref id=0] + scope=[] + name=[Bar] + nesting=[] + resolved=[Bar] + loc=test/cli/autogen_print_alias_scopes/bar.rb:3 + is_defining_ref=1 +[ref id=1] + scope=[Bar] + name=[Cnst] + nesting=[[Bar]] + resolved=[Bar Cnst] + loc=test/cli/autogen_print_alias_scopes/bar.rb:4 + is_defining_ref=1 +# ParsedFile: test/cli/autogen_print_alias_scopes/foo.rb +requires: [] +## defs: +[def id=0] + type=module + defines_behavior=0 + is_empty=0 +[def id=1] + type=module + defines_behavior=1 + is_empty=0 + defining_ref=[Foo] +## refs: +[ref id=0] + scope=[] + name=[Foo] + nesting=[] + resolved=[Foo] + loc=test/cli/autogen_print_alias_scopes/foo.rb:3 + is_defining_ref=1 +[ref id=1] + scope=[Foo] + name=[Foo Alias Cnst] + nesting=[[Foo]] + resolved=[Bar Cnst] + loc=test/cli/autogen_print_alias_scopes/foo.rb:5 + is_defining_ref=0 +[ref id=2] + scope=[Foo] + name=[Foo DeepAlias Cnst] + nesting=[[Foo]] + resolved=[Bar Cnst] + loc=test/cli/autogen_print_alias_scopes/foo.rb:8 + is_defining_ref=0 +# ParsedFile: test/cli/autogen_print_alias_scopes/other_alias.rb +requires: [] +## defs: +[def id=0] + type=module + defines_behavior=0 + is_empty=0 +[def id=1] + type=module + defines_behavior=0 + is_empty=0 + defining_ref=[Foo] +[def id=2] + type=module + defines_behavior=0 + is_empty=0 + defining_ref=[Other] +[def id=3] + type=alias + defines_behavior=1 + is_empty=0 + defining_ref=[Alias] + aliased_ref=[Bar] +[def id=4] + type=alias + defines_behavior=1 + is_empty=0 + defining_ref=[OtherAlias] + aliased_ref=[Other] +## refs: +[ref id=0] + scope=[] + name=[Foo] + nesting=[] + resolved=[Foo] + loc=test/cli/autogen_print_alias_scopes/other_alias.rb:3 + is_defining_ref=1 +[ref id=1] + scope=[Foo] + name=[Other] + nesting=[[Foo]] + resolved=[Foo Other] + loc=test/cli/autogen_print_alias_scopes/other_alias.rb:4 + is_defining_ref=1 +[ref id=2] + scope=[Foo Other] + name=[Alias] + nesting=[[Foo Other] [Foo]] + resolved=[Foo Other Alias] + loc=test/cli/autogen_print_alias_scopes/other_alias.rb:5 + is_defining_ref=1 +[ref id=3] + scope=[Foo Other] + name=[Bar] + nesting=[[Foo Other] [Foo]] + resolved=[Bar] + loc=test/cli/autogen_print_alias_scopes/other_alias.rb:5 + is_defining_ref=0 +[ref id=4] + scope=[Foo] + name=[OtherAlias] + nesting=[[Foo]] + resolved=[Foo OtherAlias] + loc=test/cli/autogen_print_alias_scopes/other_alias.rb:8 + is_defining_ref=1 +[ref id=5] + scope=[Foo] + name=[Other] + nesting=[[Foo]] + resolved=[Foo Other] + loc=test/cli/autogen_print_alias_scopes/other_alias.rb:8 + is_defining_ref=0 diff --git a/test/cli/autogen_print_alias_scopes/test.sh b/test/cli/autogen_print_alias_scopes/test.sh new file mode 100755 index 0000000000..2c24c82880 --- /dev/null +++ b/test/cli/autogen_print_alias_scopes/test.sh @@ -0,0 +1,6 @@ +#!/bin/bash +set -eu + +main/sorbet --silence-dev-message --stop-after namer \ + -p autogen \ + test/cli/autogen_print_alias_scopes/*.rb diff --git a/test/cli/autogen_print_rbi/behavior_allowed.rbi b/test/cli/autogen_print_rbi/behavior_allowed.rbi index f1f6c87288..fd2f31ab61 100644 --- a/test/cli/autogen_print_rbi/behavior_allowed.rbi +++ b/test/cli/autogen_print_rbi/behavior_allowed.rbi @@ -1,6 +1,8 @@ # typed: strict class BehaviorRBI + C = 1 + sig {void} def bar; end end diff --git a/test/cli/autogen_print_rbi/test.out b/test/cli/autogen_print_rbi/test.out index da27f498a2..310fd62077 100644 --- a/test/cli/autogen_print_rbi/test.out +++ b/test/cli/autogen_print_rbi/test.out @@ -1,46 +1,4 @@ -Version 3: -# ParsedFile: test/cli/autogen_print_rbi/a.rb -requires: [] -## defs: -[def id=0] - type=module - defines_behavior=0 - is_empty=0 -[def id=1] - type=module - defines_behavior=0 - is_empty=0 - defining_ref=[A B] -[def id=2] - type=class - defines_behavior=1 - is_empty=0 - defining_ref=[C] -## refs: -[ref id=0] - scope=[] - name=[A B] - nesting=[] - resolved=[A B] - loc=test/cli/autogen_print_rbi/a.rb:3 - is_defining_ref=1 -[ref id=1] - scope=[A B] - name=[C] - nesting=[[A B]] - resolved=[A B C] - loc=test/cli/autogen_print_rbi/a.rb:4 - is_defining_ref=1 -[ref id=2] - scope=[A B C] - name=[FromRBI A] - nesting=[[A B C] [A B]] - resolved=[FromRBI A] - loc=test/cli/autogen_print_rbi/a.rb:5 - is_defining_ref=0 - - -Version 4: +Version 5: # ParsedFile: test/cli/autogen_print_rbi/a.rb requires: [] ## defs: @@ -126,6 +84,11 @@ requires: [] defines_behavior=1 is_empty=0 defining_ref=[BehaviorRBI] +[def id=2] + type=casgn + defines_behavior=1 + is_empty=0 + defining_ref=[C] ## refs: [ref id=0] scope=[] @@ -134,62 +97,12 @@ requires: [] resolved=[BehaviorRBI] loc=test/cli/autogen_print_rbi/behavior_allowed.rbi:3 is_defining_ref=1 +[ref id=1] + scope=[BehaviorRBI] + name=[C] + nesting=[[BehaviorRBI]] + resolved=[BehaviorRBI C] + loc=test/cli/autogen_print_rbi/behavior_allowed.rbi:4 + is_defining_ref=1 -f3b9f8c31a343a60665c06f959a48dbdceef757c autogen3.msgpack -bd4da471dd4868384af366a23bed507c32c6f98b autogen4.msgpack - -39a40,93 -> # ParsedFile: test/cli/autogen_print_rbi/b.rbi -> requires: [] -> ## defs: -> [def id=0] -> type=module -> defines_behavior=0 -> is_empty=0 -> [def id=1] -> type=class -> defines_behavior=0 -> is_empty=0 -> defining_ref=[FromRBI] -> ## refs: -> [ref id=0] -> scope=[] -> name=[FromRBI] -> nesting=[] -> resolved=[FromRBI] -> loc=test/cli/autogen_print_rbi/b.rbi:3 -> is_defining_ref=1 -> [ref id=1] -> scope=[FromRBI] -> name=[A] -> nesting=[[FromRBI]] -> resolved=[FromRBI A] -> loc=test/cli/autogen_print_rbi/b.rbi:4 -> is_defining_ref=0 -> [ref id=2] -> scope=[FromRBI] -> name=[Integer] -> nesting=[[FromRBI]] -> resolved=[Integer] -> loc=test/cli/autogen_print_rbi/b.rbi:4 -> is_defining_ref=0 -> # ParsedFile: test/cli/autogen_print_rbi/behavior_allowed.rbi -> requires: [] -> ## defs: -> [def id=0] -> type=module -> defines_behavior=0 -> is_empty=0 -> [def id=1] -> type=class -> defines_behavior=1 -> is_empty=0 -> defining_ref=[BehaviorRBI] -> ## refs: -> [ref id=0] -> scope=[] -> name=[BehaviorRBI] -> nesting=[] -> resolved=[BehaviorRBI] -> loc=test/cli/autogen_print_rbi/behavior_allowed.rbi:3 -> is_defining_ref=1 +3edd3bac9519dd6fb62387f94cf4eeac0ff5d33e autogen5.msgpack diff --git a/test/cli/autogen_print_rbi/test.sh b/test/cli/autogen_print_rbi/test.sh index af95725150..f1a74b45f7 100755 --- a/test/cli/autogen_print_rbi/test.sh +++ b/test/cli/autogen_print_rbi/test.sh @@ -1,24 +1,12 @@ #!/bin/bash set -eu -echo "Version 3:" -main/sorbet --silence-dev-message --stop-after namer --autogen-version=3 \ - -p autogen:autogen3.txt -p autogen-msgpack:autogen3.msgpack \ +echo "Version 5:" +main/sorbet --silence-dev-message --stop-after namer --autogen-version=5 \ + -p autogen:autogen5.txt -p autogen-msgpack:autogen5.msgpack --autogen-behavior-allowed-in-rbi-files-paths="test/cli/autogen_print_rbi/behavior_allowed.rbi" \ test/cli/autogen_print_rbi/*.{rb,rbi} -cat autogen3.txt +cat autogen5.txt echo -echo -echo "Version 4:" -main/sorbet --silence-dev-message --stop-after namer --autogen-version=4 \ - -p autogen:autogen4.txt -p autogen-msgpack:autogen4.msgpack --autogen-behavior-allowed-in-rbi-files-paths="test/cli/autogen_print_rbi/behavior_allowed.rbi" \ - test/cli/autogen_print_rbi/*.{rb,rbi} - -cat autogen4.txt - -echo -shasum autogen3.msgpack autogen4.msgpack - -echo -diff autogen3.txt autogen4.txt +shasum autogen5.msgpack diff --git a/test/cli/cache-dir-dne/test.out b/test/cli/cache-dir-dne/test.out index d660319965..3d85d31b20 100644 --- a/test/cli/cache-dir-dne/test.out +++ b/test/cli/cache-dir-dne/test.out @@ -1 +1,2 @@ -'nope2' does not exist. When using --cache-dir, create the directory before hand. +No errors! Great job. +'nope3/subdir' is not a directory, and so cannot store the Sorbet disk cache. diff --git a/test/cli/cache-dir-dne/test.sh b/test/cli/cache-dir-dne/test.sh index f8362235b3..0829da3e5c 100755 --- a/test/cli/cache-dir-dne/test.sh +++ b/test/cli/cache-dir-dne/test.sh @@ -1,2 +1,12 @@ #!/bin/bash +set -euo pipefail + +trap 'rm -rf nope2 nope3' EXIT + main/sorbet --silence-dev-message --cache-dir=nope2 -e '0' 2>&1 + +touch nope3 +if main/sorbet --silence-dev-message --cache-dir=nope3/subdir -e 0 2>&1; then + 2>&1 echo 'Expected to fail!' + exit 1 +fi diff --git a/test/cli/call_on_unbounded_type_member/test.sh b/test/cli/call_on_unbounded_type_member/test.sh index aba90b81aa..8c11b01fd8 100755 --- a/test/cli/call_on_unbounded_type_member/test.sh +++ b/test/cli/call_on_unbounded_type_member/test.sh @@ -18,7 +18,7 @@ tmp="$(mktemp -d)" echo -------------------------------------------------------------------------- echo - # Also cat the file, to make that the autocorrect applied + # Also cat the file, to make sure that the autocorrect applied cat test.rb rm test.rb @@ -41,7 +41,7 @@ echo echo -------------------------------------------------------------------------- echo - # Also cat the file, to make that the autocorrect applied + # Also cat the file, to make sure that the autocorrect applied cat test.rb rm test.rb diff --git a/test/cli/class_of-any/test.out b/test/cli/class_of-any/test.out index 98249f4a80..e0370c8c3a 100644 --- a/test/cli/class_of-any/test.out +++ b/test/cli/class_of-any/test.out @@ -9,6 +9,13 @@ class_of-any.rb:9: `T.class_of` must wrap each individual class type, not the ou class_of-any.rb:12: `T.class_of` needs a class or module as its argument https://srb.help/5004 12 |sig { returns(T.class_of(T.any(A, D))) } ^^^^^^^^^^^^^^^^^^^^^^^ + Note: + You may wish to use `T::Class`, which doesn't have this restriction. + For more information, see https://sorbet.org/docs/class-of#tclass-vs-tclass_of + Autocorrect: Done + class_of-any.rb:12: Replaced with `T::Class[T.any(A, D)]` + 12 |sig { returns(T.class_of(T.any(A, D))) } + ^^^^^^^^^^^^^^^^^^^^^^^ Errors: 2 -------------------------------------------------------------------------- @@ -24,5 +31,5 @@ class D < A; end sig { returns(T.any(T.class_of(B), T.class_of(C), T.class_of(A))) } def foo; end -sig { returns(T.class_of(T.any(A, D))) } +sig { returns(T::Class[T.any(A, D)]) } def bar; end diff --git a/test/cli/class_of-any/test.sh b/test/cli/class_of-any/test.sh index 84818c3ae4..766598a47a 100755 --- a/test/cli/class_of-any/test.sh +++ b/test/cli/class_of-any/test.sh @@ -16,7 +16,7 @@ echo echo -------------------------------------------------------------------------- echo -# Also cat the file, to make that the autocorrect applied +# Also cat the file, to make sure that the autocorrect applied cat class_of-any.rb rm class_of-any.rb diff --git a/test/cli/compiler/BUILD b/test/cli/compiler/BUILD deleted file mode 100644 index 671ed86b2c..0000000000 --- a/test/cli/compiler/BUILD +++ /dev/null @@ -1,18 +0,0 @@ -load(":cli_test.bzl", "cli_tests") - -cli_tests( - name = "compiler", - tags = ["compiler"], - tests = glob(["*/test.sh"]), -) - -sh_binary( - name = "run_test", - srcs = ["run.sh"], - data = [ - "//compiler:sorbet", - "@sorbet_ruby_2_7_for_compiler//:ruby", - ], - tags = ["compiler"], - deps = ["@bazel_tools//tools/bash/runfiles"], -) diff --git a/test/cli/compiler/README.md b/test/cli/compiler/README.md deleted file mode 100644 index b9be841b8f..0000000000 --- a/test/cli/compiler/README.md +++ /dev/null @@ -1,37 +0,0 @@ -# Running the CLI tests - -``` -% bazel test //test/cli -``` - -# Adding a new CLI test - -Run the `new_test.sh` script to add a new test skeleton: - -``` -% ./test/cli/new_test.sh foo -[ .. ] Creating ./test/cli/foo -[ .. ] Adding expected.out -[ .. ] Adding test.sh -[ OK ] Done -% cat test/cli/foo/test.sh -#!/usr/bin/env bash - -set -euo pipefail - -# NOTE: Sorbet and Sorbet Ruby are already in the PATH -``` - -# Updating exp files - -You can update exp files by running the `update_cli_exp_files.sh` script: - -``` -% ./tools/scripts/remote-script test/cli/update_cli_exp_files.sh -[ .. ] Building the exp test outputs -[ .. ] Updating exp files -### BEGIN SYNCBACK ### -test/cli/help/expected.out -### END SYNCBACK ### -[ OK ] Done -``` diff --git a/test/cli/compiler/cli_test.bzl b/test/cli/compiler/cli_test.bzl deleted file mode 100644 index b14b3d0031..0000000000 --- a/test/cli/compiler/cli_test.bzl +++ /dev/null @@ -1,110 +0,0 @@ -def cli_tests(name, tests = [], tags = []): - test_names = [] - actual_names = [] - for path in tests: - test_dir = path.rsplit("/")[0] - test_name = "test_{}".format(test_dir) - run_test_name = "run_{}".format(test_dir) - test_srcs = "srcs_{}".format(test_dir) - expected_out = "expected_{}".format(test_dir) - actual_out = "{}/actual.out".format(test_dir) - - native.filegroup( - name = test_srcs, - srcs = native.glob(["{}/*".format(test_dir)], exclude = ["expected.out"]), - ) - - native.filegroup( - name = expected_out, - srcs = ["{}/expected.out".format(test_dir)], - ) - - _cli_test_output( - name = run_test_name, - out = actual_out, - path = test_dir, - srcs = test_srcs, - tags = tags, - ) - - _cli_test( - name = test_name, - output = run_test_name, - expected = expected_out, - tags = tags, - ) - - test_names.append(test_name) - actual_names.append(actual_out) - - native.filegroup( - name = "actual_{}".format(name), - srcs = actual_names, - tags = tags, - ) - - native.test_suite( - name = name, - tests = test_names, - ) - -CliTestOutput = provider(fields = ["output_file"]) - -def _cli_test_output_impl(ctx): - test_dir = "{}/{}".format(ctx.label.package, ctx.attr.path) - - output = ctx.outputs.out - - ctx.actions.run( - outputs = [output], - inputs = ctx.files.srcs, - executable = ctx.executable._run_test, - arguments = [ - test_dir, - output.path, - ], - ) - - return [CliTestOutput(output_file = output)] - -_cli_test_output = rule( - implementation = _cli_test_output_impl, - attrs = { - "srcs": attr.label(), - "out": attr.output(), - "path": attr.string(), - "_run_test": attr.label(default = ":run_test", cfg = "target", executable = True), - }, - provides = [CliTestOutput], -) - -def _cli_test_impl(ctx): - test_name = ctx.label.name - - script = ctx.actions.declare_file(test_name) - - output = ctx.attr.output[CliTestOutput].output_file - - ctx.actions.expand_template( - template = ctx.file._validate_template, - output = script, - is_executable = True, - substitutions = { - "{expected_output}": ctx.file.expected.short_path, - "{test_output}": output.short_path, - }, - ) - - runfiles = ctx.runfiles(files = [ctx.file.expected, output]) - - return [DefaultInfo(executable = script, runfiles = runfiles)] - -_cli_test = rule( - implementation = _cli_test_impl, - attrs = { - "output": attr.label(providers = [CliTestOutput]), - "expected": attr.label(allow_single_file = True), - "_validate_template": attr.label(default = ":validate.sh.tpl", allow_single_file = True), - }, - test = True, -) diff --git a/test/cli/compiler/ifunc-pre-pop-regression/expected.out b/test/cli/compiler/ifunc-pre-pop-regression/expected.out deleted file mode 100644 index 1ca006e4b7..0000000000 --- a/test/cli/compiler/ifunc-pre-pop-regression/expected.out +++ /dev/null @@ -1,2 +0,0 @@ -in many_args -done diff --git a/test/cli/compiler/ifunc-pre-pop-regression/ifunc-pre-pop-regression.out b/test/cli/compiler/ifunc-pre-pop-regression/ifunc-pre-pop-regression.out deleted file mode 100644 index 1ca006e4b7..0000000000 --- a/test/cli/compiler/ifunc-pre-pop-regression/ifunc-pre-pop-regression.out +++ /dev/null @@ -1,2 +0,0 @@ -in many_args -done diff --git a/test/cli/compiler/ifunc-pre-pop-regression/ifunc-pre-pop-regression.rb b/test/cli/compiler/ifunc-pre-pop-regression/ifunc-pre-pop-regression.rb deleted file mode 100644 index ee5e0fea02..0000000000 --- a/test/cli/compiler/ifunc-pre-pop-regression/ifunc-pre-pop-regression.rb +++ /dev/null @@ -1,35 +0,0 @@ -# This is a regression test for a stack corruption bug introduced by -# https://github.com/stripe/sorbet_llvm/pull/437 (since reverted). - -# This function takes "a lot" of arguments. -def many_args( - a0,b0,c0,d0,e0,f0,g0,h0,i0,j0,k0,l0,m0,n0,o0,p0,q0,r0,s0,t0,u0,v0,w0,x0,y0,z0, - a1,b1,c1,d1,e1,f1,g1,h1,i1,j1,k1,l1,m1,n1,o1,p1,q1,r1,s1,t1,u1,v1,w1,x1,y1,z1, - a2,b2,c2,d2,e2,f2,g2,h2,i2,j2,k2,l2,m2,n2,o2,p2,q2,r2,s2,t2,u2,v2,w2,x2,y2,z2 - ) - puts "in many_args" -end - -# Let's call it once, to ~fill the stack area with object references. -class C ; end -def c - C.new -end - -many_args(c,c,c,c,c,c,c,c,c,c,c,c,c,c,c,c,c,c,c,c,c,c,c,c,c,c,c,c,c,c,c,c,c,c, - c,c,c,c,c,c,c,c,c,c,c,c,c,c,c,c,c,c,c,c,c,c,c,c,c,c,c,c,c,c,c,c,c,c, - c,c,c,c,c,c,c,c,c,c) - -# GC once, to make sure all the objects that were on many_args's stack get -# freed. -GC.start - -# Now 'each_slice' will invoke 'each' with an IFUNC-type handler and a "gap" in -# the stack which will probably contain a stale reference to a freed object. If -# gc happens under those circumstances, it will trigger a "try to mark T_NONE" -# assertion during the mark phase. -["whatever"].each_slice(1) do |_| - GC.start -end - -puts "done" diff --git a/test/cli/compiler/ifunc-pre-pop-regression/ifunc-pre-pop-regression.sh b/test/cli/compiler/ifunc-pre-pop-regression/ifunc-pre-pop-regression.sh deleted file mode 100755 index 77cfa0199c..0000000000 --- a/test/cli/compiler/ifunc-pre-pop-regression/ifunc-pre-pop-regression.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -ruby ifunc-pre-pop-regression.rb diff --git a/test/cli/compiler/ifunc-pre-pop-regression/test.sh b/test/cli/compiler/ifunc-pre-pop-regression/test.sh deleted file mode 100755 index 77cfa0199c..0000000000 --- a/test/cli/compiler/ifunc-pre-pop-regression/test.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -ruby ifunc-pre-pop-regression.rb diff --git a/test/cli/compiler/new_test.sh b/test/cli/compiler/new_test.sh deleted file mode 100755 index 00b32643fe..0000000000 --- a/test/cli/compiler/new_test.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -test_dir="$(dirname "${BASH_SOURCE[0]}")" -root_dir="${test_dir}/../.." - -# shellcheck source=SCRIPTDIR/../../logging.sh -source "${root_dir}/test/logging.sh" - -usage() { - info "Usage: ./new_test.sh " - exit 1 -} - -new_test_name="${1/\//-}" -if [[ "${new_test_name}" == "" ]]; then - usage -fi - -new_test_path="${test_dir}/${new_test_name}" -if [[ -d "${new_test_path}" ]]; then - error "Error: CLI test \"${new_test_name}\" already exists" - usage -fi - -info "Creating ${new_test_path}" -mkdir "${new_test_path}" - -info "Adding expected.out" -touch "${new_test_path}/expected.out" - -info "Adding test.sh" -cat > "${new_test_path}/test.sh" << EOF -#!/usr/bin/env bash - -set -euo pipefail - -# NOTE: Sorbet and Sorbet Ruby are already in the PATH -EOF -chmod +x "${new_test_path}/test.sh" - -success "Done" diff --git a/test/cli/compiler/output-flags/expected.out b/test/cli/compiler/output-flags/expected.out deleted file mode 100644 index 7c94c2a017..0000000000 --- a/test/cli/compiler/output-flags/expected.out +++ /dev/null @@ -1,13 +0,0 @@ -No errors! Great job. -.so but no IR output... -No errors! Great job. -Files in $sodir: -./hello.rb.so -.so and IR output... -No errors! Great job. -Files in $sodir: -./hello.rb.so -Files in $irdir: -./hello.rb.ll -./hello.rb.lowered.ll -./hello.rb.opt.ll diff --git a/test/cli/compiler/output-flags/hello.rb b/test/cli/compiler/output-flags/hello.rb deleted file mode 100644 index f0911ddf83..0000000000 --- a/test/cli/compiler/output-flags/hello.rb +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -puts "hi" diff --git a/test/cli/compiler/output-flags/test.sh b/test/cli/compiler/output-flags/test.sh deleted file mode 100755 index 640be07d44..0000000000 --- a/test/cli/compiler/output-flags/test.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -# Neither IR nor .so output (can't really test that it _hasn't_ output anything anywhere, but this is here for good -# measure). -sorbet --silence-dev-message hello.rb - -cleanup() { - if [ -n "${sodir:-}" ]; then - rm -rf "$sodir" - fi - - if [ -n "${irdir:-}" ]; then - rm -rf "$irdir" - fi -} - -trap cleanup EXIT - -echo ".so but no IR output..." - -sodir="$(mktemp -d)" -sorbet --silence-dev-message --compiled-out-dir="$sodir" hello.rb - -echo "Files in \$sodir:" -pushd "$sodir" >/dev/null -find . -type f -print | sort -popd >/dev/null - -rm -rf "$sodir" - -echo ".so and IR output..." - -sodir="$(mktemp -d)" -irdir="$(mktemp -d)" -sorbet --silence-dev-message --compiled-out-dir="$sodir" --llvm-ir-dir="$irdir" hello.rb - -echo "Files in \$sodir:" -pushd "$sodir" >/dev/null -find . -type f -print | sort -popd >/dev/null - -echo "Files in \$irdir:" -pushd "$irdir" >/dev/null -find . -type f -print | sort -popd >/dev/null - -rm -rf "$sodir" -rm -rf "$irdir" \ No newline at end of file diff --git a/test/cli/compiler/run.sh b/test/cli/compiler/run.sh deleted file mode 100755 index 80969103aa..0000000000 --- a/test/cli/compiler/run.sh +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -# --- begin runfiles.bash initialization --- {{{ -# Copy-pasted from Bazel's Bash runfiles library https://github.com/bazelbuild/bazel/blob/defd737761be2b154908646121de47c30434ed51/tools/bash/runfiles/runfiles.bash -if [[ ! -d "${RUNFILES_DIR:-/dev/null}" && ! -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then - if [[ -f "$0.runfiles_manifest" ]]; then - export RUNFILES_MANIFEST_FILE="$0.runfiles_manifest" - elif [[ -f "$0.runfiles/MANIFEST" ]]; then - export RUNFILES_MANIFEST_FILE="$0.runfiles/MANIFEST" - elif [[ -f "$0.runfiles/bazel_tools/tools/bash/runfiles/runfiles.bash" ]]; then - export RUNFILES_DIR="$0.runfiles" - fi -fi -if [[ -f "${RUNFILES_DIR:-/dev/null}/bazel_tools/tools/bash/runfiles/runfiles.bash" ]]; then - # shellcheck disable=SC1091 - source "${RUNFILES_DIR}/bazel_tools/tools/bash/runfiles/runfiles.bash" -elif [[ -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then - # shellcheck disable=SC1090 - source "$(grep -m1 "^bazel_tools/tools/bash/runfiles/runfiles.bash " \ - "$RUNFILES_MANIFEST_FILE" | cut -d ' ' -f 2-)" -else - echo >&2 "ERROR: cannot find @bazel_tools//tools/bash/runfiles:runfiles.bash" - exit 1 -fi -# --- end runfiles.bash initialization --- }}} - -root="$PWD" - -test_path=$1 -test_output="${root}/$2" - -# Put sorbet into the path so that it's accessible from the cli test -sorbet="$(dirname "$(rlocation com_stripe_ruby_typer/compiler/sorbet)")" -if ! [[ "$sorbet" = /* ]]; then - sorbet="${root}/${sorbet}" -fi - -# TODO(aprocter): It feels like we should be able to say -# $(rlocation sorbet_ruby_2_7_for_compiler/ruby) here, but when we do that it triggers some -# weird runfiles-related behavior in the wrapper script that rlocation winds up -# pointing us to (the wrapper script exits with "ERROR: cannot find -# @bazel_tools//tools/bash/runfiles:runfiles.bash"). As a workaround, we go -# directly to the binary (sorbet_ruby_2_7_for_compiler/toolchain/bin/ruby) instead of the -# shell wrapper (sorbet_ruby_2_7_for_compiler/ruby). -sorbet_ruby="$(dirname "$(rlocation sorbet_ruby_2_7_for_compiler/toolchain/bin/ruby)")" -if ! [[ "$sorbet_ruby" = /* ]]; then - sorbet_ruby="${root}/${sorbet_ruby}" -fi - -export PATH="${sorbet}:${sorbet_ruby}:$PATH" - -# Run the test -cd "${test_path}" -if ! ./test.sh > "${test_output}" 2>&1; then - cat "${test_output}" - exit 1 -fi diff --git a/test/cli/compiler/simple/expected.out b/test/cli/compiler/simple/expected.out deleted file mode 100644 index 190842ac0b..0000000000 --- a/test/cli/compiler/simple/expected.out +++ /dev/null @@ -1,8 +0,0 @@ -👋 Hey there! Heads up that this is not a release build of sorbet. -Release builds are faster and more well-supported by the Sorbet team. -Check out the README to learn how to build Sorbet in release mode. -To forcibly silence this error, either pass --silence-dev-message, -or set SORBET_SILENCE_DEV_MESSAGE=1 in your shell environment. - -No errors! Great job. -Hello, world diff --git a/test/cli/compiler/simple/test.sh b/test/cli/compiler/simple/test.sh deleted file mode 100755 index da12b0a2af..0000000000 --- a/test/cli/compiler/simple/test.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -sorbet -e 'puts "Hello, world"' -ruby -e 'puts "Hello, world"' diff --git a/test/cli/compiler/update_cli_exp_files.sh b/test/cli/compiler/update_cli_exp_files.sh deleted file mode 100755 index 46c1ffad79..0000000000 --- a/test/cli/compiler/update_cli_exp_files.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -test_dir="$(dirname "${BASH_SOURCE[0]}")" -root_dir="${test_dir}/../../.." - -# shellcheck source=SCRIPTDIR/../../logging.sh -source "${root_dir}/test/logging.sh" - -info "Building the exp test outputs" -bazel build //test/cli/compiler:actual_compiler > /dev/null 2>&1 - -info "Updating exp files" - -if [[ -n "${EMIT_SYNCBACK:-}" ]]; then - echo '### BEGIN SYNCBACK ###' -fi - -bazel cquery 'filter("\.out$", deps("//test/cli/compiler:actual_compiler", 1))' 2>/dev/null | \ - cut -d ' ' -f 1 | \ - while read -r target; do - target="${target#//}" - target="${target/://}" - - output="${target%actual.out}expected.out" - - echo "${output}" - - cp "bazel-bin/${target}" "${output}" - done - -if [[ -n "${EMIT_SYNCBACK:-}" ]]; then - echo '### END SYNCBACK ###' -fi - -success "Done" diff --git a/test/cli/compiler/validate.sh.tpl b/test/cli/compiler/validate.sh.tpl deleted file mode 100644 index bb969381da..0000000000 --- a/test/cli/compiler/validate.sh.tpl +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -diff -u "{expected_output}" "{test_output}" diff --git a/test/cli/dedup_loc/test.out b/test/cli/dedup_loc/test.out index 49b115608a..4452c37b34 100644 --- a/test/cli/dedup_loc/test.out +++ b/test/cli/dedup_loc/test.out @@ -5,7 +5,4 @@ test/cli/dedup_loc/dedup_loc.rb:5: Argument does not have asserted type `NilClas test/cli/dedup_loc/dedup_loc.rb:4: 4 |rescue Exception => e ^^^^^^ - test/cli/dedup_loc/dedup_loc.rb:4: - 4 |rescue Exception => e - ^^^^^^^^^ Errors: 1 diff --git a/test/cli/deprecated-enum/test.sh b/test/cli/deprecated-enum/test.sh index e55e0fcd93..e60633057b 100755 --- a/test/cli/deprecated-enum/test.sh +++ b/test/cli/deprecated-enum/test.sh @@ -17,7 +17,7 @@ echo echo -------------------------------------------------------------------------- echo -# Also cat the file, to make that the autocorrect applied +# Also cat the file, to make sure that the autocorrect applied cat deprecated-enum.rb rm deprecated-enum.rb diff --git a/test/cli/detailed-errors-completeness/test.out b/test/cli/detailed-errors-completeness/test.out new file mode 100644 index 0000000000..7371275317 --- /dev/null +++ b/test/cli/detailed-errors-completeness/test.out @@ -0,0 +1,267 @@ +test.rb:65: The `lower` type bound `[Lower]` must be a supertype of the parent's `lower` type bound `[Middle]` for type_member `X` https://srb.help/5053 + 65 | X = type_member {{upper: [Upper], lower: [Lower] }} + ^^^^^^^ + test.rb:47: `A::X` defined in parent here + 47 | X = type_member {{upper: [Middle], lower: [Middle] }} + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Detailed explanation: + `Middle` is not a subtype of `Lower` for index `0` of `1`-tuple + Autocorrect: Use `-a` to autocorrect + test.rb:65: Replace with `[Middle]` + 65 | X = type_member {{upper: [Upper], lower: [Lower] }} + ^^^^^^^ + +test.rb:65: The `upper` type bound `[Upper]` must be a subtype of the parent's `upper` type bound `[Middle]` for type_member `X` https://srb.help/5053 + 65 | X = type_member {{upper: [Upper], lower: [Lower] }} + ^^^^^^^ + test.rb:47: `A::X` defined in parent here + 47 | X = type_member {{upper: [Middle], lower: [Middle] }} + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Detailed explanation: + `Upper` is not a subtype of `Middle` for index `0` of `1`-tuple + Autocorrect: Use `-a` to autocorrect + test.rb:65: Replace with `[Middle]` + 65 | X = type_member {{upper: [Upper], lower: [Lower] }} + ^^^^^^^ + +test.rb:93: The `fixed` type bound `[Middle]` must be equivalent to the parent's `fixed` type bound `[Upper]` for type_member `X` https://srb.help/5053 + 93 | X = type_member {{ fixed: [Middle] }} + ^^^^^^^^ + test.rb:87: `C::X` defined in parent here + 87 | X = type_member {{ fixed: [Upper] }} + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Detailed explanation: + `Upper` is not a subtype of `Middle` for index `0` of `1`-tuple + Autocorrect: Use `-a` to autocorrect + test.rb:93: Replace with `[Upper]` + 93 | X = type_member {{ fixed: [Middle] }} + ^^^^^^^^ + +test.rb:99: The `lower` type bound `[Upper]` is not a subtype of the `upper` type bound `[Lower]` for `E::X` https://srb.help/5052 + 99 | X = type_member {{upper: [Lower], lower: [Upper] }} + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Detailed explanation: + `Upper` is not a subtype of `Lower` for index `0` of `1`-tuple + +test.rb:68: Parameter `x` of type `[String]` not compatible with type of overridable method `A#foo` https://srb.help/5035 + 68 | def foo(x, y:, z: [''], &blk) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + test.rb:50: The super method parameter `x` was declared here with type `[Integer]` + 50 | def foo(x, y:, z: [1], &blk) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Note: + A parameter's type must be a supertype of the same parameter's type on the super method. + Detailed explanation: + `Integer` is not a subtype of `String` for index `0` of `1`-tuple + +test.rb:68: Keyword parameter `y` of type `[String]` not compatible with type of overridable method `A#foo` https://srb.help/5035 + 68 | def foo(x, y:, z: [''], &blk) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + test.rb:50: The corresponding parameter `y` was declared here with type `[Integer]` + 50 | def foo(x, y:, z: [1], &blk) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Note: + A parameter's type must be a supertype of the same parameter's type on the super method. + Detailed explanation: + `Integer` is not a subtype of `String` for index `0` of `1`-tuple + +test.rb:68: Keyword parameter `z` of type `[String]` not compatible with type of overridable method `A#foo` https://srb.help/5035 + 68 | def foo(x, y:, z: [''], &blk) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + test.rb:50: The super method parameter `z` was declared here with type `[Integer]` + 50 | def foo(x, y:, z: [1], &blk) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Note: + A parameter's type must be a supertype of the same parameter's type on the super method. + Detailed explanation: + `Integer` is not a subtype of `String` for index `0` of `1`-tuple + +test.rb:68: Block parameter `blk` of type `T.proc.returns([String])` not compatible with type of overridable method `A#foo` https://srb.help/5035 + 68 | def foo(x, y:, z: [''], &blk) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + test.rb:50: The super method parameter `blk` was declared here with type `T.proc.returns([Integer])` + 50 | def foo(x, y:, z: [1], &blk) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Note: + A parameter's type must be a supertype of the same parameter's type on the super method. + Detailed explanation: + `[Integer]` is not a subtype of `[String]` for covariant type member `Proc0::Return` + `Integer` is not a subtype of `String` for index `0` of `1`-tuple + +test.rb:68: Return type `[String]` does not match return type of overridable method `A#foo` https://srb.help/5035 + 68 | def foo(x, y:, z: [''], &blk) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + test.rb:50: Super method defined here with return type `[Integer]` + 50 | def foo(x, y:, z: [1], &blk) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Note: + A method's return type must be a subtype of the return type on the super method. + Detailed explanation: + `String` is not a subtype of `Integer` for index `0` of `1`-tuple + +test.rb:73: Parameter **`rest` of type `[String]` not compatible with type of overridable method `A#bar` https://srb.help/5035 + 73 | def bar(**rest) + ^^^^^^^^^^^^^^^ + test.rb:56: The super method parameter **`rest` was declared here with type `[Integer]` + 56 | def bar(**rest) + ^^^^^^^^^^^^^^^ + Note: + A parameter's type must be a supertype of the same parameter's type on the super method. + Detailed explanation: + `Integer` is not a subtype of `String` for index `0` of `1`-tuple + +test.rb:7: Expected `[Integer]` but found `[String("")]` for method result type https://srb.help/7005 + 7 | [''] + ^^^^ + Expected `[Integer]` for result type of method `foo`: + test.rb:6: + 6 |def foo(x) + ^^^^^^^^^^ + Got `[String("")] (1-tuple)` originating from: + test.rb:7: + 7 | [''] + ^^^^ + Detailed explanation: + `String("")` is not a subtype of `Integer` for index `0` of `1`-tuple + +test.rb:10: Expected `[Integer]` but found `[String("")]` for argument `x` https://srb.help/7002 + 10 |foo(['']) + ^^^^ + Expected `[Integer]` for argument `x` of method `Object#foo`: + test.rb:5: + 5 |sig { params(x: [Integer]).returns([Integer]) } + ^ + Got `[String("")] (1-tuple)` originating from: + test.rb:10: + 10 |foo(['']) + ^^^^ + Detailed explanation: + `String("")` is not a subtype of `Integer` for index `0` of `1`-tuple + +test.rb:17: Expected `[Integer]` for keyword parameter `x` but found `[String]` from keyword splat https://srb.help/7002 + 17 |bar(**h) + ^^^ + Expected `[Integer]` for argument `x` of method `Object#bar`: + test.rb:12: + 12 |sig { params(x: [Integer]).void } + ^ + Got `T::Hash[Symbol, [String]]` originating from: + test.rb:16: + 16 |h = T.let({}, T::Hash[Symbol, [String]]) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + test.rb:17: + 17 |bar(**h) + ^^^ + Detailed explanation: + `String` is not a subtype of `Integer` for index `0` of `1`-tuple + Note: + A `Hash` passed as a keyword splat must match the type of all keyword parameters + because Sorbet cannot see what specific keys exist in the `Hash`. + Autocorrect: Use `-a` to autocorrect + test.rb:17: Replace with `T.unsafe(h)` + 17 |bar(**h) + ^ + +test.rb:24: Expected `T.proc.params(arg0: [Integer]).returns([Integer])` but found `T.proc.params(arg0: [String]).returns([String])` for block argument https://srb.help/7002 + 24 |baz(&p) + ^^^^^^^ + Expected `T.proc.params(arg0: [Integer]).returns([Integer])` for block argument `blk` of method `Object#baz`: + test.rb:19: + 19 |sig {params(blk: T.proc.params(x: [Integer]).returns([Integer])).void} + ^^^ + Detailed explanation: + `[String]` is not a subtype of `[Integer]` for covariant type member `Proc1::Return` + `String` is not a subtype of `Integer` for index `0` of `1`-tuple + `[String]` is not a supertype of `[Integer]` for contravariant type member `Proc1::Arg0` + `Integer` is not a subtype of `String` for index `0` of `1`-tuple + +test.rb:27: Expected `[Integer]` but found `[String("")]` for block result type https://srb.help/7005 + 27 | [''] + ^^^^ + Expected `[Integer]` for block result type: + test.rb:19: + 19 |sig {params(blk: T.proc.params(x: [Integer]).returns([Integer])).void} + ^^^ + Got `[String("")] (1-tuple)` originating from: + test.rb:27: + 27 | [''] + ^^^^ + Detailed explanation: + `String("")` is not a subtype of `Integer` for index `0` of `1`-tuple + +test.rb:60: `[String]` is not a subtype of upper bound of type member `::A::X` https://srb.help/5060 + 60 |A[[String]] + ^^^^^^^^ + test.rb:47: `::A::X` is `upper` bounded by `[Middle]` here + 47 | X = type_member {{upper: [Middle], lower: [Middle] }} + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Detailed explanation: + `String` is not a subtype of `Middle` for index `0` of `1`-tuple + +test.rb:60: `[String]` is not a supertype of lower bound of type member `::A::X` https://srb.help/5060 + 60 |A[[String]] + ^^^^^^^^ + test.rb:47: `::A::X` is `lower` bounded by `[Middle]` here + 47 | X = type_member {{upper: [Middle], lower: [Middle] }} + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Detailed explanation: + `Middle` is not a subtype of `String` for index `0` of `1`-tuple + +test.rb:80: Changing the type of a variable in a loop is not permitted https://srb.help/7001 + 80 | a = [''] + ^^^^ + Existing variable has type: `[Integer(1)]` + Attempting to change type to: `[String("")]` + + Autocorrect: Use `-a` to autocorrect + test.rb:77: Replace with `T.let([1], [T.any(Integer, String)])` + 77 |a = [1] + ^^^ + Detailed explanation: + `String("")` is not a subtype of `Integer(1)` for index `0` of `1`-tuple + +test.rb:106: Expected `[Integer(1)]` but found `[String("")]` for key `Symbol(:foo)` https://srb.help/7002 + 106 |xs[:foo] = [''] + ^^^^ + Shape originates from here: + test.rb:102: + 102 |xs = { + 103 | foo: [1], + 104 |} + Got `[String("")] (1-tuple)` originating from: + test.rb:106: + 106 |xs[:foo] = [''] + ^^^^ + Detailed explanation: + `String("")` is not a subtype of `Integer(1)` for index `0` of `1`-tuple + +test.rb:108: Argument does not have asserted type `[Integer]` https://srb.help/7007 + 108 |T.let([''], [Integer]) + ^^^^^^^^^^^^^^^^^^^^^^ + Got `[String("")] (1-tuple)` originating from: + test.rb:108: + 108 |T.let([''], [Integer]) + ^^^^ + Detailed explanation: + `String("")` is not a subtype of `Integer` for index `0` of `1`-tuple + +test.rb:112: Incompatible assignment to variable declared via `let`: `[String("")]` is not a subtype of `[Integer]` https://srb.help/7001 + 112 | b = [""] + ^^^^ + Detailed explanation: + `String("")` is not a subtype of `Integer` for index `0` of `1`-tuple + +test.rb:51: Expected `[String]` but found `[Integer(1)]` for field https://srb.help/7013 + 51 | @a = [1] + ^^^ + Expected `[String] (1-tuple)` for field defined here: + test.rb:44: + 44 | @a = T.let([''], [String]) + ^^ + Got `[Integer(1)] (1-tuple)` originating from: + test.rb:51: + 51 | @a = [1] + ^^^ + Detailed explanation: + `Integer(1)` is not a subtype of `String` for index `0` of `1`-tuple +Errors: 22 diff --git a/test/cli/detailed-errors-completeness/test.rb b/test/cli/detailed-errors-completeness/test.rb new file mode 100644 index 0000000000..44a86e274d --- /dev/null +++ b/test/cli/detailed-errors-completeness/test.rb @@ -0,0 +1,113 @@ +# typed: true + +extend T::Sig + +sig { params(x: [Integer]).returns([Integer]) } +def foo(x) + [''] +end + +foo(['']) + +sig { params(x: [Integer]).void } +def bar(x: [1, '']) +end + +h = T.let({}, T::Hash[Symbol, [String]]) +bar(**h) + +sig {params(blk: T.proc.params(x: [Integer]).returns([Integer])).void} +def baz(&blk) +end + +p = T.let(T.unsafe(nil), T.proc.params(x: [String]).returns([String])) +baz(&p) + +baz do + [''] +end + +class Upper +end + +class Middle < Upper +end + +class Lower < Middle +end + +class A + extend T::Generic + extend T::Sig + + def initialize + @a = T.let([''], [String]) + end + + X = type_member {{upper: [Middle], lower: [Middle] }} + + sig { overridable.params(x: [Integer], y: [Integer], z: [Integer], blk: T.proc.returns([Integer])).returns([Integer]) } + def foo(x, y:, z: [1], &blk) + @a = [1] + [1] + end + + sig { overridable.params(rest: [Integer]).void } + def bar(**rest) + end +end + +A[[String]] + +class B < A + extend T::Generic + + X = type_member {{upper: [Upper], lower: [Lower] }} + + sig { override.params(x: [String], y: [String], z: [String], blk: T.proc.returns([String])).returns([String]) } + def foo(x, y:, z: [''], &blk) + [''] + end + + sig { override.params(rest: [String]).void } + def bar(**rest) + end +end + +a = [1] + +loop do + a = [''] + break +end + +class C + extend T::Generic + + X = type_member {{ fixed: [Upper] }} +end + +class D < C + extend T::Generic + + X = type_member {{ fixed: [Middle] }} +end + +class E + extend T::Generic + + X = type_member {{upper: [Lower], lower: [Upper] }} +end + +xs = { + foo: [1], +} + +xs[:foo] = [''] + +T.let([''], [Integer]) + +b = T.let([0], [Integer]) +1.times do + b = [""] +end diff --git a/test/cli/detailed-errors-completeness/test.sh b/test/cli/detailed-errors-completeness/test.sh new file mode 100755 index 0000000000..5113f4b1a7 --- /dev/null +++ b/test/cli/detailed-errors-completeness/test.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +set -euo pipefail + +main/sorbet --silence-dev-message test/cli/detailed-errors-completeness 2>&1 diff --git a/test/cli/detailed-errors/intersection.rb b/test/cli/detailed-errors/intersection.rb new file mode 100644 index 0000000000..d075368721 --- /dev/null +++ b/test/cli/detailed-errors/intersection.rb @@ -0,0 +1,52 @@ +# typed: true + +extend T::Sig + +module M1 +end + +module M2 +end + +module M3 +end + +module M4 +end + +class C +end + +class C1 + include M1 +end + +class C2 + include M2 +end + +class C124 + include M1 + include M2 + include M4 +end + +class C123 + include M1 + include M2 + include M3 +end + +class C134 + include M1 + include M3 + include M4 +end + +T.let(C.new, T.all(M1, M2)) +T.let(C1.new, T.all(M1, M2)) +T.let(C2.new, T.all(M1, M2)) +T.let(C1.new, T.all(M1, M2, M3, M4)) +T.let(C123.new, T.all(M1, M2, M3, M4)) +T.let(C124.new, T.all(M1, M2, M3, M4)) +T.let(C134.new, T.all(M1, M2, M3, M4)) diff --git a/test/cli/detailed-errors/shapes.rb b/test/cli/detailed-errors/shapes.rb new file mode 100644 index 0000000000..2e20e84e27 --- /dev/null +++ b/test/cli/detailed-errors/shapes.rb @@ -0,0 +1,22 @@ +# typed: true + +extend T::Sig + +sig { params(x: {a: Integer, b: String}).void } +def takes_shape(x) +end + +# too few keys +takes_shape({}) +takes_shape({a: 1}) + +# missing keys +takes_shape({a: 1, c: 1}) +takes_shape({c: 1, d: 1}) + +# mismatch in value types +takes_shape({a: 1, b: 1}) +takes_shape({a: '', b: 1}) + +# mismatch in value types and missing keys +takes_shape({a: '', c: 1}) diff --git a/test/cli/detailed-errors/test.out b/test/cli/detailed-errors/test.out new file mode 100644 index 0000000000..3c5319c23c --- /dev/null +++ b/test/cli/detailed-errors/test.out @@ -0,0 +1,278 @@ +type_members.rb:35: Argument does not have asserted type `Box2[Integer]` https://srb.help/7007 + 35 | T.let(box1, Box2[Integer]) + ^^^^^^^^^^^^^^^^^^^^^^^^^^ + Got `Box1[Integer]` originating from: + type_members.rb:34: + 34 |def takes_box1(box1) + ^^^^ + Detailed explanation: + `Box1` does not derive from `Box2` + +type_members.rb:36: Argument does not have asserted type `Integer` https://srb.help/7007 + 36 | T.let(box1, Integer) + ^^^^^^^^^^^^^^^^^^^^ + Got `Box1[Integer]` originating from: + type_members.rb:34: + 34 |def takes_box1(box1) + ^^^^ + Detailed explanation: + `Box1` does not derive from `Integer` + +type_members.rb:49: Expected `Box[T.any(Integer, String)]` but found `Box[Integer]` for argument `box` https://srb.help/7002 + 49 | takes_box_int_str(box) + ^^^ + Expected `Box[T.any(Integer, String)]` for argument `box` of method `Object#takes_box_int_str`: + type_members.rb:44: + 44 |sig { params(box: Box[T.any(Integer, String)]).void } + ^^^ + Got `Box[Integer]` originating from: + type_members.rb:48: + 48 |def takes_box_integer(box) + ^^^ + Detailed explanation: + `Integer` is not equivalent to `T.any(Integer, String)` for invariant type member `Box::Elem` + `Integer` is a subtype of `T.any(Integer, String)` but not the reverse, so they are not equivalent + +type_members.rb:21: Argument does not have asserted type `A[Middle, Middle, Middle]` https://srb.help/7007 + 21 |T.let(A[String, Lower, Upper].new, A[Middle, Middle, Middle]) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Got `A[String, Lower, Upper]` originating from: + type_members.rb:21: + 21 |T.let(A[String, Lower, Upper].new, A[Middle, Middle, Middle]) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Detailed explanation: + `String` is not equivalent to `Middle` for invariant type member `A::X` + `Lower` is not a supertype of `Middle` for contravariant type member `A::Y` + `Upper` is not a subtype of `Middle` for covariant type member `A::Z` + +intersection.rb:46: Argument does not have asserted type `T.all(M1, M2)` https://srb.help/7007 + 46 |T.let(C.new, T.all(M1, M2)) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Got `C` originating from: + intersection.rb:46: + 46 |T.let(C.new, T.all(M1, M2)) + ^^^^^ + Detailed explanation: + `C` is not a subtype of `M1` (the left side of the `T.all`) + +intersection.rb:47: Argument does not have asserted type `T.all(M1, M2)` https://srb.help/7007 + 47 |T.let(C1.new, T.all(M1, M2)) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Got `C1` originating from: + intersection.rb:47: + 47 |T.let(C1.new, T.all(M1, M2)) + ^^^^^^ + Detailed explanation: + `C1` is not a subtype of `M2` (the right side of the `T.all`) + +intersection.rb:48: Argument does not have asserted type `T.all(M1, M2)` https://srb.help/7007 + 48 |T.let(C2.new, T.all(M1, M2)) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Got `C2` originating from: + intersection.rb:48: + 48 |T.let(C2.new, T.all(M1, M2)) + ^^^^^^ + Detailed explanation: + `C2` is not a subtype of `M1` (the left side of the `T.all`) + +intersection.rb:49: Argument does not have asserted type `T.all(M1, M2, M3, M4)` https://srb.help/7007 + 49 |T.let(C1.new, T.all(M1, M2, M3, M4)) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Got `C1` originating from: + intersection.rb:49: + 49 |T.let(C1.new, T.all(M1, M2, M3, M4)) + ^^^^^^ + Detailed explanation: + `C1` is not a subtype of `T.all(M1, M2, M3)` (the left side of the `T.all`) + `C1` is not a subtype of `T.all(M1, M2)` (the left side of the `T.all`) + `C1` is not a subtype of `M2` (the right side of the `T.all`) + +intersection.rb:50: Argument does not have asserted type `T.all(M1, M2, M3, M4)` https://srb.help/7007 + 50 |T.let(C123.new, T.all(M1, M2, M3, M4)) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Got `C123` originating from: + intersection.rb:50: + 50 |T.let(C123.new, T.all(M1, M2, M3, M4)) + ^^^^^^^^ + Detailed explanation: + `C123` is not a subtype of `M4` (the right side of the `T.all`) + +intersection.rb:51: Argument does not have asserted type `T.all(M1, M2, M3, M4)` https://srb.help/7007 + 51 |T.let(C124.new, T.all(M1, M2, M3, M4)) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Got `C124` originating from: + intersection.rb:51: + 51 |T.let(C124.new, T.all(M1, M2, M3, M4)) + ^^^^^^^^ + Detailed explanation: + `C124` is not a subtype of `T.all(M1, M2, M3)` (the left side of the `T.all`) + `C124` is not a subtype of `M3` (the right side of the `T.all`) + +intersection.rb:52: Argument does not have asserted type `T.all(M1, M2, M3, M4)` https://srb.help/7007 + 52 |T.let(C134.new, T.all(M1, M2, M3, M4)) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Got `C134` originating from: + intersection.rb:52: + 52 |T.let(C134.new, T.all(M1, M2, M3, M4)) + ^^^^^^^^ + Detailed explanation: + `C134` is not a subtype of `T.all(M1, M2, M3)` (the left side of the `T.all`) + `C134` is not a subtype of `T.all(M1, M2)` (the left side of the `T.all`) + `C134` is not a subtype of `M2` (the right side of the `T.all`) + +shapes.rb:10: Expected `{a: Integer, b: String}` but found `{}` for argument `x` https://srb.help/7002 + 10 |takes_shape({}) + ^^ + Expected `{a: Integer, b: String}` for argument `x` of method `Object#takes_shape`: + shapes.rb:5: + 5 |sig { params(x: {a: Integer, b: String}).void } + ^ + Got `{} (shape of T::Hash[T.untyped, T.untyped])` originating from: + shapes.rb:10: + 10 |takes_shape({}) + ^^ + +shapes.rb:11: Expected `{a: Integer, b: String}` but found `{a: Integer(1)}` for argument `x` https://srb.help/7002 + 11 |takes_shape({a: 1}) + ^^^^^^ + Expected `{a: Integer, b: String}` for argument `x` of method `Object#takes_shape`: + shapes.rb:5: + 5 |sig { params(x: {a: Integer, b: String}).void } + ^ + Got `{a: Integer(1)} (shape of T::Hash[T.untyped, T.untyped])` originating from: + shapes.rb:11: + 11 |takes_shape({a: 1}) + ^^^^^^ + +shapes.rb:14: Expected `{a: Integer, b: String}` but found `{a: Integer(1), c: Integer(1)}` for argument `x` https://srb.help/7002 + 14 |takes_shape({a: 1, c: 1}) + ^^^^^^^^^^^^ + Expected `{a: Integer, b: String}` for argument `x` of method `Object#takes_shape`: + shapes.rb:5: + 5 |sig { params(x: {a: Integer, b: String}).void } + ^ + Got `{a: Integer(1), c: Integer(1)} (shape of T::Hash[T.untyped, T.untyped])` originating from: + shapes.rb:14: + 14 |takes_shape({a: 1, c: 1}) + ^^^^^^^^^^^^ + +shapes.rb:15: Expected `{a: Integer, b: String}` but found `{c: Integer(1), d: Integer(1)}` for argument `x` https://srb.help/7002 + 15 |takes_shape({c: 1, d: 1}) + ^^^^^^^^^^^^ + Expected `{a: Integer, b: String}` for argument `x` of method `Object#takes_shape`: + shapes.rb:5: + 5 |sig { params(x: {a: Integer, b: String}).void } + ^ + Got `{c: Integer(1), d: Integer(1)} (shape of T::Hash[T.untyped, T.untyped])` originating from: + shapes.rb:15: + 15 |takes_shape({c: 1, d: 1}) + ^^^^^^^^^^^^ + +shapes.rb:18: Expected `{a: Integer, b: String}` but found `{a: Integer(1), b: Integer(1)}` for argument `x` https://srb.help/7002 + 18 |takes_shape({a: 1, b: 1}) + ^^^^^^^^^^^^ + Expected `{a: Integer, b: String}` for argument `x` of method `Object#takes_shape`: + shapes.rb:5: + 5 |sig { params(x: {a: Integer, b: String}).void } + ^ + Got `{a: Integer(1), b: Integer(1)} (shape of T::Hash[T.untyped, T.untyped])` originating from: + shapes.rb:18: + 18 |takes_shape({a: 1, b: 1}) + ^^^^^^^^^^^^ + Detailed explanation: + `Integer(1)` is not a subtype of `String` for key `Symbol(:b)` + +shapes.rb:19: Expected `{a: Integer, b: String}` but found `{a: String(""), b: Integer(1)}` for argument `x` https://srb.help/7002 + 19 |takes_shape({a: '', b: 1}) + ^^^^^^^^^^^^^ + Expected `{a: Integer, b: String}` for argument `x` of method `Object#takes_shape`: + shapes.rb:5: + 5 |sig { params(x: {a: Integer, b: String}).void } + ^ + Got `{a: String(""), b: Integer(1)} (shape of T::Hash[T.untyped, T.untyped])` originating from: + shapes.rb:19: + 19 |takes_shape({a: '', b: 1}) + ^^^^^^^^^^^^^ + Detailed explanation: + `String("")` is not a subtype of `Integer` for key `Symbol(:a)` + `Integer(1)` is not a subtype of `String` for key `Symbol(:b)` + +shapes.rb:22: Expected `{a: Integer, b: String}` but found `{a: String(""), c: Integer(1)}` for argument `x` https://srb.help/7002 + 22 |takes_shape({a: '', c: 1}) + ^^^^^^^^^^^^^ + Expected `{a: Integer, b: String}` for argument `x` of method `Object#takes_shape`: + shapes.rb:5: + 5 |sig { params(x: {a: Integer, b: String}).void } + ^ + Got `{a: String(""), c: Integer(1)} (shape of T::Hash[T.untyped, T.untyped])` originating from: + shapes.rb:22: + 22 |takes_shape({a: '', c: 1}) + ^^^^^^^^^^^^^ + Detailed explanation: + `String("")` is not a subtype of `Integer` for key `Symbol(:a)` + +tuples.rb:10: Expected `[Integer, String]` but found `[]` for argument `x` https://srb.help/7002 + 10 |takes_tuple([]) + ^^ + Expected `[Integer, String]` for argument `x` of method `Object#takes_tuple`: + tuples.rb:5: + 5 |sig { params(x: [Integer, String]).void } + ^ + Got `[] (0-tuple)` originating from: + tuples.rb:10: + 10 |takes_tuple([]) + ^^ + +tuples.rb:11: Expected `[Integer, String]` but found `[Integer(1)]` for argument `x` https://srb.help/7002 + 11 |takes_tuple([1]) + ^^^ + Expected `[Integer, String]` for argument `x` of method `Object#takes_tuple`: + tuples.rb:5: + 5 |sig { params(x: [Integer, String]).void } + ^ + Got `[Integer(1)] (1-tuple)` originating from: + tuples.rb:11: + 11 |takes_tuple([1]) + ^^^ + +tuples.rb:14: Expected `[Integer, String]` but found `[Integer(1), Integer(1)]` for argument `x` https://srb.help/7002 + 14 |takes_tuple([1, 1]) + ^^^^^^ + Expected `[Integer, String]` for argument `x` of method `Object#takes_tuple`: + tuples.rb:5: + 5 |sig { params(x: [Integer, String]).void } + ^ + Got `[Integer(1), Integer(1)] (2-tuple)` originating from: + tuples.rb:14: + 14 |takes_tuple([1, 1]) + ^^^^^^ + Detailed explanation: + `Integer(1)` is not a subtype of `String` for index `1` of `2`-tuple + +tuples.rb:15: Expected `[Integer, String]` but found `[String(""), Integer(1)]` for argument `x` https://srb.help/7002 + 15 |takes_tuple(['', 1]) + ^^^^^^^ + Expected `[Integer, String]` for argument `x` of method `Object#takes_tuple`: + tuples.rb:5: + 5 |sig { params(x: [Integer, String]).void } + ^ + Got `[String(""), Integer(1)] (2-tuple)` originating from: + tuples.rb:15: + 15 |takes_tuple(['', 1]) + ^^^^^^^ + Detailed explanation: + `String("")` is not a subtype of `Integer` for index `0` of `2`-tuple + `Integer(1)` is not a subtype of `String` for index `1` of `2`-tuple + +tuples.rb:18: Expected `[Integer, String]` but found `[String("")]` for argument `x` https://srb.help/7002 + 18 |takes_tuple(['']) + ^^^^ + Expected `[Integer, String]` for argument `x` of method `Object#takes_tuple`: + tuples.rb:5: + 5 |sig { params(x: [Integer, String]).void } + ^ + Got `[String("")] (1-tuple)` originating from: + tuples.rb:18: + 18 |takes_tuple(['']) + ^^^^ +Errors: 23 diff --git a/test/cli/detailed-errors/test.sh b/test/cli/detailed-errors/test.sh new file mode 100755 index 0000000000..d321873b24 --- /dev/null +++ b/test/cli/detailed-errors/test.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +set -euo pipefail + +main/sorbet --silence-dev-message --max-threads=0 test/cli/detailed-errors 2>&1 diff --git a/test/cli/detailed-errors/tuples.rb b/test/cli/detailed-errors/tuples.rb new file mode 100644 index 0000000000..2f92523bd4 --- /dev/null +++ b/test/cli/detailed-errors/tuples.rb @@ -0,0 +1,18 @@ +# typed: true + +extend T::Sig + +sig { params(x: [Integer, String]).void } +def takes_tuple(x) +end + +# too few items +takes_tuple([]) +takes_tuple([1]) + +# mismatch in value types +takes_tuple([1, 1]) +takes_tuple(['', 1]) + +# mismatch in value types and too few items +takes_tuple(['']) diff --git a/test/cli/detailed-errors/type_members.rb b/test/cli/detailed-errors/type_members.rb new file mode 100644 index 0000000000..c386228e93 --- /dev/null +++ b/test/cli/detailed-errors/type_members.rb @@ -0,0 +1,50 @@ +# typed: true + +extend T::Sig + +class Upper +end + +class Middle < Upper +end + +class Lower < Middle +end + +class A + extend T::Generic + X = type_member + Y = type_member(:in) + Z = type_member(:out) +end + +T.let(A[String, Lower, Upper].new, A[Middle, Middle, Middle]) + +class Box1 + extend T::Generic + Elem = type_member +end + +class Box2 + extend T::Generic + Elem = type_member +end + +sig { params(box1: Box1[Integer]).void } +def takes_box1(box1) + T.let(box1, Box2[Integer]) + T.let(box1, Integer) +end + +class Box + extend T::Generic + Elem = type_member +end + +sig { params(box: Box[T.any(Integer, String)]).void } +def takes_box_int_str(box); end + +sig { params(box: Box[Integer]).void } +def takes_box_integer(box) + takes_box_int_str(box) +end diff --git a/test/cli/exception-loop/test.out b/test/cli/exception-loop/test.out new file mode 100644 index 0000000000..d087737c68 --- /dev/null +++ b/test/cli/exception-loop/test.out @@ -0,0 +1,50 @@ +test/cli/exception-loop/test.rb:23: Revealed type: `T.nilable(A)` https://srb.help/7014 + 23 | T.reveal_type(e) # error: `T.nilable(A)` + ^^^^^^^^^^^^^^^^ + Got `T.nilable(A)` originating from: + test/cli/exception-loop/test.rb:17: + 17 | rescue A => e + ^^^^^^ + test/cli/exception-loop/test.rb:17: + 17 | rescue A => e + ^ + test/cli/exception-loop/test.rb:14: Possibly uninitialized (`NilClass`) in: + 14 |def example1 + ^^^^^^^^^^^^ + +test/cli/exception-loop/test.rb:25: Changing the type of a variable in a loop is not permitted https://srb.help/7001 + 25 | rescue B => e + ^ + Existing variable has type: `T.nilable(A)` + Attempting to change type to: `B` + + Original type from: + test/cli/exception-loop/test.rb:17: + 17 | rescue A => e + ^^^^^^ + test/cli/exception-loop/test.rb:17: + 17 | rescue A => e + ^ + test/cli/exception-loop/test.rb:14: Possibly uninitialized (`NilClass`) in: + 14 |def example1 + ^^^^^^^^^^^^ + Note: + The exception variables of two `rescue` blocks conflict with each other because + the second is inside a loop. Either use `T.let` to initialize the exception + variable before the first `rescue`, or pick unique variable names. + + +test/cli/exception-loop/test.rb:43: Revealed type: `T.nilable(T.any(A, B))` https://srb.help/7014 + 43 | T.reveal_type(e) # error: `T.nilable(T.any(A, B))` + ^^^^^^^^^^^^^^^^ + Got `T.nilable(T.any(A, B))` originating from: + test/cli/exception-loop/test.rb:33: + 33 | e = T.let(nil, T.nilable(T.any(A, B))) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + test/cli/exception-loop/test.rb:37: + 37 | rescue A => e + ^^^^^^ + test/cli/exception-loop/test.rb:37: + 37 | rescue A => e + ^ +Errors: 3 diff --git a/test/cli/exception-loop/test.rb b/test/cli/exception-loop/test.rb new file mode 100644 index 0000000000..8cc4ee518a --- /dev/null +++ b/test/cli/exception-loop/test.rb @@ -0,0 +1,48 @@ +# typed: true +extend T::Sig + +class A < StandardError; end +class B < StandardError; end + +def raises_a + raise A.new +end +def raises_b + raise B.new +end + +def example1 + begin + raises_a + rescue A => e + end + + 2.times do + begin + puts(e.class) + T.reveal_type(e) # error: `T.nilable(A)` + raises_b + rescue B => e + # ^ error: Changing the type of a variable in a loop is not permitted + end + end +end + +def example2 + # works to pin this outside the loop + e = T.let(nil, T.nilable(T.any(A, B))) + + begin + raises_a + rescue A => e + end + + 2.times do + begin + puts(e.class) + T.reveal_type(e) # error: `T.nilable(T.any(A, B))` + raises_b + rescue B => e + end + end +end diff --git a/test/cli/exception-loop/test.sh b/test/cli/exception-loop/test.sh new file mode 100755 index 0000000000..f756235cce --- /dev/null +++ b/test/cli/exception-loop/test.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +set -e + +main/sorbet --silence-dev-message test/cli/exception-loop/test.rb 2>&1 diff --git a/test/cli/expected-got/test.out b/test/cli/expected-got/test.out index 805afe3f38..610408ff8f 100644 --- a/test/cli/expected-got/test.out +++ b/test/cli/expected-got/test.out @@ -40,10 +40,12 @@ test/cli/expected-got/expected-got.rb:27: Expected `Integer` but found `T.any(In test/cli/expected-got/expected-got.rb:42: Expected `T.proc.returns(Integer)` but found `T.proc.returns(String)` for block argument https://srb.help/7002 42 | takes_int_block(&blk) ^^^^^^^^^^^^^^^^^^^^^ - Expected `T.proc.returns(Integer)` for for block argument `blk` of method `Object#takes_int_block`: + Expected `T.proc.returns(Integer)` for block argument `blk` of method `Object#takes_int_block`: test/cli/expected-got/expected-got.rb:32: 32 |sig {params(blk: T.proc.returns(Integer)).void} ^^^ + Detailed explanation: + `String` is not a subtype of `Integer` for covariant type member `Proc0::Return` test/cli/expected-got/expected-got.rb:53: Control flow could reach `T.absurd` because the type `Integer` wasn't handled https://srb.help/7026 53 | else T.absurd(x) @@ -149,6 +151,8 @@ test/cli/expected-got/expected-got.rb:73: Expected `T::Array[Integer]` but found test/cli/expected-got/expected-got.rb:72: 72 |xs = [1, 2, T.let(3, T.nilable(Integer))] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Detailed explanation: + `T.nilable(Integer)` is not a subtype of `Integer` for covariant type member `Array::Elem` test/cli/expected-got/expected-got.rb:74: Argument does not have asserted type `T::Array[Integer]` https://srb.help/7007 74 |T.let(xs, T::Array[Integer]) @@ -157,4 +161,6 @@ test/cli/expected-got/expected-got.rb:74: Argument does not have asserted type ` test/cli/expected-got/expected-got.rb:72: 72 |xs = [1, 2, T.let(3, T.nilable(Integer))] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Detailed explanation: + `T.nilable(Integer)` is not a subtype of `Integer` for covariant type member `Array::Elem` Errors: 16 diff --git a/test/cli/file-table-json/test.out b/test/cli/file-table-json/test.out index b6c6576510..a4c9287d24 100644 --- a/test/cli/file-table-json/test.out +++ b/test/cli/file-table-json/test.out @@ -23,12 +23,6 @@ ----- -p file-table-full-json ------------------------------------------ { "files": [ - { - "path": "https://github.com/sorbet/sorbet/tree/master/bazel-out/host/bin/rbi/procs.rbi", - "sigil": "True", - "strict": "True", - "min_error_level": "Max" - }, { "path": "https://github.com/sorbet/sorbet/tree/master/rbi/core/argf.rbi", "sigil": "Stdlib", @@ -83,6 +77,12 @@ "strict": "Stdlib", "min_error_level": "Max" }, + { + "path": "https://github.com/sorbet/sorbet/tree/master/rbi/core/dir.rbi", + "sigil": "Stdlib", + "strict": "Stdlib", + "min_error_level": "Max" + }, { "path": "test/cli/file-table-json/file-table-json.rb", "sigil": "True", diff --git a/test/cli/file-table-json/test.sh b/test/cli/file-table-json/test.sh index 8c4eb7bdc2..951f07cc49 100755 --- a/test/cli/file-table-json/test.sh +++ b/test/cli/file-table-json/test.sh @@ -7,6 +7,6 @@ main/sorbet --censor-for-snapshot-tests --silence-dev-message -p file-table-json echo "----- --no-stdlib -p file-table-json -----------------------------------" main/sorbet --censor-for-snapshot-tests --silence-dev-message --no-stdlib -p file-table-json test/cli/file-table-json/file-table-json.rb echo "----- -p file-table-full-json ------------------------------------------" -main/sorbet --censor-for-snapshot-tests --silence-dev-message -p file-table-full-json test/cli/file-table-json/file-table-json.rb +main/sorbet --censor-for-snapshot-tests --silence-dev-message -p file-table-full-json test/cli/file-table-json/file-table-json.rb | sed 's/https:\/\/.*procs.rbi/procs.rbi/g' echo "----- --no-stdlib -p file-table-full-json ------------------------------" main/sorbet --censor-for-snapshot-tests --silence-dev-message --no-stdlib -p file-table-full-json test/cli/file-table-json/file-table-json.rb diff --git a/test/cli/invariant_type_parameter/test.out b/test/cli/invariant_type_parameter/test.out index 7cc2392894..b149ab6c56 100644 --- a/test/cli/invariant_type_parameter/test.out +++ b/test/cli/invariant_type_parameter/test.out @@ -37,6 +37,8 @@ test/cli/invariant_type_parameter/test.rb:75: Expected `Box[T.type_parameter(:U) test/cli/invariant_type_parameter/test.rb:71: 71 |ibox_m = T.let(Box[M].new, IBox[M]) ^^^^^^^^^^^^^^^^^^^^^^^^^^ + Detailed explanation: + `IBox` does not derive from `Box` test/cli/invariant_type_parameter/test.rb:76: Revealed type: `Box[T.noreturn]` https://srb.help/7014 76 | T.reveal_type(box) diff --git a/test/cli/lsp-common-case-exit/test.out b/test/cli/lsp-common-case-exit/test.out index df173edf11..0a839d6542 100644 --- a/test/cli/lsp-common-case-exit/test.out +++ b/test/cli/lsp-common-case-exit/test.out @@ -1,5 +1,5 @@ -Content-Length: 639 +Content-Length: 638 -{"jsonrpc":"2.0","id":0,"requestMethod":"initialize","result":{"capabilities":{"textDocumentSync":1,"hoverProvider":true,"completionProvider":{"triggerCharacters":[".",":","@","#"]},"definitionProvider":true,"typeDefinitionProvider":true,"implementationProvider":true,"referencesProvider":true,"documentHighlightProvider":false,"documentSymbolProvider":false,"workspaceSymbolProvider":true,"codeActionProvider":{"codeActionKinds":["quickfix","source.fixAll.sorbet","refactor.extract","refactor.rewrite"],"resolveProvider":true},"documentFormattingProvider":false,"renameProvider":{"prepareProvider":true},"sorbetShowSymbolProvider":true}}}Content-Length: 65 +{"jsonrpc":"2.0","id":0,"requestMethod":"initialize","result":{"capabilities":{"textDocumentSync":1,"hoverProvider":true,"completionProvider":{"triggerCharacters":[".",":","@","#"]},"definitionProvider":true,"typeDefinitionProvider":true,"implementationProvider":true,"referencesProvider":true,"documentHighlightProvider":false,"documentSymbolProvider":true,"workspaceSymbolProvider":true,"codeActionProvider":{"codeActionKinds":["quickfix","source.fixAll.sorbet","refactor.extract","refactor.rewrite"],"resolveProvider":true},"documentFormattingProvider":false,"renameProvider":{"prepareProvider":true},"sorbetShowSymbolProvider":true}}}Content-Length: 65 {"jsonrpc":"2.0","id":1,"requestMethod":"shutdown","result":null} \ No newline at end of file diff --git a/test/cli/missing-constants/missing-constants.rb b/test/cli/missing-constants/missing-constants.rb index 0eb79813c7..9babcf7320 100644 --- a/test/cli/missing-constants/missing-constants.rb +++ b/test/cli/missing-constants/missing-constants.rb @@ -1,4 +1,4 @@ -class A +class A < E def foo B Duplicate @@ -11,4 +11,3 @@ def self.baz A::C B - diff --git a/test/cli/missing-constants/test.out b/test/cli/missing-constants/test.out index da5e6f4ea0..8fb98534eb 100644 --- a/test/cli/missing-constants/test.out +++ b/test/cli/missing-constants/test.out @@ -1,4 +1,5 @@ ::B +::E A::B A::C A::D diff --git a/test/cli/multiple-return-loc/test.out b/test/cli/multiple-return-loc/test.out new file mode 100644 index 0000000000..80362adeba --- /dev/null +++ b/test/cli/multiple-return-loc/test.out @@ -0,0 +1,22 @@ +test.rb:6: Expected `T::Array[Integer]` but found `[Integer(1), String("")]` for method result type https://srb.help/7005 + 6 | return 1, "" + ^^^^^^^^^^^^ + Expected `T::Array[Integer]` for result type of method `foo`: + test.rb:5: + 5 |def foo + ^^^^^^^ + Got `[Integer(1), String("")] (2-tuple)` originating from: + test.rb:6: + 6 | return 1, "" + ^^^^^ + Detailed explanation: + `T.any(Integer, String)` is not a subtype of `Integer` for covariant type member `Array::Elem` + +test.rb:13: Revealed type: `[Integer(1), Integer(2)] (2-tuple)` https://srb.help/7014 + 13 |T.reveal_type(x) + ^^^^^^^^^^^^^^^^ + Got `[Integer(1), Integer(2)] (2-tuple)` originating from: + test.rb:10: + 10 | break 1, 2 + ^^^^ +Errors: 2 diff --git a/test/cli/multiple-return-loc/test.rb b/test/cli/multiple-return-loc/test.rb new file mode 100644 index 0000000000..72ebe92f58 --- /dev/null +++ b/test/cli/multiple-return-loc/test.rb @@ -0,0 +1,13 @@ +# typed: true +extend T::Sig + +sig { returns(T::Array[Integer]) } +def foo + return 1, "" +end + +x = loop do + break 1, 2 +end + +T.reveal_type(x) diff --git a/test/cli/multiple-return-loc/test.sh b/test/cli/multiple-return-loc/test.sh new file mode 100755 index 0000000000..d3c1733a9d --- /dev/null +++ b/test/cli/multiple-return-loc/test.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +set -euo pipefail + +main/sorbet --silence-dev-message test/cli/multiple-return-loc 2>&1 diff --git a/test/cli/package-skip-import-visibility-check-for/__package.rb b/test/cli/package-allow-relaxed-packager-checks-for/__package.rb similarity index 100% rename from test/cli/package-skip-import-visibility-check-for/__package.rb rename to test/cli/package-allow-relaxed-packager-checks-for/__package.rb diff --git a/test/cli/package-skip-import-visibility-check-for/a/__package.rb b/test/cli/package-allow-relaxed-packager-checks-for/a/__package.rb similarity index 100% rename from test/cli/package-skip-import-visibility-check-for/a/__package.rb rename to test/cli/package-allow-relaxed-packager-checks-for/a/__package.rb diff --git a/test/cli/package-skip-import-visibility-check-for/b/__package.rb b/test/cli/package-allow-relaxed-packager-checks-for/b/__package.rb similarity index 100% rename from test/cli/package-skip-import-visibility-check-for/b/__package.rb rename to test/cli/package-allow-relaxed-packager-checks-for/b/__package.rb diff --git a/test/cli/package-skip-import-visibility-check-for/test.out b/test/cli/package-allow-relaxed-packager-checks-for/test.out similarity index 100% rename from test/cli/package-skip-import-visibility-check-for/test.out rename to test/cli/package-allow-relaxed-packager-checks-for/test.out diff --git a/test/cli/package-allow-relaxed-packager-checks-for/test.sh b/test/cli/package-allow-relaxed-packager-checks-for/test.sh new file mode 100755 index 0000000000..5cf0c521eb --- /dev/null +++ b/test/cli/package-allow-relaxed-packager-checks-for/test.sh @@ -0,0 +1,5 @@ +cd test/cli/package-allow-relaxed-packager-checks-for || exit 1 + +../../../main/sorbet --silence-dev-message --stripe-packages --allow-relaxed-packager-checks-for=Project::Root --max-threads=0 . 2>&1 + + diff --git a/test/cli/package-autocorrect-missing-import/foo.test.rb b/test/cli/package-autocorrect-missing-import/foo.test.rb index 1d4c288012..4f110f1bed 100644 --- a/test/cli/package-autocorrect-missing-import/foo.test.rb +++ b/test/cli/package-autocorrect-missing-import/foo.test.rb @@ -2,4 +2,6 @@ module Test::Foo::MyPackage Test::Foo::Bar::OtherPackage::TestUtil + + Foo::Bar::OtherPackage::ImportMeTestOnly end diff --git a/test/cli/package-autocorrect-missing-import/foo_class.rb b/test/cli/package-autocorrect-missing-import/foo_class.rb index aafc24ceae..94fda2b22b 100644 --- a/test/cli/package-autocorrect-missing-import/foo_class.rb +++ b/test/cli/package-autocorrect-missing-import/foo_class.rb @@ -13,4 +13,6 @@ class FooClass module Foo::MyPackage Foo::Bar::OtherPackage::OtherClass # resolves via root Foo::Bar::MyClass::SUBCLASSES # resolves via root + + Test::Foo::Bar::OtherPackage::TestUtil end diff --git a/test/cli/package-autocorrect-missing-import/other/__package.rb b/test/cli/package-autocorrect-missing-import/other/__package.rb index 8e1fe1831a..e9273d5586 100644 --- a/test/cli/package-autocorrect-missing-import/other/__package.rb +++ b/test/cli/package-autocorrect-missing-import/other/__package.rb @@ -2,5 +2,6 @@ class Foo::Bar::OtherPackage < PackageSpec export Foo::Bar::OtherPackage::OtherClass + export Foo::Bar::OtherPackage::ImportMeTestOnly export Test::Foo::Bar::OtherPackage::TestUtil end diff --git a/test/cli/package-autocorrect-missing-import/other/import_me_test_only.rb b/test/cli/package-autocorrect-missing-import/other/import_me_test_only.rb new file mode 100644 index 0000000000..1fa3b9deb0 --- /dev/null +++ b/test/cli/package-autocorrect-missing-import/other/import_me_test_only.rb @@ -0,0 +1,4 @@ +# typed: strict + +class Foo::Bar::OtherPackage::ImportMeTestOnly +end diff --git a/test/cli/package-autocorrect-missing-import/test.out b/test/cli/package-autocorrect-missing-import/test.out index 7a1dcec77e..430c13fc24 100644 --- a/test/cli/package-autocorrect-missing-import/test.out +++ b/test/cli/package-autocorrect-missing-import/test.out @@ -56,6 +56,10 @@ foo_class.rb:14: `Foo::Bar::OtherPackage::OtherClass` resolves but its package i 6 | # import Foo::Bar::OtherPackage ## MISSING! ^ +foo_class.rb:17: `Test::Foo::Bar::OtherPackage::TestUtil` is defined in a test namespace and cannot be referenced in a non-test file https://srb.help/3720 + 17 | Test::Foo::Bar::OtherPackage::TestUtil + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + foo.test.rb:4: `Test::Foo::Bar::OtherPackage::TestUtil` resolves but its package is not imported https://srb.help/3718 4 | Test::Foo::Bar::OtherPackage::TestUtil ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -66,7 +70,18 @@ foo.test.rb:4: `Test::Foo::Bar::OtherPackage::TestUtil` resolves but its package __package.rb:6: Inserted `test_import Foo::Bar::OtherPackage` 6 | # import Foo::Bar::OtherPackage ## MISSING! ^ -Errors: 5 + +foo.test.rb:6: `Foo::Bar::OtherPackage::ImportMeTestOnly` resolves but its package is not imported https://srb.help/3718 + 6 | Foo::Bar::OtherPackage::ImportMeTestOnly + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + other/__package.rb:3: Exported from package here + 3 |class Foo::Bar::OtherPackage < PackageSpec + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Autocorrect: Done + __package.rb:6: Inserted `test_import Foo::Bar::OtherPackage` + 6 | # import Foo::Bar::OtherPackage ## MISSING! + ^ +Errors: 7 -------------------------------------------------------------------------- diff --git a/test/cli/package-prefix-enforcement/test.out b/test/cli/package-prefix-enforcement/test.out index 00f722ff98..96eb5a631e 100644 --- a/test/cli/package-prefix-enforcement/test.out +++ b/test/cli/package-prefix-enforcement/test.out @@ -91,7 +91,21 @@ nested/nested.test.rb:16: Tests in the `Root::Nested` package must define tests 3 |class Root::Nested < PackageSpec ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -critic_prefix/real.test.rb:13: File belongs to package `Critic::SomePkg` but defines a constant that does not match this namespace https://srb.help/3713 +critic_prefix/real.test.rb:3: Tests in the `Critic::SomePkg` package must define tests in the `Test::Critic::SomePkg` namespace https://srb.help/3713 + 3 |module Critic::SomePkg::Real + ^^^^^^^^^^^^^^^^^^^^^ + critic_prefix/__package.rb:3: Enclosing package declared here + 3 |class Critic::SomePkg < PackageSpec + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +critic_prefix/real.test.rb:7: Tests in the `Critic::SomePkg` package must define tests in the `Test::Critic::SomePkg` namespace https://srb.help/3713 + 7 |Critic::SomePkg::RealConst = 2 + ^^^^^^^^^^^^^^^^^^^^^^^^^^ + critic_prefix/__package.rb:3: Enclosing package declared here + 3 |class Critic::SomePkg < PackageSpec + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +critic_prefix/real.test.rb:13: Tests in the `Critic::SomePkg` package must define tests in the `Test::Critic::SomePkg` namespace https://srb.help/3713 13 |SomeOtherNamespace::SomePkg::SomeConst = 4 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ critic_prefix/__package.rb:3: Enclosing package declared here @@ -107,4 +121,4 @@ commands/foo_command.rb:4: File belongs to package `Root::Commands::Foo` but def __package.rb:3: Must belong to this package, given constant name `Root::Commands::Baz::Ban` 3 |class Root < PackageSpec ^^^^^^^^^^^^^^^^^^^^^^^^ -Errors: 11 +Errors: 13 diff --git a/test/cli/package-prefix-enforcement/test.sh b/test/cli/package-prefix-enforcement/test.sh index 3d4c213147..4b151eebcd 100755 --- a/test/cli/package-prefix-enforcement/test.sh +++ b/test/cli/package-prefix-enforcement/test.sh @@ -1,3 +1,3 @@ cd test/cli/package-prefix-enforcement || exit 1 -../../../main/sorbet --silence-dev-message --stripe-packages --secondary-test-package-namespaces=Critic --max-threads=0 . 2>&1 +../../../main/sorbet --silence-dev-message --stripe-packages --max-threads=0 . 2>&1 diff --git a/test/cli/package-secondary-test-namespace-invalid/__package.rb b/test/cli/package-secondary-test-namespace-invalid/__package.rb deleted file mode 100644 index e1f8c11c66..0000000000 --- a/test/cli/package-secondary-test-namespace-invalid/__package.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -# typed: strict -# enable-packager: true - -class Critic < PackageSpec -end diff --git a/test/cli/package-secondary-test-namespace-invalid/test.out b/test/cli/package-secondary-test-namespace-invalid/test.out deleted file mode 100644 index 5dc23f83eb..0000000000 --- a/test/cli/package-secondary-test-namespace-invalid/test.out +++ /dev/null @@ -1,3 +0,0 @@ ---secondary-test-package-namespaces must contain items that start with a capital letter and are alphanumeric. ---secondary-test-package-namespaces must contain items that start with a capital letter and are alphanumeric. ---secondary-test-package-namespaces must contain items that start with a capital letter and are alphanumeric. diff --git a/test/cli/package-secondary-test-namespace-invalid/test.sh b/test/cli/package-secondary-test-namespace-invalid/test.sh deleted file mode 100755 index ad9f34ceb2..0000000000 --- a/test/cli/package-secondary-test-namespace-invalid/test.sh +++ /dev/null @@ -1,5 +0,0 @@ -cd test/cli/package-secondary-test-namespace-invalid || exit 1 - -../../../main/sorbet --silence-dev-message --stripe-packages --secondary-test-package-namespaces=Critic:: --stripe-mode . 2>&1 -../../../main/sorbet --silence-dev-message --stripe-packages --secondary-test-package-namespaces=critic --stripe-mode . 2>&1 -../../../main/sorbet --silence-dev-message --stripe-packages --secondary-test-package-namespaces=^ --stripe-mode . 2>&1 diff --git a/test/cli/package-secondary-test-namespace/test.sh b/test/cli/package-secondary-test-namespace/test.sh index 72d28b33a5..a15c2d76b4 100755 --- a/test/cli/package-secondary-test-namespace/test.sh +++ b/test/cli/package-secondary-test-namespace/test.sh @@ -1,4 +1,4 @@ cd test/cli/package-secondary-test-namespace || exit 0 -../../../main/sorbet --silence-dev-message --stripe-packages --secondary-test-package-namespaces=Critic,Minitest --stripe-mode . 2>&1 +../../../main/sorbet --silence-dev-message --stripe-packages --stripe-mode . 2>&1 diff --git a/test/cli/package-skip-import-visibility-check-for/test.sh b/test/cli/package-skip-import-visibility-check-for/test.sh deleted file mode 100755 index 61eb08e03b..0000000000 --- a/test/cli/package-skip-import-visibility-check-for/test.sh +++ /dev/null @@ -1,5 +0,0 @@ -cd test/cli/package-skip-import-visibility-check-for || exit 1 - -../../../main/sorbet --silence-dev-message --stripe-packages --skip-package-import-visibility-check-for=Project::Root --max-threads=0 . 2>&1 - - diff --git a/test/cli/package-special-allowed-dsls/test.out b/test/cli/package-special-allowed-dsls/test.out index a5c6b1a3b8..eef53a3479 100644 --- a/test/cli/package-special-allowed-dsls/test.out +++ b/test/cli/package-special-allowed-dsls/test.out @@ -1,22 +1,20 @@ -baz/__package.rb:4: Argument to `autoloader_compatibility` must be a string literal https://srb.help/3706 +baz/__package.rb:4: Method `autoloader_compatibility` does not exist on `T.class_of(Project::Baz)` https://srb.help/7003 4 | autoloader_compatibility :invalid - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ^^^^^^^^^^^^^^^^^^^^^^^^ -baz/__package.rb:5: Argument to `autoloader_compatibility` can only be 'legacy' https://srb.help/3706 +baz/__package.rb:5: Method `autoloader_compatibility` does not exist on `T.class_of(Project::Baz)` https://srb.help/7003 5 | autoloader_compatibility 'something' - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ^^^^^^^^^^^^^^^^^^^^^^^^ -baz/__package.rb:6: Argument to `autoloader_compatibility` must be a string literal https://srb.help/3706 +baz/__package.rb:6: Method `autoloader_compatibility` does not exist on `T.class_of(Project::Baz)` https://srb.help/7003 6 | autoloader_compatibility :legacy - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Autocorrect: Use `-a` to autocorrect - baz/__package.rb:6: Replace with `"legacy"` - 6 | autoloader_compatibility :legacy - ^^^^^^^ + ^^^^^^^^^^^^^^^^^^^^^^^^ -foo/__package.rb:7: The 'strict' argument has been deprecated as an argument to `autoloader_compatibility` https://srb.help/3706 +foo/__package.rb:7: Method `autoloader_compatibility` does not exist on `T.class_of(Project::Foo)` https://srb.help/7003 7 | autoloader_compatibility 'strict' - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Note: - If you wish to mark your package as strictly path-based-autoloading compatible, do not provide an autoloader_compatibility annotation -Errors: 4 + ^^^^^^^^^^^^^^^^^^^^^^^^ + +bar/__package.rb:4: Method `autoloader_compatibility` does not exist on `T.class_of(Project::Bar)` https://srb.help/7003 + 4 | autoloader_compatibility 'legacy' + ^^^^^^^^^^^^^^^^^^^^^^^^ +Errors: 5 diff --git a/test/cli/package-test-simple/test.sh b/test/cli/package-test-simple/test.sh index debd7f963f..586c6ec3b6 100755 --- a/test/cli/package-test-simple/test.sh +++ b/test/cli/package-test-simple/test.sh @@ -1,3 +1,3 @@ cd test/cli/package-test-simple || exit 1 -../../../main/sorbet --silence-dev-message --stripe-packages --max-threads=0 --secondary-test-package-namespaces=Critic --stripe-mode --stripe-packages-hint-message="RUN SCRIPT HINT" . 2>&1 +../../../main/sorbet --silence-dev-message --stripe-packages --max-threads=0 --stripe-mode --stripe-packages-hint-message="RUN SCRIPT HINT" . 2>&1 diff --git a/test/cli/packager_did_you_mean/__package.rb b/test/cli/packager_did_you_mean/__package.rb index 8618bf6c4d..de4a7b960a 100644 --- a/test/cli/packager_did_you_mean/__package.rb +++ b/test/cli/packager_did_you_mean/__package.rb @@ -2,6 +2,6 @@ # typed: strict # enable-packager: true -class ProjectWithLongDiscernableName::Foo < PackageSpec - export ProjectWithLongDiscernableName::Foo::Example +class ProjectWithLongDiscernibleName::Foo < PackageSpec + export ProjectWithLongDiscernibleName::Foo::Example end diff --git a/test/cli/packager_did_you_mean/test.out b/test/cli/packager_did_you_mean/test.out index 9df402f9c7..bbddc94686 100644 --- a/test/cli/packager_did_you_mean/test.out +++ b/test/cli/packager_did_you_mean/test.out @@ -1,4 +1,4 @@ -__package.rb:6: Unable to resolve constant `ProjectWithLongDiscernableName` https://srb.help/5002 - 6 | export ProjectWithLongDiscernableName::Foo::Example +__package.rb:6: Unable to resolve constant `ProjectWithLongDiscernibleName` https://srb.help/5002 + 6 | export ProjectWithLongDiscernibleName::Foo::Example ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Errors: 1 diff --git a/test/cli/packager_suggest_nested_crash/test.out b/test/cli/packager_suggest_nested_crash/test.out index a4fe8f44b4..70f2b4818e 100644 --- a/test/cli/packager_suggest_nested_crash/test.out +++ b/test/cli/packager_suggest_nested_crash/test.out @@ -1,25 +1,11 @@ -test/cli/packager_suggest_nested_crash/consumer_auth/data/identifier_struct.rb:4: Tests in the `Project::ConsumerAuth::Data` package must define tests in the `Test::Project::ConsumerAuth::Data` namespace https://srb.help/3713 - 4 |class Project::ConsumerAuth::Data - ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - test/cli/packager_suggest_nested_crash/consumer_auth/data/__package.rb:4: Enclosing package declared here - 4 |class Project::ConsumerAuth::Data < PackageSpec - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -test/cli/packager_suggest_nested_crash/consumer_auth/data/identifier_struct.rb:6: `Project::ConsumerAuth::IdentifierType` resolves but is not exported from `Project::ConsumerAuth` https://srb.help/3717 +consumer_auth/data/identifier_struct.rb:6: `Project::ConsumerAuth::IdentifierType` resolves but is not exported from `Project::ConsumerAuth` https://srb.help/3717 6 | puts(Project::ConsumerAuth::IdentifierType) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - test/cli/packager_suggest_nested_crash/consumer_auth/identity_type.rb:5: Defined here + consumer_auth/identity_type.rb:5: Defined here 5 | class IdentifierType ^^^^^^^^^^^^^^^^^^^^ Autocorrect: Use `-a` to autocorrect - test/cli/packager_suggest_nested_crash/consumer_auth/__package.rb:6: Insert `export Project::ConsumerAuth::IdentifierType` + consumer_auth/__package.rb:6: Insert `export Project::ConsumerAuth::IdentifierType` 6 | import Project::ConsumerAuth::Data ^ - -test/cli/packager_suggest_nested_crash/consumer_auth/identity_type.rb:4: Tests in the `Project::ConsumerAuth` package must define tests in the `Test::Project::ConsumerAuth` namespace https://srb.help/3713 - 4 |class Project::ConsumerAuth - ^^^^^^^^^^^^^^^^^^^^^ - test/cli/packager_suggest_nested_crash/consumer_auth/__package.rb:5: Enclosing package declared here - 5 |class Project::ConsumerAuth < PackageSpec - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Errors: 3 +Errors: 1 diff --git a/test/cli/packager_suggest_nested_crash/test.sh b/test/cli/packager_suggest_nested_crash/test.sh index 28a8ae4f97..d50f0a66d4 100755 --- a/test/cli/packager_suggest_nested_crash/test.sh +++ b/test/cli/packager_suggest_nested_crash/test.sh @@ -1,3 +1,4 @@ #!/usr/bin/env bash -main/sorbet --silence-dev-message --stripe-packages --max-threads=0 . 2>&1 +cd test/cli/packager_suggest_nested_crash || exit 0 +../../../main/sorbet --silence-dev-message --stripe-packages --max-threads=0 . 2>&1 diff --git a/test/cli/phases/test.out b/test/cli/phases/test.out index f261cc97ce..ca35062c30 100644 --- a/test/cli/phases/test.out +++ b/test/cli/phases/test.out @@ -67,133 +67,97 @@ ClassDef{ } --- index-tree-raw end --- --- name-tree start --- -begin - class <>> < (::) - 1 - end - +class <>> < (::) + 1 end --- name-tree end --- --- name-tree-raw start --- -InsSeq{ - stats = [ - ClassDef{ - kind = class - name = EmptyTree - symbol = >> - ancestors = [ConstantLit{ - symbol = (class ::) - orig = nullptr - }] - rhs = [ - Literal{ value = 1 } - ] - } - ], - expr = EmptyTree +ClassDef{ + kind = class + name = EmptyTree + symbol = >> + ancestors = [ConstantLit{ + symbol = (class ::) + orig = nullptr + }] + rhs = [ + Literal{ value = 1 } + ] } --- name-tree-raw end --- --- resolve-tree start --- -begin - class <>> < (::) - 1 - end - +class <>> < (::) + 1 end --- resolve-tree end --- --- resolve-tree-raw start --- -InsSeq{ - stats = [ - ClassDef{ - kind = class - name = EmptyTree - symbol = >> - ancestors = [ConstantLit{ - symbol = (class ::) - orig = nullptr - }] - rhs = [ - Literal{ value = 1 } - ] - } - ], - expr = EmptyTree +ClassDef{ + kind = class + name = EmptyTree + symbol = >> + ancestors = [ConstantLit{ + symbol = (class ::) + orig = nullptr + }] + rhs = [ + Literal{ value = 1 } + ] } --- resolve-tree-raw end --- --- flatten-tree start --- -begin - - class <>> < (::) - def self.<$CENSORED>() - 1 - end +class <>> < (::) + def self.<$CENSORED>() + 1 end - end --- flatten-tree end --- --- flatten-tree-raw start --- -InsSeq{ - stats = [ - EmptyTree - ClassDef{ - kind = class - name = EmptyTree - symbol = >> - ancestors = [ConstantLit{ - symbol = (class ::) - orig = nullptr +ClassDef{ + kind = class + name = EmptyTree + symbol = >> + ancestors = [ConstantLit{ + symbol = (class ::) + orig = nullptr + }] + rhs = [ + MethodDef{ + flags = {self} + name = ><> $CENSORED>> + args = [Local{ + localVariable = > }] - rhs = [ - MethodDef{ - flags = {self} - name = ><> $CENSORED>> - args = [Local{ - localVariable = > - }] - rhs = Literal{ value = 1 } - } - ] + rhs = Literal{ value = 1 } } - ], - expr = EmptyTree + ] } --- flatten-tree-raw end --- --- ast start --- -begin - - class <>> < (::) - def self.<$CENSORED>() - 1 - end +class <>> < (::) + def self.<$CENSORED>() + 1 end - end --- ast end --- --- ast-raw start --- -InsSeq{ - stats = [ - EmptyTree - ClassDef{ - kind = class - name = EmptyTree - symbol = >> - ancestors = [ConstantLit{ - symbol = (class ::) - orig = nullptr +ClassDef{ + kind = class + name = EmptyTree + symbol = >> + ancestors = [ConstantLit{ + symbol = (class ::) + orig = nullptr + }] + rhs = [ + MethodDef{ + flags = {self} + name = ><> $CENSORED>> + args = [Local{ + localVariable = > }] - rhs = [ - MethodDef{ - flags = {self} - name = ><> $CENSORED>> - args = [Local{ - localVariable = > - }] - rhs = Literal{ value = 1 } - } - ] + rhs = Literal{ value = 1 } } - ], - expr = EmptyTree + ] } --- ast-raw end --- --- cfg start --- @@ -203,7 +167,7 @@ subgraph "cluster_::>#" { color = blue; "bb::>#_0" [ - shape = invhouse; + shape = cds; color = black; label = "block[id=0, rubyRegionId=0]()\l: T.class_of() = cast(: NilClass, T.class_of());\l$2: Integer(1) = 1\l: T.noreturn = return $2: Integer(1)\l\l" ]; @@ -231,7 +195,7 @@ subgraph "cluster_::>#" { color = blue; "bb::>#_0" [ - shape = invhouse; + shape = cds; color = black; label = "block[id=0]()\lBinding {\l bind = VariableUseSite {\l  variable = >,\l  type = T.class_of(),\l },\l value = Cast {\l  cast = T.cast,\l  value = VariableUseSite {\l   variable = >,\l   type = NilClass,\l  },\l  type = T.class_of(),\l },\l}\lBinding {\l bind = VariableUseSite {\l  variable = >$2,\l  type = Integer(1),\l },\l value = Literal { value = Integer(1) },\l}\lBinding {\l bind = VariableUseSite {\l  variable = >,\l  type = T.noreturn,\l },\l value = Return {\l  what = VariableUseSite {\l   variable = >$2,\l   type = Integer(1),\l  },\l },\l}\lVariableUseSite { variable = > }\l" ]; @@ -282,53 +246,43 @@ class >> < > () --- symbol-table-raw end --- --- checking diff --- 1,15d0 -1,25d0 -1,9d0 +1,19d0 +1,5d0 < < < < -< localVariable = > -< }] -< args = [Local{ -< flags = {self} -< name = ><> $CENSORED>> -< orig = nullptr -< rhs = Literal{ value = 1 } -< symbol = (class ::) -< MethodDef{ -< } +< localVariable = > < }] -< 1 -< ] -< ancestors = [ConstantLit{ -< kind = class -< name = EmptyTree -< rhs = [ -< symbol = >> +< args = [Local{ +< flags = {self} +< name = ><> $CENSORED>> +< orig = nullptr +< rhs = Literal{ value = 1 } +< symbol = (class ::) +< 1 < : T.noreturn = return $2: Integer(1) < $2: Integer(1) = 1 < : T.class_of() = cast(: NilClass, T.class_of()); < -> bb1 < -> bb1 -< ClassDef{ -< EmptyTree -< def self.<$CENSORED>() -< end +< MethodDef{ < } -< -< -< ], -< class <>> < (::) +< }] +< ] +< ancestors = [ConstantLit{ +< def self.<$CENSORED>() < end -< expr = EmptyTree -< stats = [ +< kind = class +< name = EmptyTree +< rhs = [ +< symbol = >> < # - bb0(rubyRegionId=0) < # backedges -< InsSeq{ +< ClassDef{ < bb0[rubyRegionId=0, firstDead=3](): < bb1[rubyRegionId=0, firstDead=-1](): -< begin +< class <>> < (::) < end < method ::># { < } diff --git a/test/cli/print_to_file_v2/test.out b/test/cli/print_to_file_v2/test.out deleted file mode 100644 index 7471ba8eea..0000000000 --- a/test/cli/print_to_file_v2/test.out +++ /dev/null @@ -1,257 +0,0 @@ ---- autogen.txt start --- -# ParsedFile: test/cli/print_to_file_v2/a.rb -requires: [] -## defs: -[def id=0] - type=module - defines_behavior=0 - is_empty=0 -[def id=1] - type=module - defines_behavior=0 - is_empty=0 - defining_ref=[A] -[def id=2] - type=class - defines_behavior=0 - is_empty=1 - defining_ref=[Foo] -## refs: -[ref id=0] - scope=[] - name=[A] - nesting=[] - resolved=[A] - loc=test/cli/print_to_file_v2/a.rb:3 - is_defining_ref=1 -[ref id=1] - scope=[A] - name=[Foo] - nesting=[[A]] - resolved=[A Foo] - loc=test/cli/print_to_file_v2/a.rb:4 - is_defining_ref=1 -# ParsedFile: test/cli/print_to_file_v2/b.rb -requires: [] -## defs: -[def id=0] - type=module - defines_behavior=0 - is_empty=0 -[def id=1] - type=module - defines_behavior=0 - is_empty=0 - defining_ref=[B] -[def id=2] - type=class - defines_behavior=0 - is_empty=1 - defining_ref=[Bar] -## refs: -[ref id=0] - scope=[] - name=[B] - nesting=[] - resolved=[B] - loc=test/cli/print_to_file_v2/b.rb:3 - is_defining_ref=1 -[ref id=1] - scope=[B] - name=[Bar] - nesting=[[B]] - resolved=[B Bar] - loc=test/cli/print_to_file_v2/b.rb:4 - is_defining_ref=1 -# ParsedFile: test/cli/print_to_file_v2/c.rb -requires: [] -## defs: -[def id=0] - type=module - defines_behavior=0 - is_empty=0 -[def id=1] - type=module - defines_behavior=0 - is_empty=0 - defining_ref=[C] -[def id=2] - type=class - defines_behavior=0 - is_empty=1 - defining_ref=[Bar] -[def id=3] - type=class - defines_behavior=1 - is_empty=0 - defining_ref=[BarChild] - parent_ref=[Bar] -[def id=4] - type=module - defines_behavior=1 - is_empty=0 - defining_ref=[TestExtend] -## refs: -[ref id=0] - scope=[] - name=[C] - nesting=[] - resolved=[C] - loc=test/cli/print_to_file_v2/c.rb:3 - is_defining_ref=1 -[ref id=1] - scope=[C] - name=[Bar] - nesting=[[C]] - resolved=[C Bar] - loc=test/cli/print_to_file_v2/c.rb:4 - is_defining_ref=1 -[ref id=2] - scope=[C] - name=[BarChild] - nesting=[[C]] - resolved=[C BarChild] - loc=test/cli/print_to_file_v2/c.rb:6 - is_defining_ref=1 -[ref id=3] - scope=[C] - name=[Bar] - nesting=[[C]] - resolved=[C Bar] - loc=test/cli/print_to_file_v2/c.rb:6 - is_defining_ref=0 - parent_of=[C BarChild] -[ref id=4] - scope=[] - name=[X] - nesting=[] - resolved=[] - loc=test/cli/print_to_file_v2/c.rb:7 - is_defining_ref=0 - parent_of=[C BarChild] -[ref id=5] - scope=[] - name=[InMethodX] - nesting=[] - resolved=[] - loc=test/cli/print_to_file_v2/c.rb:9 - is_defining_ref=0 -[ref id=6] - scope=[C] - name=[TestExtend] - nesting=[[C]] - resolved=[C TestExtend] - loc=test/cli/print_to_file_v2/c.rb:13 - is_defining_ref=1 -[ref id=7] - scope=[] - name=[Y] - nesting=[] - resolved=[] - loc=test/cli/print_to_file_v2/c.rb:14 - is_defining_ref=0 -[ref id=8] - scope=[] - name=[InMethodY] - nesting=[] - resolved=[] - loc=test/cli/print_to_file_v2/c.rb:16 - is_defining_ref=0 -[ref id=9] - scope=[] - name=[ViaMethod] - nesting=[] - resolved=[] - loc=test/cli/print_to_file_v2/c.rb:19 - is_defining_ref=0 -# ParsedFile: test/cli/print_to_file_v2/d.rb -requires: [] -## defs: -[def id=0] - type=module - defines_behavior=0 - is_empty=0 -[def id=1] - type=module - defines_behavior=0 - is_empty=1 - defining_ref=[MyMixin] -[def id=2] - type=class - defines_behavior=1 - is_empty=0 - defining_ref=[MyClass] -[def id=3] - type=alias - defines_behavior=1 - is_empty=0 - defining_ref=[X] - aliased_ref=[Y] -[def id=4] - type=casgn - defines_behavior=1 - is_empty=0 - defining_ref=[Z] -## refs: -[ref id=0] - scope=[] - name=[MyMixin] - nesting=[] - resolved=[MyMixin] - loc=test/cli/print_to_file_v2/d.rb:3 - is_defining_ref=1 -[ref id=1] - scope=[] - name=[MyClass] - nesting=[] - resolved=[MyClass] - loc=test/cli/print_to_file_v2/d.rb:4 - is_defining_ref=1 -[ref id=2] - scope=[MyClass] - name=[MyMixin] - nesting=[[MyClass]] - resolved=[MyMixin] - loc=test/cli/print_to_file_v2/d.rb:6 - is_defining_ref=0 - parent_of=[MyClass] -[ref id=3] - scope=[MyClass] - name=[X] - nesting=[[MyClass]] - resolved=[MyClass X] - loc=test/cli/print_to_file_v2/d.rb:5 - is_defining_ref=1 -[ref id=4] - scope=[MyClass] - name=[Y] - nesting=[[MyClass]] - resolved=[] - loc=test/cli/print_to_file_v2/d.rb:5 - is_defining_ref=0 -[ref id=5] - scope=[MyClass] - name=[Z] - nesting=[[MyClass]] - resolved=[MyClass Z] - loc=test/cli/print_to_file_v2/d.rb:7 - is_defining_ref=1 -[ref id=6] - scope=[MyClass] - name=[T] - nesting=[[MyClass]] - resolved=[T] - loc=test/cli/print_to_file_v2/d.rb:7 - is_defining_ref=0 -[ref id=7] - scope=[MyClass] - name=[String] - nesting=[[MyClass]] - resolved=[String] - loc=test/cli/print_to_file_v2/d.rb:7 - is_defining_ref=0 ---- autogen.txt end --- - -bebe1bec5a305f6c01a56ce81b2dbbab7fd1b194 autogen.msgpack - ---print=cfg specified multiple times with inconsistent output options diff --git a/test/cli/print_to_file_v2/test.sh b/test/cli/print_to_file_v2/test.sh deleted file mode 100755 index 3a8b7d6b7f..0000000000 --- a/test/cli/print_to_file_v2/test.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -set -eu - -main/sorbet --silence-dev-message --stop-after namer --autogen-version=2 \ - -p autogen:autogen.txt -p autogen-msgpack:autogen.msgpack \ - test/cli/print_to_file_v2/*.rb - -echo "--- autogen.txt start ---" -cat autogen.txt -echo "--- autogen.txt end ---" -echo - -shasum autogen.msgpack - -echo -# Do not allow a print option to use inconsistent paths -main/sorbet --silence-dev-message -p cfg -p cfg:out -e '1' 2>&1 diff --git a/test/cli/print_to_file_v3/a.rb b/test/cli/print_to_file_v3/a.rb deleted file mode 100644 index cad7afff65..0000000000 --- a/test/cli/print_to_file_v3/a.rb +++ /dev/null @@ -1,6 +0,0 @@ -# typed: true - -module A - class Foo - end -end diff --git a/test/cli/print_to_file_v3/b.rb b/test/cli/print_to_file_v3/b.rb deleted file mode 100644 index a66e8cdda1..0000000000 --- a/test/cli/print_to_file_v3/b.rb +++ /dev/null @@ -1,6 +0,0 @@ -# typed: true - -module B - class Bar - end -end diff --git a/test/cli/print_to_file_v3/c.rb b/test/cli/print_to_file_v3/c.rb deleted file mode 100644 index d155e544b9..0000000000 --- a/test/cli/print_to_file_v3/c.rb +++ /dev/null @@ -1,22 +0,0 @@ -# typed: true - -module C - class Bar; end - - class BarChild < Bar; - include ::X - some_method do - include ::InMethodX - end - end - - module TestExtend - extend ::Y - some_method do - extend ::InMethodY - end - - app.include ::ViaMethod - end -end - diff --git a/test/cli/print_to_file_v3/d.rb b/test/cli/print_to_file_v3/d.rb deleted file mode 100644 index 86c817a656..0000000000 --- a/test/cli/print_to_file_v3/d.rb +++ /dev/null @@ -1,8 +0,0 @@ -# typed: true -# -module MyMixin; end -class MyClass - X = Y - include MyMixin - Z = T.type_alias { String } -end diff --git a/test/cli/print_to_file_v3/test.out b/test/cli/print_to_file_v3/test.out deleted file mode 100644 index be6a8a9b21..0000000000 --- a/test/cli/print_to_file_v3/test.out +++ /dev/null @@ -1,257 +0,0 @@ ---- autogen.txt start --- -# ParsedFile: test/cli/print_to_file_v3/a.rb -requires: [] -## defs: -[def id=0] - type=module - defines_behavior=0 - is_empty=0 -[def id=1] - type=module - defines_behavior=0 - is_empty=0 - defining_ref=[A] -[def id=2] - type=class - defines_behavior=0 - is_empty=1 - defining_ref=[Foo] -## refs: -[ref id=0] - scope=[] - name=[A] - nesting=[] - resolved=[A] - loc=test/cli/print_to_file_v3/a.rb:3 - is_defining_ref=1 -[ref id=1] - scope=[A] - name=[Foo] - nesting=[[A]] - resolved=[A Foo] - loc=test/cli/print_to_file_v3/a.rb:4 - is_defining_ref=1 -# ParsedFile: test/cli/print_to_file_v3/b.rb -requires: [] -## defs: -[def id=0] - type=module - defines_behavior=0 - is_empty=0 -[def id=1] - type=module - defines_behavior=0 - is_empty=0 - defining_ref=[B] -[def id=2] - type=class - defines_behavior=0 - is_empty=1 - defining_ref=[Bar] -## refs: -[ref id=0] - scope=[] - name=[B] - nesting=[] - resolved=[B] - loc=test/cli/print_to_file_v3/b.rb:3 - is_defining_ref=1 -[ref id=1] - scope=[B] - name=[Bar] - nesting=[[B]] - resolved=[B Bar] - loc=test/cli/print_to_file_v3/b.rb:4 - is_defining_ref=1 -# ParsedFile: test/cli/print_to_file_v3/c.rb -requires: [] -## defs: -[def id=0] - type=module - defines_behavior=0 - is_empty=0 -[def id=1] - type=module - defines_behavior=0 - is_empty=0 - defining_ref=[C] -[def id=2] - type=class - defines_behavior=0 - is_empty=1 - defining_ref=[Bar] -[def id=3] - type=class - defines_behavior=1 - is_empty=0 - defining_ref=[BarChild] - parent_ref=[Bar] -[def id=4] - type=module - defines_behavior=1 - is_empty=0 - defining_ref=[TestExtend] -## refs: -[ref id=0] - scope=[] - name=[C] - nesting=[] - resolved=[C] - loc=test/cli/print_to_file_v3/c.rb:3 - is_defining_ref=1 -[ref id=1] - scope=[C] - name=[Bar] - nesting=[[C]] - resolved=[C Bar] - loc=test/cli/print_to_file_v3/c.rb:4 - is_defining_ref=1 -[ref id=2] - scope=[C] - name=[BarChild] - nesting=[[C]] - resolved=[C BarChild] - loc=test/cli/print_to_file_v3/c.rb:6 - is_defining_ref=1 -[ref id=3] - scope=[C] - name=[Bar] - nesting=[[C]] - resolved=[C Bar] - loc=test/cli/print_to_file_v3/c.rb:6 - is_defining_ref=0 - parent_of=[C BarChild] -[ref id=4] - scope=[] - name=[X] - nesting=[] - resolved=[] - loc=test/cli/print_to_file_v3/c.rb:7 - is_defining_ref=0 - parent_of=[C BarChild] -[ref id=5] - scope=[] - name=[InMethodX] - nesting=[] - resolved=[] - loc=test/cli/print_to_file_v3/c.rb:9 - is_defining_ref=0 -[ref id=6] - scope=[C] - name=[TestExtend] - nesting=[[C]] - resolved=[C TestExtend] - loc=test/cli/print_to_file_v3/c.rb:13 - is_defining_ref=1 -[ref id=7] - scope=[] - name=[Y] - nesting=[] - resolved=[] - loc=test/cli/print_to_file_v3/c.rb:14 - is_defining_ref=0 -[ref id=8] - scope=[] - name=[InMethodY] - nesting=[] - resolved=[] - loc=test/cli/print_to_file_v3/c.rb:16 - is_defining_ref=0 -[ref id=9] - scope=[] - name=[ViaMethod] - nesting=[] - resolved=[] - loc=test/cli/print_to_file_v3/c.rb:19 - is_defining_ref=0 -# ParsedFile: test/cli/print_to_file_v3/d.rb -requires: [] -## defs: -[def id=0] - type=module - defines_behavior=0 - is_empty=0 -[def id=1] - type=module - defines_behavior=0 - is_empty=1 - defining_ref=[MyMixin] -[def id=2] - type=class - defines_behavior=1 - is_empty=0 - defining_ref=[MyClass] -[def id=3] - type=alias - defines_behavior=1 - is_empty=0 - defining_ref=[X] - aliased_ref=[Y] -[def id=4] - type=typealias - defines_behavior=1 - is_empty=0 - defining_ref=[Z] -## refs: -[ref id=0] - scope=[] - name=[MyMixin] - nesting=[] - resolved=[MyMixin] - loc=test/cli/print_to_file_v3/d.rb:3 - is_defining_ref=1 -[ref id=1] - scope=[] - name=[MyClass] - nesting=[] - resolved=[MyClass] - loc=test/cli/print_to_file_v3/d.rb:4 - is_defining_ref=1 -[ref id=2] - scope=[MyClass] - name=[MyMixin] - nesting=[[MyClass]] - resolved=[MyMixin] - loc=test/cli/print_to_file_v3/d.rb:6 - is_defining_ref=0 - parent_of=[MyClass] -[ref id=3] - scope=[MyClass] - name=[X] - nesting=[[MyClass]] - resolved=[MyClass X] - loc=test/cli/print_to_file_v3/d.rb:5 - is_defining_ref=1 -[ref id=4] - scope=[MyClass] - name=[Y] - nesting=[[MyClass]] - resolved=[] - loc=test/cli/print_to_file_v3/d.rb:5 - is_defining_ref=0 -[ref id=5] - scope=[MyClass] - name=[Z] - nesting=[[MyClass]] - resolved=[MyClass Z] - loc=test/cli/print_to_file_v3/d.rb:7 - is_defining_ref=1 -[ref id=6] - scope=[MyClass] - name=[T] - nesting=[[MyClass]] - resolved=[T] - loc=test/cli/print_to_file_v3/d.rb:7 - is_defining_ref=0 -[ref id=7] - scope=[MyClass] - name=[String] - nesting=[[MyClass]] - resolved=[String] - loc=test/cli/print_to_file_v3/d.rb:7 - is_defining_ref=0 ---- autogen.txt end --- - -ef1f9c175a81b441fabaf42a3fc947b94ff3f0de autogen.msgpack - ---print=cfg specified multiple times with inconsistent output options diff --git a/test/cli/print_to_file_v3/test.sh b/test/cli/print_to_file_v3/test.sh deleted file mode 100755 index 6fcdae2af3..0000000000 --- a/test/cli/print_to_file_v3/test.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -set -eu - -main/sorbet --silence-dev-message --stop-after namer --autogen-version=3 \ - -p autogen:autogen.txt -p autogen-msgpack:autogen.msgpack \ - test/cli/print_to_file_v3/*.rb - -echo "--- autogen.txt start ---" -cat autogen.txt -echo "--- autogen.txt end ---" -echo - -shasum autogen.msgpack - -echo -# Do not allow a print option to use inconsistent paths -main/sorbet --silence-dev-message -p cfg -p cfg:out -e '1' 2>&1 diff --git a/test/cli/print_to_file_v2/a.rb b/test/cli/print_to_file_v5/a.rb similarity index 100% rename from test/cli/print_to_file_v2/a.rb rename to test/cli/print_to_file_v5/a.rb diff --git a/test/cli/print_to_file_v2/b.rb b/test/cli/print_to_file_v5/b.rb similarity index 100% rename from test/cli/print_to_file_v2/b.rb rename to test/cli/print_to_file_v5/b.rb diff --git a/test/cli/print_to_file_v2/c.rb b/test/cli/print_to_file_v5/c.rb similarity index 100% rename from test/cli/print_to_file_v2/c.rb rename to test/cli/print_to_file_v5/c.rb diff --git a/test/cli/print_to_file_v2/d.rb b/test/cli/print_to_file_v5/d.rb similarity index 100% rename from test/cli/print_to_file_v2/d.rb rename to test/cli/print_to_file_v5/d.rb diff --git a/test/cli/print_to_file_v5/test.out b/test/cli/print_to_file_v5/test.out new file mode 100644 index 0000000000..985e6962a4 --- /dev/null +++ b/test/cli/print_to_file_v5/test.out @@ -0,0 +1,257 @@ +--- autogen.txt start --- +# ParsedFile: test/cli/print_to_file_v5/a.rb +requires: [] +## defs: +[def id=0] + type=module + defines_behavior=0 + is_empty=0 +[def id=1] + type=module + defines_behavior=0 + is_empty=0 + defining_ref=[A] +[def id=2] + type=class + defines_behavior=0 + is_empty=1 + defining_ref=[Foo] +## refs: +[ref id=0] + scope=[] + name=[A] + nesting=[] + resolved=[A] + loc=test/cli/print_to_file_v5/a.rb:3 + is_defining_ref=1 +[ref id=1] + scope=[A] + name=[Foo] + nesting=[[A]] + resolved=[A Foo] + loc=test/cli/print_to_file_v5/a.rb:4 + is_defining_ref=1 +# ParsedFile: test/cli/print_to_file_v5/b.rb +requires: [] +## defs: +[def id=0] + type=module + defines_behavior=0 + is_empty=0 +[def id=1] + type=module + defines_behavior=0 + is_empty=0 + defining_ref=[B] +[def id=2] + type=class + defines_behavior=0 + is_empty=1 + defining_ref=[Bar] +## refs: +[ref id=0] + scope=[] + name=[B] + nesting=[] + resolved=[B] + loc=test/cli/print_to_file_v5/b.rb:3 + is_defining_ref=1 +[ref id=1] + scope=[B] + name=[Bar] + nesting=[[B]] + resolved=[B Bar] + loc=test/cli/print_to_file_v5/b.rb:4 + is_defining_ref=1 +# ParsedFile: test/cli/print_to_file_v5/c.rb +requires: [] +## defs: +[def id=0] + type=module + defines_behavior=0 + is_empty=0 +[def id=1] + type=module + defines_behavior=0 + is_empty=0 + defining_ref=[C] +[def id=2] + type=class + defines_behavior=0 + is_empty=1 + defining_ref=[Bar] +[def id=3] + type=class + defines_behavior=1 + is_empty=0 + defining_ref=[BarChild] + parent_ref=[Bar] +[def id=4] + type=module + defines_behavior=1 + is_empty=0 + defining_ref=[TestExtend] +## refs: +[ref id=0] + scope=[] + name=[C] + nesting=[] + resolved=[C] + loc=test/cli/print_to_file_v5/c.rb:3 + is_defining_ref=1 +[ref id=1] + scope=[C] + name=[Bar] + nesting=[[C]] + resolved=[C Bar] + loc=test/cli/print_to_file_v5/c.rb:4 + is_defining_ref=1 +[ref id=2] + scope=[C] + name=[BarChild] + nesting=[[C]] + resolved=[C BarChild] + loc=test/cli/print_to_file_v5/c.rb:6 + is_defining_ref=1 +[ref id=3] + scope=[C] + name=[Bar] + nesting=[[C]] + resolved=[C Bar] + loc=test/cli/print_to_file_v5/c.rb:6 + is_defining_ref=0 + parent_of=[C BarChild] +[ref id=4] + scope=[] + name=[X] + nesting=[] + resolved=[] + loc=test/cli/print_to_file_v5/c.rb:7 + is_defining_ref=0 + parent_of=[C BarChild] +[ref id=5] + scope=[] + name=[InMethodX] + nesting=[] + resolved=[] + loc=test/cli/print_to_file_v5/c.rb:9 + is_defining_ref=0 +[ref id=6] + scope=[C] + name=[TestExtend] + nesting=[[C]] + resolved=[C TestExtend] + loc=test/cli/print_to_file_v5/c.rb:13 + is_defining_ref=1 +[ref id=7] + scope=[] + name=[Y] + nesting=[] + resolved=[] + loc=test/cli/print_to_file_v5/c.rb:14 + is_defining_ref=0 +[ref id=8] + scope=[] + name=[InMethodY] + nesting=[] + resolved=[] + loc=test/cli/print_to_file_v5/c.rb:16 + is_defining_ref=0 +[ref id=9] + scope=[] + name=[ViaMethod] + nesting=[] + resolved=[] + loc=test/cli/print_to_file_v5/c.rb:19 + is_defining_ref=0 +# ParsedFile: test/cli/print_to_file_v5/d.rb +requires: [] +## defs: +[def id=0] + type=module + defines_behavior=0 + is_empty=0 +[def id=1] + type=module + defines_behavior=0 + is_empty=1 + defining_ref=[MyMixin] +[def id=2] + type=class + defines_behavior=1 + is_empty=0 + defining_ref=[MyClass] +[def id=3] + type=alias + defines_behavior=1 + is_empty=0 + defining_ref=[X] + aliased_ref=[Y] +[def id=4] + type=typealias + defines_behavior=1 + is_empty=0 + defining_ref=[Z] +## refs: +[ref id=0] + scope=[] + name=[MyMixin] + nesting=[] + resolved=[MyMixin] + loc=test/cli/print_to_file_v5/d.rb:3 + is_defining_ref=1 +[ref id=1] + scope=[] + name=[MyClass] + nesting=[] + resolved=[MyClass] + loc=test/cli/print_to_file_v5/d.rb:4 + is_defining_ref=1 +[ref id=2] + scope=[MyClass] + name=[MyMixin] + nesting=[[MyClass]] + resolved=[MyMixin] + loc=test/cli/print_to_file_v5/d.rb:6 + is_defining_ref=0 + parent_of=[MyClass] +[ref id=3] + scope=[MyClass] + name=[X] + nesting=[[MyClass]] + resolved=[MyClass X] + loc=test/cli/print_to_file_v5/d.rb:5 + is_defining_ref=1 +[ref id=4] + scope=[MyClass] + name=[Y] + nesting=[[MyClass]] + resolved=[] + loc=test/cli/print_to_file_v5/d.rb:5 + is_defining_ref=0 +[ref id=5] + scope=[MyClass] + name=[Z] + nesting=[[MyClass]] + resolved=[MyClass Z] + loc=test/cli/print_to_file_v5/d.rb:7 + is_defining_ref=1 +[ref id=6] + scope=[MyClass] + name=[T] + nesting=[[MyClass]] + resolved=[T] + loc=test/cli/print_to_file_v5/d.rb:7 + is_defining_ref=0 +[ref id=7] + scope=[MyClass] + name=[String] + nesting=[[MyClass]] + resolved=[String] + loc=test/cli/print_to_file_v5/d.rb:7 + is_defining_ref=0 +--- autogen.txt end --- + +1e04100ab5481a15bb7d1cc185ce7169fcc3c917 autogen.msgpack + +--print=cfg specified multiple times with inconsistent output options diff --git a/test/cli/print_to_file_v5/test.sh b/test/cli/print_to_file_v5/test.sh new file mode 100755 index 0000000000..0f00bf243b --- /dev/null +++ b/test/cli/print_to_file_v5/test.sh @@ -0,0 +1,17 @@ +#!/bin/bash +set -eu + +main/sorbet --silence-dev-message --stop-after namer --autogen-version=5 \ + -p autogen:autogen.txt -p autogen-msgpack:autogen.msgpack \ + test/cli/print_to_file_v5/*.rb + +echo "--- autogen.txt start ---" +cat autogen.txt +echo "--- autogen.txt end ---" +echo + +shasum autogen.msgpack + +echo +# Do not allow a print option to use inconsistent paths +main/sorbet --silence-dev-message -p cfg -p cfg:out -e '1' 2>&1 diff --git a/test/cli/setter-does-not-exist/test.out b/test/cli/setter-does-not-exist/test.out new file mode 100644 index 0000000000..fccb876da3 --- /dev/null +++ b/test/cli/setter-does-not-exist/test.out @@ -0,0 +1,33 @@ +test/cli/setter-does-not-exist/test.rb:11: Setter method `foo=` does not exist on `A` https://srb.help/7003 + 11 |A.new(foo: 0).foo = 1 + ^^^^^ + Got `A` originating from: + test/cli/setter-does-not-exist/test.rb:11: + 11 |A.new(foo: 0).foo = 1 + ^^^^^^^^^^^^^ + test/cli/setter-does-not-exist/test.rb:4: Method `foo` defined here without a corresponding setter + 4 | const :foo, Integer + ^^^^^^^^^^^^^^^^^^^ + +test/cli/setter-does-not-exist/test.rb:12: Setter method `bar=` does not exist on `A` https://srb.help/7003 + 12 |A.new(foo: 0).bar = 1 + ^^^^^ + Got `A` originating from: + test/cli/setter-does-not-exist/test.rb:12: + 12 |A.new(foo: 0).bar = 1 + ^^^^^^^^^^^^^ + test/cli/setter-does-not-exist/test.rb:6: Method `bar` defined here without a corresponding setter + 6 | attr_reader :bar + ^^^^^^^^^^^^^^^^ + +test/cli/setter-does-not-exist/test.rb:13: Setter method `qux=` does not exist on `A` https://srb.help/7003 + 13 |A.new(foo: 0).qux = 1 + ^^^^^ + Got `A` originating from: + test/cli/setter-does-not-exist/test.rb:13: + 13 |A.new(foo: 0).qux = 1 + ^^^^^^^^^^^^^ + test/cli/setter-does-not-exist/test.rb:8: Method `qux` defined here without a corresponding setter + 8 | def qux; end + ^^^^^^^ +Errors: 3 diff --git a/test/cli/setter-does-not-exist/test.rb b/test/cli/setter-does-not-exist/test.rb new file mode 100644 index 0000000000..8f4f9ee450 --- /dev/null +++ b/test/cli/setter-does-not-exist/test.rb @@ -0,0 +1,13 @@ +# typed: true + +class A < T::Struct + const :foo, Integer + + attr_reader :bar + + def qux; end +end + +A.new(foo: 0).foo = 1 +A.new(foo: 0).bar = 1 +A.new(foo: 0).qux = 1 diff --git a/test/cli/setter-does-not-exist/test.sh b/test/cli/setter-does-not-exist/test.sh new file mode 100755 index 0000000000..4b97b8d698 --- /dev/null +++ b/test/cli/setter-does-not-exist/test.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +set -e + +main/sorbet --silence-dev-message test/cli/setter-does-not-exist/test.rb 2>&1 diff --git a/test/cli/statsd-invalid-link/test.out b/test/cli/statsd-invalid-link/test.out index 29e722d985..a3bbd7b7bb 100644 --- a/test/cli/statsd-invalid-link/test.out +++ b/test/cli/statsd-invalid-link/test.out @@ -1,2 +1,2 @@ Exception::raise(): statsd initialization failed: host=f4K#l1N&.com port=8200 prefix=foo.bar.counters -caught N6sorbet15SorbetExceptionE: statsd initialization failed: host=f4K#l1N&.com port=8200 prefix=foo.bar.counters +Sorbet raised uncaught exception type="sorbet::SorbetException" what="statsd initialization failed: host=f4K#l1N&.com port=8200 prefix=foo.bar.counters" diff --git a/test/cli/statsd-invalid-link/test.sh b/test/cli/statsd-invalid-link/test.sh index 42f0c8ef94..9186aad59b 100755 --- a/test/cli/statsd-invalid-link/test.sh +++ b/test/cli/statsd-invalid-link/test.sh @@ -1,3 +1,3 @@ #!/bin/bash -# checking exception is raised when host is invlaid +# checking exception is raised when host is invalid main/sorbet --silence-dev-message -e 'class Foo; end' --statsd-host="f4K#l1N&.com" --statsd-prefix=foo.bar 2> >(grep --color=never "statsd initialization failed") diff --git a/test/cli/struct_fuzz/struct_fuzz.rb b/test/cli/struct_fuzz/struct_fuzz.rb new file mode 100644 index 0000000000..144020072b --- /dev/null +++ b/test/cli/struct_fuzz/struct_fuzz.rb @@ -0,0 +1,4 @@ +# typed: true +::B=Struct.new:x +# these errors aren't actually what we're checking for, we just want to make sure sorbet doesn't crash on this input +# when no-stdlib is true. diff --git a/test/cli/struct_fuzz/test.out b/test/cli/struct_fuzz/test.out new file mode 100644 index 0000000000..5ccd3008c3 --- /dev/null +++ b/test/cli/struct_fuzz/test.out @@ -0,0 +1,44 @@ +test/cli/struct_fuzz/struct_fuzz.rb:2: Method `raise` does not exist on `T.class_of(Kernel)` https://srb.help/7003 + 2 |::B=Struct.new:x + ^^^^^^^^^^^^^^^^ + Got `T.class_of(Kernel)` originating from: + test/cli/struct_fuzz/struct_fuzz.rb:2: + 2 |::B=Struct.new:x + ^^^^^^^^^^^^^^^^ + +test/cli/struct_fuzz/struct_fuzz.rb:2: Method `raise` does not exist on `T.class_of(Kernel)` https://srb.help/7003 + 2 |::B=Struct.new:x + ^^^^^^^^^^^^^^^^ + Got `T.class_of(Kernel)` originating from: + test/cli/struct_fuzz/struct_fuzz.rb:2: + 2 |::B=Struct.new:x + ^^^^^^^^^^^^^^^^ + +test/cli/struct_fuzz/struct_fuzz.rb:2: Method `raise` does not exist on `T.class_of(Kernel)` https://srb.help/7003 + 2 |::B=Struct.new:x + ^^^^^^^^^^^^^^^^ + Got `T.class_of(Kernel)` originating from: + test/cli/struct_fuzz/struct_fuzz.rb:2: Possibly uninitialized (`NilClass`) in: + 2 |::B=Struct.new:x + ^^^^^^^^^^^^^^^^ + +test/cli/struct_fuzz/struct_fuzz.rb:2: Method `returns` does not exist on `T.class_of(B)` https://srb.help/7003 + 2 |::B=Struct.new:x + ^ + +test/cli/struct_fuzz/struct_fuzz.rb:2: Method `params` does not exist on `T.class_of(B)` https://srb.help/7003 + 2 |::B=Struct.new:x + ^ + +test/cli/struct_fuzz/struct_fuzz.rb:2: Method `params` does not exist on `T.class_of(B)` https://srb.help/7003 + 2 |::B=Struct.new:x + ^^^^^^^^^^^^^^^^ + +test/cli/struct_fuzz/struct_fuzz.rb:2: Method `type_member` does not exist on `T.class_of(B)` https://srb.help/7003 + 2 |::B=Struct.new:x + ^^^^^^^^^^^^^^^^ + +test/cli/struct_fuzz/struct_fuzz.rb:2: Method `type_member` does not exist on `T.class_of(B)` https://srb.help/7003 + 2 |::B=Struct.new:x + ^^^^^^^^^^^^^^^^ +Errors: 8 diff --git a/test/cli/struct_fuzz/test.sh b/test/cli/struct_fuzz/test.sh new file mode 100755 index 0000000000..ab9ba7f08c --- /dev/null +++ b/test/cli/struct_fuzz/test.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +main/sorbet --censor-for-snapshot-tests --silence-dev-message --no-stdlib test/cli/struct_fuzz/struct_fuzz.rb 2>&1 diff --git a/test/cli/struct_strict/test.out b/test/cli/struct_strict/test.out deleted file mode 100644 index eb0cfbf836..0000000000 --- a/test/cli/struct_strict/test.out +++ /dev/null @@ -1,14 +0,0 @@ -test/cli/struct_strict/test.rb:7: The method `foo` does not have a `sig` https://srb.help/7017 - 7 |Bad = Struct.new(:foo) do - ^^^ - Note: - Struct classes defined with `Struct` are hard to use in `# typed: strict` files. - Consider using `T::Struct` instead. - -test/cli/struct_strict/test.rb:7: The method `foo=` does not have a `sig` https://srb.help/7017 - 7 |Bad = Struct.new(:foo) do - ^^^ - Note: - Struct classes defined with `Struct` are hard to use in `# typed: strict` files. - Consider using `T::Struct` instead. -Errors: 2 diff --git a/test/cli/struct_strict/test.rb b/test/cli/struct_strict/test.rb deleted file mode 100644 index d77247f91a..0000000000 --- a/test/cli/struct_strict/test.rb +++ /dev/null @@ -1,16 +0,0 @@ -# typed: strict - -class Module - include T::Sig -end - -Bad = Struct.new(:foo) do -end - -Okay = Struct.new(:foo) do - sig {returns(T.nilable(Integer))} - def foo; super; end - - sig {params(foo: T.nilable(Integer)).returns(T.nilable(Integer))} - def foo=(foo); super; end -end diff --git a/test/cli/struct_strict/test.sh b/test/cli/struct_strict/test.sh deleted file mode 100755 index 1cb151e288..0000000000 --- a/test/cli/struct_strict/test.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -if main/sorbet --silence-dev-message test/cli/struct_strict/test.rb 2>&1 ; then - echo "expected to fail!" - exit 1 -fi diff --git a/test/cli/suggest-class-new-not-singleton/test.sh b/test/cli/suggest-class-new-not-singleton/test.sh index c7f8637ad6..4be654f372 100755 --- a/test/cli/suggest-class-new-not-singleton/test.sh +++ b/test/cli/suggest-class-new-not-singleton/test.sh @@ -16,7 +16,7 @@ echo echo -------------------------------------------------------------------------- echo -# Also cat the file, to make that the autocorrect applied +# Also cat the file, to make sure that the autocorrect applied cat suggest-class-new-not-singleton.rb rm suggest-class-new-not-singleton.rb diff --git a/test/cli/suggest-kernel/suggest-kernel.rb b/test/cli/suggest-kernel/suggest-kernel.rb index 169a5f849a..e77255f3e3 100644 --- a/test/cli/suggest-kernel/suggest-kernel.rb +++ b/test/cli/suggest-kernel/suggest-kernel.rb @@ -2,5 +2,7 @@ module Foo def foo raise "hi" + Integer(0) + Array(0) end end diff --git a/test/cli/suggest-kernel/test.out b/test/cli/suggest-kernel/test.out index ef3c578b63..5700fb613e 100644 --- a/test/cli/suggest-kernel/test.out +++ b/test/cli/suggest-kernel/test.out @@ -2,5 +2,65 @@ test/cli/suggest-kernel/suggest-kernel.rb:4: Method `raise` does not exist on `F .. | raise "hi" ^^^^^ Note: - Did you mean to `include Kernel` in this module? -Errors: 1 + `raise` is actually defined as a method on `Kernel`. To call it, either + `include Kernel` in this module to ensure the method is always there, or + call the method using `Kernel.raise` instead. + Autocorrect: Use `-a` to autocorrect + test/cli/suggest-kernel/suggest-kernel.rb:4: Insert `Kernel.` + .. | raise "hi" + ^ + +test/cli/suggest-kernel/suggest-kernel.rb:5: Method `Integer` does not exist on `Foo` https://srb.help/7003 + .. | Integer(0) + ^^^^^^^ + Note: + `Integer` is actually defined as a method on `Kernel`. To call it, either + `include Kernel` in this module to ensure the method is always there, or + call the method using `Kernel.Integer` instead. + Autocorrect: Use `-a` to autocorrect + test/cli/suggest-kernel/suggest-kernel.rb:5: Insert `Kernel.` + .. | Integer(0) + ^ + Note: + Ruby uses `.new` to invoke a class's constructor + Autocorrect: Use `-a` to autocorrect + test/cli/suggest-kernel/suggest-kernel.rb:5: Insert `.new` + .. | Integer(0) + ^ + Note: + Ruby uses `.new` to invoke a class's constructor + Autocorrect: Use `-a` to autocorrect + test/cli/suggest-kernel/suggest-kernel.rb:5: Insert `.new` + .. | Integer(0) + ^ + Note: + Ruby uses `.new` to invoke a class's constructor + Autocorrect: Use `-a` to autocorrect + test/cli/suggest-kernel/suggest-kernel.rb:5: Insert `.new` + .. | Integer(0) + ^ + +test/cli/suggest-kernel/suggest-kernel.rb:6: Method `Array` does not exist on `Foo` https://srb.help/7003 + .. | Array(0) + ^^^^^ + Note: + `Array` is actually defined as a method on `Kernel`. To call it, either + `include Kernel` in this module to ensure the method is always there, or + call the method using `Kernel.Array` instead. + Autocorrect: Use `-a` to autocorrect + test/cli/suggest-kernel/suggest-kernel.rb:6: Insert `Kernel.` + .. | Array(0) + ^ + Note: + Ruby uses `.new` to invoke a class's constructor + Autocorrect: Use `-a` to autocorrect + test/cli/suggest-kernel/suggest-kernel.rb:6: Insert `.new` + .. | Array(0) + ^ + Note: + Ruby uses `.new` to invoke a class's constructor + Autocorrect: Use `-a` to autocorrect + test/cli/suggest-kernel/suggest-kernel.rb:6: Insert `.new` + .. | Array(0) + ^ +Errors: 3 diff --git a/test/cli/suggest-sig-override-edge/suggest-sig-override-edge.rb b/test/cli/suggest-sig-override-edge/suggest-sig-override-edge.rb index a73d5c3f20..16a6c81013 100644 --- a/test/cli/suggest-sig-override-edge/suggest-sig-override-edge.rb +++ b/test/cli/suggest-sig-override-edge/suggest-sig-override-edge.rb @@ -1,6 +1,6 @@ # typed: strict -# initialize can be overriden without overridable/override +# initialize can be overridden without overridable/override class ParentInitialize extend T::Sig sig {void} diff --git a/test/cli/suggest-sig-override-edge/test.out b/test/cli/suggest-sig-override-edge/test.out index bed72ea5c1..8589c94590 100644 --- a/test/cli/suggest-sig-override-edge/test.out +++ b/test/cli/suggest-sig-override-edge/test.out @@ -1,6 +1,6 @@ # typed: strict -# initialize can be overriden without overridable/override +# initialize can be overridden without overridable/override class ParentInitialize extend T::Sig sig {void} diff --git a/test/cli/suggest-sig/test.out b/test/cli/suggest-sig/test.out index 8a14ea8891..b7691e28da 100644 --- a/test/cli/suggest-sig/test.out +++ b/test/cli/suggest-sig/test.out @@ -177,11 +177,14 @@ suggest-sig.rb:85: This code is unreachable https://srb.help/7006 ^^^^ suggest-sig.rb:85: This condition was always `truthy` (`TrueClass`) 85 | if true || qux || blah - ^^^^^^^^^^^ + ^^^^ Got `TrueClass` originating from: suggest-sig.rb:85: 85 | if true || qux || blah ^^^^ + suggest-sig.rb:85: + 85 | if true || qux || blah + ^^^^ suggest-sig.rb:88: This code is unreachable https://srb.help/7006 88 | takesString(x) @@ -195,7 +198,10 @@ suggest-sig.rb:88: This code is unreachable https://srb.help/7006 ^^^^ suggest-sig.rb:85: 85 | if true || qux || blah - ^^^^^^^^^^^ + ^^^^ + suggest-sig.rb:85: + 85 | if true || qux || blah + ^^^^ suggest-sig.rb:84: The method `dead` does not have a `sig` https://srb.help/7017 84 |def dead(x) @@ -287,34 +293,6 @@ suggest-sig.rb:43: The method `a_protected` does not have a `sig` https://srb.he 43 | protected def a_protected; end ^ -suggest-sig.rb:102: The method `a` does not have a `sig` https://srb.help/7017 - 102 |Foo = Struct.new(:a, :b) - ^ - Note: - Struct classes defined with `Struct` are hard to use in `# typed: strict` files. - Consider using `T::Struct` instead. - -suggest-sig.rb:102: The method `a=` does not have a `sig` https://srb.help/7017 - 102 |Foo = Struct.new(:a, :b) - ^ - Note: - Struct classes defined with `Struct` are hard to use in `# typed: strict` files. - Consider using `T::Struct` instead. - -suggest-sig.rb:102: The method `b` does not have a `sig` https://srb.help/7017 - 102 |Foo = Struct.new(:a, :b) - ^ - Note: - Struct classes defined with `Struct` are hard to use in `# typed: strict` files. - Consider using `T::Struct` instead. - -suggest-sig.rb:102: The method `b=` does not have a `sig` https://srb.help/7017 - 102 |Foo = Struct.new(:a, :b) - ^ - Note: - Struct classes defined with `Struct` are hard to use in `# typed: strict` files. - Consider using `T::Struct` instead. - suggest-sig.rb:112: The method `load_account_business_profile` does not have a `sig` https://srb.help/7017 112 | def self.load_account_business_profile(merchant) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -385,7 +363,7 @@ suggest-sig.rb:204: The method `returns_int` does not have a `sig` https://srb.h suggest-sig.rb:204: Inserted `sig(:final) { returns(Integer) }` 204 | def returns_int; 42; end ^ -Errors: 47 +Errors: 43 -------------------------------------------------------------------------- diff --git a/test/cli/suggest-singleton/test.out b/test/cli/suggest-singleton/test.out index 2621ac7adc..c76ae51b95 100644 --- a/test/cli/suggest-singleton/test.out +++ b/test/cli/suggest-singleton/test.out @@ -19,15 +19,17 @@ suggest-singleton.rb:13: Method `bar` does not exist on `A` https://srb.help/700 suggest-singleton.rb:13: 13 |A.new.bar ^^^^^ - Note: - Did you mean to call `A.bar` which is a singleton class method? + There is a singleton class method with the same name: + suggest-singleton.rb:5: Defined here + 5 | def self.bar + ^^^^^^^^^^^^ + Either: + - use `.class` to call it, or + - remove `self.` from its definition to make it an instance method Autocorrect: Done suggest-singleton.rb:13: Inserted `.class` 13 |A.new.bar ^ - suggest-singleton.rb:5: `bar` defined here - 5 | def self.bar - ^^^^^^^^^^^^ suggest-singleton.rb:14: Method `my_attribute` does not exist on `T.class_of(A)` https://srb.help/7003 14 |A.my_attribute diff --git a/test/cli/suggest-singleton/test.sh b/test/cli/suggest-singleton/test.sh index bee97e5730..fe5bc1407e 100755 --- a/test/cli/suggest-singleton/test.sh +++ b/test/cli/suggest-singleton/test.sh @@ -17,7 +17,7 @@ echo echo -------------------------------------------------------------------------- echo -# Also cat the file, to make that the autocorrect applied +# Also cat the file, to make sure that the autocorrect applied cat suggest-singleton.rb rm suggest-singleton.rb diff --git a/test/cli/suggest_initialize_sig/suggest_initialize_sig.rb b/test/cli/suggest_initialize_sig/suggest_initialize_sig.rb deleted file mode 100644 index a47f809abc..0000000000 --- a/test/cli/suggest_initialize_sig/suggest_initialize_sig.rb +++ /dev/null @@ -1,94 +0,0 @@ -# typed: true - -class SimpleReturns - extend T::Sig - - sig {params(x: Integer).returns(Integer).on_failure(:soft, notify: 'sorbet')} - def initialize(x) - @x = x - end -end - -class SimpleMultiLineReturns - extend T::Sig - - sig do - params( - x: Integer - ) - .returns(Integer) - end - def initialize(x) - @x = x - end -end - -class MultiLineReturnsWithCombinators - extend T::Sig - - sig do - params( - x: T::Array[T.any(String, T::Enum)] - ) - .returns(T::Array[T.any(String, T::Enum)]) - end - def initialize(x) - @x = x - end -end - -class SingleLineReturnsWithCombinators - extend T::Sig - - sig {params(x: T.nilable(Integer)).returns(T.nilable(Integer)).on_failure(:soft, notify: 'sorbet')} - def initialize(x) - @x = x - end -end - -class SingleLineNoAfterStatements - extend T::Sig - - sig {params(x: T.any(Integer, String)).returns(T.any(Integer, String))} - def initialize(x) - @x = x - end -end - -class ClassMethodInitializeIsIgnored - extend T::Sig - - sig {returns(ClassMethodInitializeIsIgnored)} - def self.initialize - new - end -end - -class LineBreakAfterReturns - extend T::Sig - - sig do - params( - path: String, - key: String - ) - .returns(T.self_type) - .checked(:tests) - end - def initialize(path, key) - end -end - -class LineBreakOnlyAtEnd - extend T::Sig - - sig do - params( - path: String, - key: String - ) - .returns(T.self_type).checked(:tests) - end - def initialize(path, key) - end -end diff --git a/test/cli/suggest_initialize_sig/test.out b/test/cli/suggest_initialize_sig/test.out deleted file mode 100644 index 30e8295efa..0000000000 --- a/test/cli/suggest_initialize_sig/test.out +++ /dev/null @@ -1,117 +0,0 @@ -test/cli/suggest_initialize_sig/suggest_initialize_sig.rb:6: The initialize method should always return void https://srb.help/3510 - 6 | sig {params(x: Integer).returns(Integer).on_failure(:soft, notify: 'sorbet')} - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Autocorrect: Use `-a` to autocorrect - test/cli/suggest_initialize_sig/suggest_initialize_sig.rb:6: Replace with `params(x: Integer).void.on_failure(:soft, notify: 'sorbet')` - 6 | sig {params(x: Integer).returns(Integer).on_failure(:soft, notify: 'sorbet')} - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -test/cli/suggest_initialize_sig/suggest_initialize_sig.rb:16: The initialize method should always return void https://srb.help/3510 - 16 | params( - 17 | x: Integer - 18 | ) - 19 | .returns(Integer) - Autocorrect: Use `-a` to autocorrect - test/cli/suggest_initialize_sig/suggest_initialize_sig.rb:16: Replace with `params( - x: Integer - ) - .void` - 16 | params( - 17 | x: Integer - 18 | ) - 19 | .returns(Integer) - -test/cli/suggest_initialize_sig/suggest_initialize_sig.rb:30: The initialize method should always return void https://srb.help/3510 - 30 | params( - 31 | x: T::Array[T.any(String, T::Enum)] - 32 | ) - 33 | .returns(T::Array[T.any(String, T::Enum)]) - Autocorrect: Use `-a` to autocorrect - test/cli/suggest_initialize_sig/suggest_initialize_sig.rb:30: Replace with `params( - x: T::Array[T.any(String, T::Enum)] - ) - .void` - 30 | params( - 31 | x: T::Array[T.any(String, T::Enum)] - 32 | ) - 33 | .returns(T::Array[T.any(String, T::Enum)]) - -test/cli/suggest_initialize_sig/suggest_initialize_sig.rb:43: The initialize method should always return void https://srb.help/3510 - 43 | sig {params(x: T.nilable(Integer)).returns(T.nilable(Integer)).on_failure(:soft, notify: 'sorbet')} - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Autocorrect: Use `-a` to autocorrect - test/cli/suggest_initialize_sig/suggest_initialize_sig.rb:43: Replace with `params(x: T.nilable(Integer)).void.on_failure(:soft, notify: 'sorbet')` - 43 | sig {params(x: T.nilable(Integer)).returns(T.nilable(Integer)).on_failure(:soft, notify: 'sorbet')} - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -test/cli/suggest_initialize_sig/suggest_initialize_sig.rb:52: The initialize method should always return void https://srb.help/3510 - 52 | sig {params(x: T.any(Integer, String)).returns(T.any(Integer, String))} - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Autocorrect: Use `-a` to autocorrect - test/cli/suggest_initialize_sig/suggest_initialize_sig.rb:52: Replace with `params(x: T.any(Integer, String)).void` - 52 | sig {params(x: T.any(Integer, String)).returns(T.any(Integer, String))} - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -test/cli/suggest_initialize_sig/suggest_initialize_sig.rb:71: The initialize method should always return void https://srb.help/3510 - 71 | params( - 72 | path: String, - 73 | key: String - 74 | ) - 75 | .returns(T.self_type) - 76 | .checked(:tests) - Autocorrect: Use `-a` to autocorrect - test/cli/suggest_initialize_sig/suggest_initialize_sig.rb:71: Replace with `params( - path: String, - key: String - ) - .void - .checked(:tests)` - 71 | params( - 72 | path: String, - 73 | key: String - 74 | ) - 75 | .returns(T.self_type) - 76 | .checked(:tests) - -test/cli/suggest_initialize_sig/suggest_initialize_sig.rb:86: The initialize method should always return void https://srb.help/3510 - 86 | params( - 87 | path: String, - 88 | key: String - 89 | ) - 90 | .returns(T.self_type).checked(:tests) - Autocorrect: Use `-a` to autocorrect - test/cli/suggest_initialize_sig/suggest_initialize_sig.rb:86: Replace with `params( - path: String, - key: String - ) - .void.checked(:tests)` - 86 | params( - 87 | path: String, - 88 | key: String - 89 | ) - 90 | .returns(T.self_type).checked(:tests) - -test/cli/suggest_initialize_sig/suggest_initialize_sig.rb:79: Expected `LineBreakAfterReturns` but found `NilClass` for method result type https://srb.help/7005 - 79 | end - ^^^ - Expected `LineBreakAfterReturns` for result type of method `initialize`: - test/cli/suggest_initialize_sig/suggest_initialize_sig.rb:78: - 78 | def initialize(path, key) - ^^^^^^^^^^^^^^^^^^^^^^^^^ - Got `NilClass` originating from: - test/cli/suggest_initialize_sig/suggest_initialize_sig.rb:78: Possibly uninitialized (`NilClass`) in: - 78 | def initialize(path, key) - ^^^^^^^^^^^^^^^^^^^^^^^^^ - -test/cli/suggest_initialize_sig/suggest_initialize_sig.rb:93: Expected `LineBreakOnlyAtEnd` but found `NilClass` for method result type https://srb.help/7005 - 93 | end - ^^^ - Expected `LineBreakOnlyAtEnd` for result type of method `initialize`: - test/cli/suggest_initialize_sig/suggest_initialize_sig.rb:92: - 92 | def initialize(path, key) - ^^^^^^^^^^^^^^^^^^^^^^^^^ - Got `NilClass` originating from: - test/cli/suggest_initialize_sig/suggest_initialize_sig.rb:92: Possibly uninitialized (`NilClass`) in: - 92 | def initialize(path, key) - ^^^^^^^^^^^^^^^^^^^^^^^^^ -Errors: 9 diff --git a/test/cli/suggest_initialize_sig/test.sh b/test/cli/suggest_initialize_sig/test.sh deleted file mode 100755 index 6d63bde830..0000000000 --- a/test/cli/suggest_initialize_sig/test.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -main/sorbet --silence-dev-message test/cli/suggest_initialize_sig/suggest_initialize_sig.rb 2>&1 diff --git a/test/cli/suggest_static/test.out b/test/cli/suggest_static/test.out index c16af89606..cbcc8a1bb6 100644 --- a/test/cli/suggest_static/test.out +++ b/test/cli/suggest_static/test.out @@ -5,15 +5,17 @@ suggest_static.rb:11: Method `foo` does not exist on `A` https://srb.help/7003 suggest_static.rb:11: 11 |A.new.foo ^^^^^ - Note: - Did you mean to call `A.foo` which is a singleton class method? + There is a singleton class method with the same name: + suggest_static.rb:3: Defined here + 3 | def self.foo + ^^^^^^^^^^^^ + Either: + - use `.class` to call it, or + - remove `self.` from its definition to make it an instance method Autocorrect: Done suggest_static.rb:11: Inserted `.class` 11 |A.new.foo ^ - suggest_static.rb:3: `foo` defined here - 3 | def self.foo - ^^^^^^^^^^^^ suggest_static.rb:21: Method `on_both` does not exist on `Left` component of `T.any(Left, Right)` https://srb.help/7003 21 |left_or_right.on_both @@ -22,15 +24,17 @@ suggest_static.rb:21: Method `on_both` does not exist on `Left` component of `T. suggest_static.rb:20: 20 |left_or_right = T.let(Left.new, T.any(Left, Right)) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Note: - Did you mean to call `Left.on_both` which is a singleton class method? + There is a singleton class method with the same name: + suggest_static.rb:14: Defined here + 14 | def self.on_both; end + ^^^^^^^^^^^^^^^^ + Either: + - use `.class` to call it, or + - remove `self.` from its definition to make it an instance method Autocorrect: Done suggest_static.rb:21: Inserted `.class` 21 |left_or_right.on_both ^ - suggest_static.rb:14: `on_both` defined here - 14 | def self.on_both; end - ^^^^^^^^^^^^^^^^ suggest_static.rb:21: Method `on_both` does not exist on `Right` component of `T.any(Left, Right)` https://srb.help/7003 21 |left_or_right.on_both @@ -39,28 +43,33 @@ suggest_static.rb:21: Method `on_both` does not exist on `Right` component of `T suggest_static.rb:20: 20 |left_or_right = T.let(Left.new, T.any(Left, Right)) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Note: - Did you mean to call `Right.on_both` which is a singleton class method? + There is a singleton class method with the same name: + suggest_static.rb:17: Defined here + 17 | def self.on_both; end + ^^^^^^^^^^^^^^^^ + Either: + - use `.class` to call it, or + - remove `self.` from its definition to make it an instance method Autocorrect: Done suggest_static.rb:21: Inserted `.class` 21 |left_or_right.on_both ^ - suggest_static.rb:17: `on_both` defined here - 17 | def self.on_both; end - ^^^^^^^^^^^^^^^^ suggest_static.rb:7: Method `foo` does not exist on `A` https://srb.help/7003 7 | foo ^^^ - Note: - Did you mean to call `A.foo` which is a singleton class method? + There is a singleton class method with the same name: + suggest_static.rb:3: Defined here + 3 | def self.foo + ^^^^^^^^^^^^ + Either: + - use `.class` to call it, + - remove `self.` from its definition to make it an instance method, or + - define the current method as a singleton class method using `def self.` Autocorrect: Done suggest_static.rb:7: Inserted `self.class.` 7 | foo ^ - suggest_static.rb:3: `foo` defined here - 3 | def self.foo - ^^^^^^^^^^^^ Errors: 4 -------------------------------------------------------------------------- diff --git a/test/cli/suggest_static/test.sh b/test/cli/suggest_static/test.sh index 930e0ed7aa..575ac69169 100755 --- a/test/cli/suggest_static/test.sh +++ b/test/cli/suggest_static/test.sh @@ -17,7 +17,7 @@ echo echo -------------------------------------------------------------------------- echo -# Also cat the file, to make that the autocorrect applied +# Also cat the file, to make sure that the autocorrect applied cat suggest_static.rb rm suggest_static.rb diff --git a/test/cli/suggest_t_must/test.out b/test/cli/suggest_t_must/test.out index a4ad1c012c..de82ba81d6 100644 --- a/test/cli/suggest_t_must/test.out +++ b/test/cli/suggest_t_must/test.out @@ -1,6 +1,6 @@ suggest_t_must.rb:4: Method `[]` does not exist on `NilClass` component of `T.nilable(String)` https://srb.help/7003 4 |foo[0] - ^^^^^^ + ^^^ Got `T.nilable(String)` originating from: suggest_t_must.rb:3: 3 |foo = T.let(nil, T.nilable(String)) diff --git a/test/cli/suggest_t_must/test.sh b/test/cli/suggest_t_must/test.sh index 78f2461205..09f1706f91 100755 --- a/test/cli/suggest_t_must/test.sh +++ b/test/cli/suggest_t_must/test.sh @@ -16,7 +16,7 @@ echo echo -------------------------------------------------------------------------- echo -# Also cat the file, to make that the autocorrect applied +# Also cat the file, to make sure that the autocorrect applied cat suggest_t_must.rb rm suggest_t_must.rb diff --git a/test/cli/suggest_t_unsafe/test.out b/test/cli/suggest_t_unsafe/test.out index 306d11bb65..04c8eeafb9 100644 --- a/test/cli/suggest_t_unsafe/test.out +++ b/test/cli/suggest_t_unsafe/test.out @@ -16,7 +16,7 @@ suggest_t_unsafe.rb:25: Expected `String` but found `T.nilable(String)` for argu suggest_t_unsafe.rb:8: Method `[]` does not exist on `NilClass` component of `T.nilable(String)` https://srb.help/7003 8 |foo[0] - ^^^^^^ + ^^^ Got `T.nilable(String)` originating from: suggest_t_unsafe.rb:7: 7 |foo = T.let(nil, T.nilable(String)) @@ -132,7 +132,7 @@ suggest_t_unsafe.rb:25: Expected `String` but found `T.nilable(String)` for argu suggest_t_unsafe.rb:8: Method `[]` does not exist on `NilClass` component of `T.nilable(String)` https://srb.help/7003 8 |foo[0] - ^^^^^^ + ^^^ Got `T.nilable(String)` originating from: suggest_t_unsafe.rb:7: 7 |foo = T.let(nil, T.nilable(String)) diff --git a/test/cli/suggest_t_unsafe/test.sh b/test/cli/suggest_t_unsafe/test.sh index 30619e4437..62eeb4af4e 100755 --- a/test/cli/suggest_t_unsafe/test.sh +++ b/test/cli/suggest_t_unsafe/test.sh @@ -18,7 +18,7 @@ tmp="$(mktemp -d)" echo -------------------------------------------------------------------------- echo - # Also cat the file, to make that the autocorrect applied + # Also cat the file, to make sure that the autocorrect applied cat suggest_t_unsafe.rb rm suggest_t_unsafe.rb @@ -41,7 +41,7 @@ echo echo -------------------------------------------------------------------------- echo - # Also cat the file, to make that the autocorrect applied + # Also cat the file, to make sure that the autocorrect applied cat suggest_t_unsafe.rb rm suggest_t_unsafe.rb diff --git a/test/cli/suggest_unsafe_dead/test.sh b/test/cli/suggest_unsafe_dead/test.sh index 4c5d936d30..0ab9837917 100755 --- a/test/cli/suggest_unsafe_dead/test.sh +++ b/test/cli/suggest_unsafe_dead/test.sh @@ -17,7 +17,7 @@ echo echo -------------------------------------------------------------------------- echo -# Also cat the file, to make that the autocorrect applied +# Also cat the file, to make sure that the autocorrect applied cat suggest_unsafe_dead.rb rm suggest_unsafe_dead.rb diff --git a/test/cli/super-did-you-mean/test.out b/test/cli/super-did-you-mean/test.out new file mode 100644 index 0000000000..50b388a74a --- /dev/null +++ b/test/cli/super-did-you-mean/test.out @@ -0,0 +1,6 @@ +test/cli/super-did-you-mean/test.rb:15: Method `no_parent_method` does not exist on ancestors of `Child` https://srb.help/7048 + 15 | super + ^^^^^ + Note: + For help fixing `super` errors: https://sorbet.org/docs/typed-super +Errors: 1 diff --git a/test/cli/super-did-you-mean/test.rb b/test/cli/super-did-you-mean/test.rb new file mode 100644 index 0000000000..4d1a04234b --- /dev/null +++ b/test/cli/super-did-you-mean/test.rb @@ -0,0 +1,17 @@ +# typed: strict +class Module; include T::Sig; end + +class Parent + sig {void} + def supper + # Previously, Sorbet would include this in the "did you mean" suggestions, + # because `supper` is like `` + end +end + +class Child < Parent + sig {void} + def no_parent_method + super + end +end diff --git a/test/cli/super-did-you-mean/test.sh b/test/cli/super-did-you-mean/test.sh new file mode 100755 index 0000000000..a7de8484de --- /dev/null +++ b/test/cli/super-did-you-mean/test.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +main/sorbet --silence-dev-message test/cli/super-did-you-mean/test.rb 2>&1 diff --git a/test/cli/suppress-payload-superclass-redefinition-for/complex.rbi b/test/cli/suppress-payload-superclass-redefinition-for/complex.rbi new file mode 100644 index 0000000000..fe6d4a145f --- /dev/null +++ b/test/cli/suppress-payload-superclass-redefinition-for/complex.rbi @@ -0,0 +1,5 @@ +# typed: true + +class NewParent; end +class Complex < NewParent +end diff --git a/test/cli/suppress-payload-superclass-redefinition-for/double.rb b/test/cli/suppress-payload-superclass-redefinition-for/double.rb new file mode 100644 index 0000000000..1474b744c8 --- /dev/null +++ b/test/cli/suppress-payload-superclass-redefinition-for/double.rb @@ -0,0 +1,8 @@ +# typed: true + +class Parent +end + +class Complex < Parent; end +class IRB::RelineInputMethod < Parent +end diff --git a/test/cli/suppress-payload-superclass-redefinition-for/irb.rbi b/test/cli/suppress-payload-superclass-redefinition-for/irb.rbi new file mode 100644 index 0000000000..f7afadf056 --- /dev/null +++ b/test/cli/suppress-payload-superclass-redefinition-for/irb.rbi @@ -0,0 +1,7 @@ +# typed: true + +# Note: If we ever change Sorbet's payload to make StdioInputMethod the +# canonical version, let's change this test to mention IRB::InputMethod, the +# old superclass, so that we don't have to delete the test. +class IRB::RelineInputMethod < ::IRB::StdioInputMethod +end diff --git a/test/cli/suppress-payload-superclass-redefinition-for/test.out b/test/cli/suppress-payload-superclass-redefinition-for/test.out new file mode 100644 index 0000000000..6cdd7c7f6b --- /dev/null +++ b/test/cli/suppress-payload-superclass-redefinition-for/test.out @@ -0,0 +1,12 @@ +-- complex.rbi -- +No errors! Great job. +-- complex.rbi -- +No errors! Great job. +-- expect constant resolution error -- +???: Unable to resolve constant `DoesNotExist` provided via `--suppress-payload-superclass-redefinition-for` https://srb.help/5002 + Note: + Double check that the provided class name exists, or delete this option + (which will be either from the command line args or from the `sorbet/config` file). +Errors: 1 +-- double.rb -- +No errors! Great job. diff --git a/test/cli/suppress-payload-superclass-redefinition-for/test.sh b/test/cli/suppress-payload-superclass-redefinition-for/test.sh new file mode 100755 index 0000000000..ea9c24a253 --- /dev/null +++ b/test/cli/suppress-payload-superclass-redefinition-for/test.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash + +set -euo pipefail + +cwd="$(pwd)" +cd test/cli/suppress-payload-superclass-redefinition-for + +echo "-- complex.rbi --" + +2>&1 "$cwd/main/sorbet" --silence-dev-message \ + --suppress-payload-superclass-redefinition-for=Complex complex.rbi + +echo "-- complex.rbi --" +2>&1 "$cwd/main/sorbet" --silence-dev-message \ + --suppress-payload-superclass-redefinition-for=IRB::RelineInputMethod irb.rbi + +echo "-- expect constant resolution error --" +if 2>&1 "$cwd/main/sorbet" --silence-dev-message -e '' \ + --suppress-payload-superclass-redefinition-for=DoesNotExist; then + echo "Expected to fail!" + exit 1 +fi + + +echo "-- double.rb --" +2>&1 "$cwd/main/sorbet" --silence-dev-message \ + --suppress-payload-superclass-redefinition-for=Complex \ + --suppress-payload-superclass-redefinition-for=IRB::RelineInputMethod \ + double.rb + diff --git a/test/cli/t_types_base/test.sh b/test/cli/t_types_base/test.sh index 1e03d04611..af7825d935 100755 --- a/test/cli/t_types_base/test.sh +++ b/test/cli/t_types_base/test.sh @@ -17,7 +17,7 @@ echo echo -------------------------------------------------------------------------- echo -# Also cat the file, to make that the autocorrect applied +# Also cat the file, to make sure that the autocorrect applied cat test.rb rm test.rb diff --git a/test/cli/test_one.sh b/test/cli/test_one.sh index 583bd1c99b..141f816070 100755 --- a/test/cli/test_one.sh +++ b/test/cli/test_one.sh @@ -2,7 +2,7 @@ script="$1" expect="$2" -export ASAN_SYMBOLIZER_PATH=`pwd`/external/llvm_toolchain_12_0_0/bin/llvm-symbolizer +export ASAN_SYMBOLIZER_PATH=`pwd`/external/llvm_toolchain_15_0_7/bin/llvm-symbolizer if ! diff "$expect" -u <("$script"); then cat <&1 +args=( + --censor-for-snapshot-tests + --silence-dev-message + --print=file-table-json + --max-threads=0 + test/cli/track-untyped +) + +echo --- implicit 'everywhere' --- +main/sorbet "${args[@]}" --track-untyped 2>&1 || true + +echo --- explicit 'nowhere' --- +main/sorbet "${args[@]}" --track-untyped=nowhere 2>&1 || true + +echo --- explicit 'everywhere' --- +main/sorbet "${args[@]}" --track-untyped=everywhere 2>&1 || true diff --git a/test/cli/type-member-template/test.out b/test/cli/type-member-template/test.out index b43014bb21..5d4a1f35e0 100755 --- a/test/cli/type-member-template/test.out +++ b/test/cli/type-member-template/test.out @@ -1,3 +1,34 @@ +test/cli/type-member-template/type-member-template.rb:24: Type `X` declared by parent `IFoo` must be re-declared in `T.class_of(Foo1)` https://srb.help/5014 + 24 |class Foo1 + ^^^^^^^^^^ + test/cli/type-member-template/type-member-template.rb:21: `X` declared in parent here + 21 | X = type_member + ^^^^^^^^^^^^^^^ + Note: + Did you mean to define `X` as a `type_template` instead? + test/cli/type-member-template/type-member-template.rb:27: A `type_member` with the same name is defined here + 27 | X = type_member + ^^^^^^^^^^^^^^^ + +test/cli/type-member-template/type-member-template.rb:33: `X` must be declared as a type_member (not a type_template) to match the parent https://srb.help/5018 + 33 | X = type_template + ^^^^^^^^^^^^^^^^^ + test/cli/type-member-template/type-member-template.rb:21: Declared in parent `IFoo` here + 21 | X = type_member + ^^^^^^^^^^^^^^^ + +test/cli/type-member-template/type-member-template.rb:36: Type `X` declared by parent `IFoo` must be re-declared in `Foo3` https://srb.help/5014 + 36 |class Foo3 + ^^^^^^^^^^ + test/cli/type-member-template/type-member-template.rb:21: `X` declared in parent here + 21 | X = type_member + ^^^^^^^^^^^^^^^ + Note: + Did you mean to define `X` as a `type_member` instead? + test/cli/type-member-template/type-member-template.rb:40: A `type_template` with the same name is defined here + 40 | X = type_member + ^^^^^^^^^^^^^^^ + test/cli/type-member-template/type-member-template.rb:10: `type_template` type `Template` used in an instance method definition https://srb.help/5072 10 | sig {returns(Template)} ^^^^^^^^ @@ -15,4 +46,4 @@ test/cli/type-member-template/type-member-template.rb:13: `type_member` type `Me ^^^^^^^^^^^^^^^^^^^^ Note: Only a `type_template` can be used in a singleton method definition. -Errors: 2 +Errors: 5 diff --git a/test/cli/type-member-template/type-member-template.rb b/test/cli/type-member-template/type-member-template.rb index 2cb677aa42..7fc5196caf 100644 --- a/test/cli/type-member-template/type-member-template.rb +++ b/test/cli/type-member-template/type-member-template.rb @@ -13,3 +13,30 @@ def foo; end sig {returns(Member)} def self.foo; end end + +# typed: true + +module IFoo + extend T::Generic + X = type_member +end + +class Foo1 + extend T::Generic + extend IFoo + X = type_member +end + +class Foo2 + extend T::Generic + include IFoo + X = type_template +end + +class Foo3 + include IFoo + class << self + extend T::Generic + X = type_member + end +end diff --git a/test/cli/type_argument_suggest_unsafe/test.sh b/test/cli/type_argument_suggest_unsafe/test.sh index c11ee4b166..dbb2c344f0 100755 --- a/test/cli/type_argument_suggest_unsafe/test.sh +++ b/test/cli/type_argument_suggest_unsafe/test.sh @@ -17,7 +17,7 @@ echo echo -------------------------------------------------------------------------- echo -# Also cat the file, to make that the autocorrect applied +# Also cat the file, to make sure that the autocorrect applied cat type_argument_suggest_unsafe.rb rm type_argument_suggest_unsafe.rb diff --git a/test/cli/type_member_bounds/test.sh b/test/cli/type_member_bounds/test.sh index 22d1b76bf5..0478fea5d4 100755 --- a/test/cli/type_member_bounds/test.sh +++ b/test/cli/type_member_bounds/test.sh @@ -18,7 +18,7 @@ tmp="$(mktemp -d)" echo -------------------------------------------------------------------------- echo - # Also cat the file, to make that the autocorrect applied + # Also cat the file, to make sure that the autocorrect applied cat test.rb rm test.rb @@ -41,7 +41,7 @@ echo echo -------------------------------------------------------------------------- echo - # Also cat the file, to make that the autocorrect applied + # Also cat the file, to make sure that the autocorrect applied cat test.rb rm test.rb diff --git a/test/compiler_tests.bzl b/test/compiler_tests.bzl deleted file mode 100644 index 5063dd3d7b..0000000000 --- a/test/compiler_tests.bzl +++ /dev/null @@ -1,515 +0,0 @@ -def basename(p): - return p.rpartition("/")[-1] - -def dirname(p): - dirname, sep, fname = p.rpartition("/") - if dirname: - return dirname.rstrip("/") - else: - return sep - -def dropExtension(p): - "TODO: handle multiple . in name" - return p.partition(".")[0] - -def compiler_tests(suite_name, all_paths, extra_args = [], tags = []): - # test_name-> {"path": String, "sentinel": String, "isMultiFile": bool, "disabled": bool, "too_slow": bool} - tests = {} - - for path in all_paths: - if path.endswith(".rb"): - prefix = dropExtension(basename(path).partition("__")[0]) - test_name = dirname(path) + "/" + prefix - - #test_name = test_name.replace("/", "_") - current = tests.get(test_name) - if None == current: - data = { - "path": dirname(path), - "sentinel": path, - "isMultiFile": "__" in path, - "disabled": "disabled" in path, - "too_slow": "too_slow" in path, - } - tests[test_name] = data - - oracle_tests = [] - validate_tests = [] - filecheck_tests = [] - too_slow_tests = [] - for name in tests.keys(): - test_name = "test_{}".format(name) - validate_exp = "validate_exp_{}".format(name) - filecheck = "filecheck_{}".format(name) - - path = tests[name]["path"] - sentinel = tests[name]["sentinel"] - sources_name = "test_{}_rb_source".format(name) - exps_name = "test_{}_exp".format(name) - expected_outfile = "{}.ruby.stdout".format(name) - expected_errfile = "{}.ruby.stderr".format(name) - expected_exitfile = "{}.ruby.exit".format(name) - build_dir = "{}.sorbet.build".format(name) - sorbet_exitfile = "{}.sorbet.exit".format(name) - sorbet_out = "{}.sorbet.stdout".format(name) - - # All of the expectations (if this test is a single file) - if tests[name]["isMultiFile"]: - exp_sources = [] - test_sources = native.glob(["{}__*.rb".format(name), "{}__*.rbi".format(name)]) - else: - exp_sources = native.glob(["{}.*.exp".format(name)]) - test_sources = [sentinel] - - # All of the test sources - native.filegroup( - name = sources_name, - srcs = test_sources, - visibility = ["//visibility:public"], - ) - - native.filegroup( - name = exps_name, - srcs = exp_sources, - visibility = ["//visibility:public"], - ) - - # Mark the too_slow tests as manual - extra_tags = [] - if tests[name]["too_slow"]: - extra_tags = ["manual"] - - oracle = "oracle_{}".format(name) - build_ruby_oracle( - name = oracle, - prefix = name, - test = sentinel, - srcs = sources_name, - log = "{}.ruby.log".format(name), - stdout = expected_outfile, - stderr = expected_errfile, - exit = expected_exitfile, - tags = tags + extra_tags, - ) - - extension = "extension_{}".format(name) - build_extension( - name = extension, - prefix = name, - srcs = sources_name, - out = build_dir, - log = "{}.sorbet.log".format(name), - stdout = sorbet_out, - exit = sorbet_exitfile, - tags = tags + extra_tags, - ) - - validate_sorbet_output_test( - name = test_name, - srcs = sources_name, - expected_failure = tests[name]["disabled"], - build = extension, - oracle = oracle, - tags = tags + extra_tags, - size = "small", - ) - - exp_tests = [] - if len(exp_sources) > 0: - validate_exp_test( - name = validate_exp, - srcs = sources_name, - exp_files = exps_name, - expected_failure = tests[name]["disabled"], - extension = extension, - tags = tags + extra_tags, - size = "small", - ) - - exp_tests = [validate_exp] - - filecheck_test( - name = filecheck, - srcs = sources_name, - extension = extension, - tags = tags + extra_tags, - size = "small", - ) - - if tests[name]["too_slow"]: - too_slow_tests.append(test_name) - too_slow_tests.extend(exp_tests) - else: - oracle_tests.append(test_name) - validate_tests.extend(exp_tests) - filecheck_tests.append(filecheck) - - native.test_suite( - name = suite_name, - tests = oracle_tests + validate_tests + filecheck_tests, - ) - - native.test_suite( - name = "{}_oracle".format(suite_name), - tests = oracle_tests, - ) - - native.test_suite( - name = "{}_validate_exp".format(suite_name), - tests = validate_tests, - ) - - native.test_suite( - name = "{}_filecheck".format(suite_name), - tests = filecheck_tests, - ) - - native.test_suite( - name = "{}_too_slow".format(suite_name), - tests = too_slow_tests, - tags = ["manual"], - ) - -RubyOracle = provider( - fields = { - "log": "The log of oracle generation", - "stdout": "The stdout from ruby", - "stderr": "The stderr from ruby", - "exit": "The exit code from ruby", - }, -) - -def _build_ruby_oracle_impl(ctx): - outputs = [ctx.outputs.log, ctx.outputs.stdout, ctx.outputs.stderr, ctx.outputs.exit] - - ctx.actions.run_shell( - command = """ - if ! "{generate_out_file}" "{stdout}" "{stderr}" "{exit}" "{test}" > "{log}"; then - cat {log} - exit 1 - fi - """.format( - generate_out_file = ctx.executable._generate_out_file.path, - log = ctx.outputs.log.path, - stdout = ctx.outputs.stdout.path, - stderr = ctx.outputs.stderr.path, - exit = ctx.outputs.exit.path, - test = ctx.file.test.path, - ), - tools = ctx.files._generate_out_file, - inputs = ctx.files.srcs, - outputs = outputs, - progress_message = "Ruby {}".format(ctx.attr.prefix), - ) - - runfiles = ctx.runfiles(ctx.files._generate_out_file + ctx.files.srcs) - - return [ - DefaultInfo(files = depset(outputs), runfiles = runfiles), - RubyOracle( - log = ctx.outputs.log, - stdout = ctx.outputs.stdout, - stderr = ctx.outputs.stderr, - exit = ctx.outputs.exit, - ), - ] - -build_ruby_oracle = rule( - implementation = _build_ruby_oracle_impl, - attrs = { - "prefix": attr.string( - mandatory = True, - ), - "test": attr.label( - allow_single_file = True, - ), - "srcs": attr.label( - allow_files = True, - ), - "log": attr.output( - mandatory = True, - ), - "stdout": attr.output( - mandatory = True, - ), - "stderr": attr.output( - mandatory = True, - ), - "exit": attr.output( - mandatory = True, - ), - "_generate_out_file": attr.label( - cfg = "target", - default = "//test:generate_out_file", - executable = True, - ), - }, - provides = [DefaultInfo, RubyOracle], -) - -SorbetOutput = provider( - fields = { - "build": "The output directory for test artifacts", - "log": "The build log", - "stdout": "The stdout of the compiler", - "exit": "The exit code of the compiler", - }, -) - -def _build_extension_impl(ctx): - sources = [file.path for file in ctx.files.srcs] - - outputs = [ctx.outputs.out, ctx.outputs.log, ctx.outputs.stdout, ctx.outputs.exit] - - ctx.actions.run_shell( - command = """ - if ! "{build_extension}" "{output}" "{stdout}" "{exit}" {inputs} 2>&1 > "{log}" 2>&1 ; then - cat "{log}" - exit 1 - fi - """.format( - build_extension = ctx.executable._build_extension.path, - output = ctx.outputs.out.path, - inputs = " ".join([file.path for file in ctx.files.srcs]), - log = ctx.outputs.log.path, - stdout = ctx.outputs.stdout.path, - exit = ctx.outputs.exit.path, - ), - tools = ctx.files._build_extension, - inputs = ctx.files.srcs, - outputs = outputs, - progress_message = "Sorbet {}".format(ctx.attr.prefix), - ) - - runfiles = ctx.runfiles(ctx.files._build_extension + ctx.files.srcs) - - return [ - DefaultInfo(files = depset(outputs), runfiles = runfiles), - SorbetOutput(build = ctx.outputs.out, log = ctx.outputs.log, stdout = ctx.outputs.stdout, exit = ctx.outputs.exit), - ] - -build_extension = rule( - implementation = _build_extension_impl, - attrs = { - "prefix": attr.string( - mandatory = True, - ), - "srcs": attr.label( - ), - "out": attr.output( - mandatory = True, - ), - "log": attr.output( - mandatory = True, - ), - "stdout": attr.output( - mandatory = True, - ), - "exit": attr.output( - mandatory = True, - ), - "_build_extension": attr.label( - cfg = "target", - default = "//test:build_extension", - executable = True, - ), - }, - provides = [DefaultInfo, SorbetOutput], -) - -def _validate_exp_test_impl(ctx): - output = ctx.attr.extension[SorbetOutput] - - validate_exp = ctx.attr._validate_exp[DefaultInfo] - - runfiles = ctx.runfiles( - ctx.files.srcs + ctx.files.exp_files + [output.stdout, output.build, output.log], - ) - - runfiles = runfiles.merge(ctx.attr._validate_exp[DefaultInfo].default_runfiles) - runfiles = runfiles.merge(ctx.attr._llvm_diff[DefaultInfo].default_runfiles) - - ctx.actions.write( - ctx.outputs.executable, - content = """ - cat "{build_log}" - - cat "{sorbet_log}" - - {validate_exp} --build_dir="{build_dir}" --expected_failure="{expected_failure}" {sources} - """.format( - build_log = output.log.short_path, - sorbet_log = output.stdout.short_path, - validate_exp = ctx.executable._validate_exp.short_path, - build_dir = output.build.short_path, - sources = " ".join([file.short_path for file in ctx.files.srcs]), - expected_failure = ctx.attr.expected_failure, - ), - is_executable = True, - ) - - return [DefaultInfo(runfiles = runfiles)] - -validate_exp_test = rule( - test = True, - implementation = _validate_exp_test_impl, - attrs = { - "srcs": attr.label( - mandatory = True, - ), - "exp_files": attr.label( - mandatory = True, - ), - "expected_failure": attr.bool( - default = False, - ), - "extension": attr.label( - mandatory = True, - providers = [SorbetOutput], - ), - "_validate_exp": attr.label( - cfg = "host", - default = "//test:validate_exp", - executable = True, - ), - "_llvm_diff": attr.label( - default = "//test:llvm-diff", - ), - }, -) - -def _filecheck_test_impl(ctx): - output = ctx.attr.extension[SorbetOutput] - - filecheck = ctx.attr._filecheck[DefaultInfo] - - runfiles = ctx.runfiles( - ctx.files.srcs + [output.stdout, output.build, output.log], - ) - - runfiles = runfiles.merge(ctx.attr._filecheck[DefaultInfo].default_runfiles) - - ctx.actions.write( - ctx.outputs.executable, - content = """ - cat "{build_log}" - - cat "{sorbet_log}" - - {filecheck} --build_dir="{build_dir}" {sources} - """.format( - build_log = output.log.short_path, - sorbet_log = output.stdout.short_path, - filecheck = ctx.executable._filecheck.short_path, - build_dir = output.build.short_path, - sources = " ".join([file.short_path for file in ctx.files.srcs]), - ), - is_executable = True, - ) - - return [DefaultInfo(runfiles = runfiles)] - -filecheck_test = rule( - test = True, - implementation = _filecheck_test_impl, - attrs = { - "srcs": attr.label( - mandatory = True, - ), - "extension": attr.label( - mandatory = True, - providers = [SorbetOutput], - ), - "_filecheck": attr.label( - cfg = "host", - default = "//test:filecheck", - executable = True, - ), - }, -) - -def _validate_sorbet_output_test_impl(ctx): - sources = [file.short_path for file in ctx.files.srcs] - - build = ctx.attr.build[SorbetOutput] - oracle = ctx.attr.oracle[RubyOracle] - - ctx.actions.write( - ctx.outputs.executable, - content = """ - cat "{build_log}" - - cat "{sorbet_log}" - - cat "{oracle_log}" - - {test_corpus_runner} \\ - --expected_output="{expected_outfile}" \\ - --expected_err="{expected_errfile}" \\ - --expected_exit_code="{expected_exitfile}" \\ - --build_dir="{build_dir}" \\ - --ruby="{ruby}" \\ - --expect_fail="{expect_fail}" \\ - --sorbet_exit="{sorbet_exitfile}" \\ - --sorbet_out="{sorbet_out}" \\ - {sources} 2>&1 - """.format( - build_log = build.log.short_path, - sorbet_log = build.stdout.short_path, - oracle_log = oracle.log.short_path, - test_corpus_runner = ctx.executable._test_corpus_runner.short_path, - expected_outfile = oracle.stdout.short_path, - expected_errfile = oracle.stderr.short_path, - expected_exitfile = oracle.exit.short_path, - build_dir = build.build.short_path, - ruby = ctx.executable._ruby.short_path, - expect_fail = ctx.attr.expected_failure, - sorbet_exitfile = build.exit.short_path, - sorbet_out = build.stdout.short_path, - sources = " ".join(sources), - ), - is_executable = True, - ) - - runfiles = ctx.runfiles(files = [ctx.file._patch_require] + ctx.files.build + ctx.files.oracle + ctx.files._sorbet_runtime + - ctx.files.srcs) - runfiles = runfiles.merge(ctx.attr._test_corpus_runner[DefaultInfo].default_runfiles) - runfiles = runfiles.merge(ctx.attr._ruby[DefaultInfo].default_runfiles) - - return [DefaultInfo(runfiles = runfiles)] - -validate_sorbet_output_test = rule( - test = True, - implementation = _validate_sorbet_output_test_impl, - attrs = { - "srcs": attr.label( - ), - "build": attr.label( - providers = [SorbetOutput], - ), - "oracle": attr.label( - providers = [RubyOracle], - ), - "expected_failure": attr.bool( - default = False, - ), - "_test_corpus_runner": attr.label( - cfg = "target", - default = "//test:test_corpus_runner", - executable = True, - ), - "_ruby": attr.label( - cfg = "target", - default = "@sorbet_ruby_2_7_for_compiler//:ruby", - executable = True, - ), - "_sorbet_runtime": attr.label( - default = "//gems/sorbet-runtime", - ), - "_patch_require": attr.label( - default = ":patch_require.rb", - allow_single_file = True, - ), - }, - provides = [DefaultInfo], -) diff --git a/test/diff-diff.rb b/test/diff-diff.rb deleted file mode 100755 index 47767d2ee3..0000000000 --- a/test/diff-diff.rb +++ /dev/null @@ -1,117 +0,0 @@ -#!/usr/bin/env ruby - -# This script post-processes the output of llvm-diff to work around the bug -# identified in https://bugs.llvm.org/show_bug.cgi?id=48137. This bug presents -# itself as struct types that are defined in both files getting a unique suffix -# added in the diff, indicating incorrectly that two lines differ. As an -# example, you can run the following command (on osx): -# -# > /usr/local/opt/llvm@12/bin/llvm-diff \ -# > test/testdata/compiler/hello.llo.exp \ -# > test/testdata/compiler/hello.llo.exp -# -# This will indicate that there are multiple differences despite both arguments -# being the exact same file. For example, the diff reported below differs only -# in the use of `%struct.FunctionInlineCache`, where the right entry has a -# renamed version of the same struct present. -# -# > %send.i = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache.69* noundef @ic_puts, i64 0) #6, !dbg !4 -# < %send.i = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* noundef @ic_puts, i64 0) #6, !dbg !4 - -module DiffDiff - - def self.main(io) - real_failure, lines = sanitize(io) - if real_failure - puts lines - exit(1) - end - end - - LINE_ERROR = %r{llvm-diff: [^ ]+:\d+:\d+: error: } - - # Determine if this is a real failure, or differences introduced as a result - # of the `llvm-diff` bug https://bugs.llvm.org/show_bug.cgi?id=48137 - def self.sanitize(io) - real_failure = false - - right = [] - left = [] - in_diff = false - - lines = [] - - io.each do |line| - stripped = line.strip - - if stripped.start_with? '>' - right << line - in_diff = true - elsif stripped.start_with? '<' - left << line - in_diff = true - else - if in_diff - real_diff = sanitize_lines(lines, right, left) - real_failure ||= real_diff - - left = [] - right = [] - in_diff = false - end - - # encountering a function that only exists in one file or the other will - # cause `llvm-diff` to return a non-zero exit code - real_failure ||= line.include? 'exists only in' - - # encountering an error when parsing either file - real_failure ||= line.match? LINE_ERROR - - lines << line - end - end - - # if the llvm-diff output ends with a diff, the right and left arrays will - # need to be processed explicitly - if in_diff - real_diff = sanitize_lines(lines, right, left) - real_failure ||= real_diff - end - - [ real_failure, lines ] - end - - # For all of the lines in the left and right sets, strip out suffixes that are - # added by the bug in `llvm-diff`, and compare them for equality. If they are - # equal after that sanitizing pass, indicate that this diff block did not - # contribute to the overall diff failure and concatenate the sanitized lines - # to the output - def self.sanitize_lines(out, right, left) - sanitized_left = left.map do |line| - sanitize_line(line.sub('<', ' ')) - end - - sanitized_right = right.map do |line| - sanitize_line(line.sub('>', ' ')) - end - - if sanitized_right == sanitized_left - out.concat(sanitized_right) - false - else - out.concat(right, left) - true - end - end - - LOCAL_PATTERN = %r{(%struct\.[^\s]+)\.\d+} - - def self.sanitize_line(line) - line.gsub(LOCAL_PATTERN, '\1') - end - -end - -if __FILE__ == $0 - DiffDiff.main($stdin) -end diff --git a/test/fuzz/BUILD b/test/fuzz/BUILD index c8c7835633..b434c284a3 100644 --- a/test/fuzz/BUILD +++ b/test/fuzz/BUILD @@ -53,15 +53,17 @@ cc_test( ], ) -load("@com_google_protobuf//:protobuf.bzl", "cc_proto_library") +load("@com_google_protobuf//bazel:cc_proto_library.bzl", "cc_proto_library") +load("@com_google_protobuf//bazel:proto_library.bzl", "proto_library") + +proto_library( + name = "proto_proto", + srcs = glob(["**/*.proto"]), +) cc_proto_library( name = "proto", - srcs = glob(["**/*.proto"]), - linkstatic = select({ - "//tools/config:linkshared": 0, - "//conditions:default": 1, - }), tags = ["manual"], visibility = ["//tools:__pkg__"], + deps = [":proto_proto"], ) diff --git a/test/fuzz/fuzz_dash_e.cc b/test/fuzz/fuzz_dash_e.cc index 28a07dd7a9..d55a999c6c 100644 --- a/test/fuzz/fuzz_dash_e.cc +++ b/test/fuzz/fuzz_dash_e.cc @@ -69,9 +69,10 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { file.data(*gs).strictLevel = core::StrictLevel::True; } - indexed = realmain::pipeline::index(*gs, inputFiles, *opts, *workers, kvstore); + indexed = realmain::pipeline::index(*gs, absl::Span(inputFiles), *opts, *workers, kvstore); + // We don't run this fuzzer with any packager options, so we can skip pipeline::package() auto foundHashes = nullptr; - indexed = move(realmain::pipeline::resolve(gs, move(indexed), *opts, *workers, foundHashes).result()); + indexed = move(realmain::pipeline::nameAndResolve(gs, move(indexed), *opts, *workers, foundHashes).result()); realmain::pipeline::typecheck(*gs, move(indexed), *opts, *workers); return 0; } diff --git a/test/fuzz/fuzz_doc_symbols.cc b/test/fuzz/fuzz_doc_symbols.cc index 8ec36c2a99..d926cc8ff5 100644 --- a/test/fuzz/fuzz_doc_symbols.cc +++ b/test/fuzz/fuzz_doc_symbols.cc @@ -21,7 +21,6 @@ std::shared_ptr mkOpts(std::string_view cont auto opts = std::make_shared(); opts->fs = std::make_shared(rootPath); opts->fs->writeFile(filePath, contents); - opts->lspDocumentSymbolEnabled = true; opts->rawInputDirNames.emplace_back(rootPath); opts->inputFileNames.emplace_back(filePath); return opts; diff --git a/test/generate_out_file.sh b/test/generate_out_file.sh deleted file mode 100755 index b051235392..0000000000 --- a/test/generate_out_file.sh +++ /dev/null @@ -1,91 +0,0 @@ -#!/bin/bash -set -euo pipefail - -# --- begin runfiles.bash initialization --- {{{ -# Copy-pasted from Bazel's Bash runfiles library https://github.com/bazelbuild/bazel/blob/defd737761be2b154908646121de47c30434ed51/tools/bash/runfiles/runfiles.bash -if [[ ! -d "${RUNFILES_DIR:-/dev/null}" && ! -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then - if [[ -f "$0.runfiles_manifest" ]]; then - export RUNFILES_MANIFEST_FILE="$0.runfiles_manifest" - elif [[ -f "$0.runfiles/MANIFEST" ]]; then - export RUNFILES_MANIFEST_FILE="$0.runfiles/MANIFEST" - elif [[ -f "$0.runfiles/bazel_tools/tools/bash/runfiles/runfiles.bash" ]]; then - export RUNFILES_DIR="$0.runfiles" - fi -fi -if [[ -f "${RUNFILES_DIR:-/dev/null}/bazel_tools/tools/bash/runfiles/runfiles.bash" ]]; then - # shellcheck disable=SC1091 - source "${RUNFILES_DIR}/bazel_tools/tools/bash/runfiles/runfiles.bash" -elif [[ -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then - # shellcheck disable=SC1090 - source "$(grep -m1 "^bazel_tools/tools/bash/runfiles/runfiles.bash " \ - "$RUNFILES_MANIFEST_FILE" | cut -d ' ' -f 2-)" -else - echo >&2 "ERROR: cannot find @bazel_tools//tools/bash/runfiles:runfiles.bash" - exit 1 -fi -# --- end runfiles.bash initialization --- }}} - -# Find logging with rlocation, as this script is run from a genrule -# shellcheck source=SCRIPTDIR/logging.sh -source "$(rlocation com_stripe_ruby_typer/test/logging.sh)" - -# Argument Parsing ############################################################# - -rbout=${1} -rberr=${2} -rbexit=${3} -rb=${4} - -# Environment Setup ############################################################ - -# NOTE: using a temp file here, as that will cause ruby to not print the name of -# the main file in a stack trace. -rbrunfile=$(mktemp) - -# Find ruby -ruby="$(rlocation sorbet_ruby_2_7_for_compiler/ruby)" - -# These paths are absolute when running standalone, but relative when running -# in the sandbox. To work around this, we pull out these dirs and use -I below -# to modify the include path (which can take absolute or relative paths). -sorbet_runtime_include=$(dirname "$(rlocation com_stripe_ruby_typer/gems/sorbet-runtime/lib/sorbet-runtime.rb)") -patch_require_include=$(dirname "$(rlocation com_stripe_ruby_typer/test/patch_require.rb)") - -# Main ######################################################################### - -info "--- Build Config ---" -info "* Test: ${rb}" -info "* Rbout: ${rbout}" -info "* Rberr: ${rberr}" -info "* Rbexit: ${rbexit}" -info "* Runner: ${rbrunfile}" - -info "--- Debugging ---" -info " test/run_ruby.sh -d ${rb}" - -info "--- Building output ---" -echo "require './$rb';" > "$rbrunfile" - -# Use a temp directory for LLVMIR so we don't accidentally pick up changes from -# the environment -llvmir=$(mktemp -d) -cleanup() { - rm -r "$llvmir" - rm "$rbrunfile" -} -trap cleanup EXIT - -set +e -# NOTE: we run with patch_require incluced so that the stack trace looks similar -# to what we'll see in the compiled version -llvmir="$llvmir" "$ruby" \ - --disable=gems \ - --disable=did_you_mean \ - -r "rubygems" \ - -I "$sorbet_runtime_include" -rsorbet-runtime.rb \ - -I "$patch_require_include" -rpatch_require.rb \ - "$rbrunfile" > "$rbout" 2> "$rberr" -echo "$?" > "$rbexit" -set -e - -success "* Captured output for ${rb}" diff --git a/test/helpers/expectations.cc b/test/helpers/expectations.cc index 008270fb31..f668526ebc 100644 --- a/test/helpers/expectations.cc +++ b/test/helpers/expectations.cc @@ -89,15 +89,15 @@ bool addToExpectations(Expectations &exp, string_view filePath, bool isDirectory string source_file_path = absl::StrCat(exp.folder, filePath.substr(0, kind_start)); exp.expectations[kind][source_file_path] = filePath; return true; - } else if (absl::EndsWith(filePath, ".rbupdate")) { - int suffixLength = strlen(".rbupdate"); - - auto pos = filePath.rfind('.', filePath.length() - suffixLength - 1); + } else if (absl::EndsWith(filePath, ".rbupdate") || absl::EndsWith(filePath, ".rbiupdate")) { + auto suffixStart = filePath.rfind('.'); + auto pos = filePath.rfind('.', suffixStart - 1); if (pos != string::npos) { - int version = stoi(string(filePath.substr(pos + 1, filePath.length() - suffixLength))); + int version = stoi(string(filePath.substr(pos + 1, suffixStart))); auto &updates = exp.sourceLSPFileUpdates[version]; - updates.emplace_back(absl::StrCat(filePath.substr(0, pos), ".rb"), filePath); + auto suffix = absl::EndsWith(filePath, ".rbupdate") ? ".rb" : ".rbi"; + updates.emplace_back(absl::StrCat(filePath.substr(0, pos), suffix), filePath); } else { cout << "Ignoring " << filePath << ": No version number provided (expected .[number].rbupdate).\n"; } @@ -109,8 +109,8 @@ bool addToExpectations(Expectations &exp, string_view filePath, bool isDirectory vector listTrimmedTestFilesInDir(string_view dir, bool recursive) { unique_ptr workerPool = WorkerPool::create(0, *spdlog::default_logger()); - vector names = - sorbet::FileOps::listFilesInDir(dir, {".rb", ".rbi", ".rbupdate", ".exp"}, *workerPool, recursive, {}, {}); + vector names = sorbet::FileOps::listFilesInDir(dir, {".rb", ".rbi", ".rbupdate", ".rbiupdate", ".exp"}, + *workerPool, recursive, {}, {}); const int prefixLen = dir.length() + 1; // Trim off the input directory from the name. transform(names.begin(), names.end(), names.begin(), diff --git a/test/helpers/lsp.cc b/test/helpers/lsp.cc index d51b191bdb..21fc3fb404 100644 --- a/test/helpers/lsp.cc +++ b/test/helpers/lsp.cc @@ -480,6 +480,11 @@ unique_ptr makeClose(string_view uri) { return make_unique(move(didCloseNotif)); } +std::unique_ptr makeConfigurationChange(std::unique_ptr params) { + auto changeConfigNotification = + make_unique("2.0", LSPMethod::WorkspaceDidChangeConfiguration, move(params)); + return make_unique(move(changeConfigNotification)); +} vector> getLSPResponsesFor(LSPWrapper &wrapper, vector> messages) { if (auto stWrapper = dynamic_cast(&wrapper)) { return stWrapper->getLSPResponsesFor(move(messages)); diff --git a/test/helpers/lsp.h b/test/helpers/lsp.h index 537d341a54..2da67ab02c 100644 --- a/test/helpers/lsp.h +++ b/test/helpers/lsp.h @@ -44,6 +44,9 @@ std::unique_ptr makeChange(std::string_view uri, std::string_view co /** Create an LSPMessage containing a textDocument/didClose notification. */ std::unique_ptr makeClose(std::string_view uri); +/** Create an LSPMessage containing a workspace/didChangeConfiguration notification */ +std::unique_ptr makeConfigurationChange(std::unique_ptr params); + /** Checks that we are properly advertising Sorbet LSP's capabilities to clients. */ void checkServerCapabilities(const ServerCapabilities &capabilities); diff --git a/test/helpers/position_assertions.cc b/test/helpers/position_assertions.cc index cd59a41ac6..f1ad5ed0c8 100644 --- a/test/helpers/position_assertions.cc +++ b/test/helpers/position_assertions.cc @@ -13,6 +13,7 @@ #include "test/helpers/position_assertions.h" #include #include +#include using namespace std; @@ -231,7 +232,7 @@ bool checkAllInner(const sorbet::UnorderedMap> parseAssertionsForFile(const shared_ptr> parseAssertionsForFile(const shared_ptr 1) { + // Position assertion with no selection + ADD_FAIL_CHECK_AT(filename.c_str(), lineNum + 1, + fmt::format("Invalid assertion comment found on line {}:\n{}\nAssertions with | " + "should only point to 1 character", + lineNum, line)); + } } if (numCarets == 0 && lineHasCode) { @@ -602,7 +615,7 @@ vector> parseAssertionsForFile(const shared_ptr range; if (numCarets > 0) { int caretBeginPos = textBeforeComment.size() + matches[1].str().size(); - int caretEndPos = caretBeginPos + numCarets; + int caretEndPos = caretBeginPos + (zeroLenSelection ? 0 : numCarets); range = RangeAssertion::makeRange(lastSourceLineNum, caretBeginPos, caretEndPos); } else if (assertionContents == "unexpected token tNL") { range = RangeAssertion::makeRange(lineNum); @@ -887,7 +900,13 @@ void UsageAssertion::check(const UnorderedMap> &s return; } - assertLocationsMatch(config, sourceFileContents, symbol, allLocs, line, character, locSourceLine, locFilename, + vector> newLocs; + for (const auto &assertion : allLocs) { + if (dynamic_pointer_cast(assertion) == nullptr) { + newLocs.emplace_back(assertion); + } + } + assertLocationsMatch(config, sourceFileContents, symbol, newLocs, line, character, locSourceLine, locFilename, locations, "reference"); } @@ -944,7 +963,13 @@ void UsageAssertion::checkHighlights(const UnorderedMap> newLocs; + for (const auto &assertion : allLocs) { + if (dynamic_pointer_cast(assertion) == nullptr) { + newLocs.emplace_back(assertion); + } + } + assertLocationsMatch(config, sourceFileContents, symbol, newLocs, line, character, locSourceLine, locFilename, locations, "highlight"); } @@ -999,6 +1024,25 @@ string UsageAssertion::toString() const { return fmt::format("usage: {}", symbol); } +GoToDefSpecialAssertion::GoToDefSpecialAssertion(string_view filename, unique_ptr &range, int assertionLine, + string_view symbol, vector versions) + : UsageAssertion(filename, range, assertionLine, symbol, versions) {} + +shared_ptr GoToDefSpecialAssertion::make(string_view filename, unique_ptr &range, + int assertionLine, string_view assertionContents, + string_view assertionType) { + auto [symbol, versions, option] = getSymbolVersionAndOption(assertionContents); + if (!option.empty()) { + ADD_FAIL_CHECK_AT(string(filename).c_str(), assertionLine + 1, + fmt::format("Unexpected import assertion option: `{}`", option)); + } + return make_shared(filename, range, assertionLine, symbol, versions); +} + +string GoToDefSpecialAssertion::toString() const { + return fmt::format("go-to-def-special: {}", symbol); +} + TypeDefAssertion::TypeDefAssertion(string_view filename, unique_ptr &range, int assertionLine, string_view symbol) : RangeAssertion(filename, range, assertionLine), symbol(symbol) {} @@ -1450,7 +1494,7 @@ shared_ptr ApplyCompletionAssertion::make(string_view ADD_FAIL_CHECK_AT( string(filename).c_str(), assertionLine + 1, - fmt::format("Improperly formatted apply-completion assertion. Expected '[] '. Found '{}'", + fmt::format("Improperly formatted apply-completion assertion. Expected '[] item: '. Found '{}'", assertionContents)); return nullptr; @@ -1717,8 +1761,8 @@ ApplyCodeActionAssertion::ApplyCodeActionAssertion(string_view filename, unique_ string ApplyCodeActionAssertion::toString() const { return fmt::format("apply-code-action: [{}] {}", version, title); } -optional> ApplyCodeActionAssertion::expectedFile() { - auto expectedUpdatedFilePath = updatedFilePath(this->filename, this->version); +optional> ApplyCodeActionAssertion::expectedFile(string filename, string version) { + auto expectedUpdatedFilePath = updatedFilePath(filename, version); string expectedEditedFileContents; try { expectedEditedFileContents = FileOps::read(expectedUpdatedFilePath); @@ -1771,11 +1815,6 @@ getFileByUri(const LSPConfiguration &config, void ApplyCodeActionAssertion::check(const UnorderedMap> &sourceFileContents, LSPWrapper &wrapper, const CodeAction &codeAction) { - auto maybeFile = expectedFile(); - if (!maybeFile.has_value()) { - return; - } - auto [expectedUpdatedFilePath, expectedEditedFileContents] = maybeFile.value(); const auto &config = wrapper.config(); for (auto &c : *codeAction.edit.value()->documentChanges) { auto file = getFileByUri(config, sourceFileContents, c->textDocument->uri); @@ -1783,6 +1822,12 @@ void ApplyCodeActionAssertion::check(const UnorderedMapsource()); c = sortEdits(move(c)); + auto maybeFile = expectedFile(uriToFilePath(config, c->textDocument->uri), this->version); + if (!maybeFile.has_value()) { + return; + } + + auto [expectedUpdatedFilePath, expectedEditedFileContents] = maybeFile.value(); for (auto &e : c->edits) { auto reindent = false; actualEditedFileContents = applyEdit(actualEditedFileContents, *file, *e->range, e->newText, reindent); @@ -1796,12 +1841,11 @@ void ApplyCodeActionAssertion::checkAll( const CodeAction &codeAction) { const auto &config = wrapper.config(); UnorderedMap accumulatedOriginalEditedContents{}; + + // actualEditedFileContents -> (expectedUpdatedFilePath, expectedEditedFileContents) + // Maps original file contents to a edited filename and contents + UnorderedMap> fileToUpdatedFile; string actualEditedFileContents; - auto maybeFile = expectedFile(); - if (!maybeFile.has_value()) { - return; - } - auto [expectedUpdatedFilePath, expectedEditedFileContents] = maybeFile.value(); for (auto &c : *codeAction.edit.value()->documentChanges) { auto file = getFileByUri(config, sourceFileContents, c->textDocument->uri); @@ -1809,6 +1853,12 @@ void ApplyCodeActionAssertion::checkAll( actualEditedFileContents = string(file->source()); for (auto &e : c->edits) { + auto maybeFile = expectedFile(uriToFilePath(config, c->textDocument->uri), this->version); + if (!maybeFile.has_value()) { + continue; + } + fileToUpdatedFile.insert_or_assign(actualEditedFileContents, maybeFile.value()); + string oldSource = accumulatedOriginalEditedContents.contains(actualEditedFileContents) ? accumulatedOriginalEditedContents[actualEditedFileContents] : actualEditedFileContents; @@ -1820,6 +1870,7 @@ void ApplyCodeActionAssertion::checkAll( } for (auto pair : accumulatedOriginalEditedContents) { + auto [expectedUpdatedFilePath, expectedEditedFileContents] = fileToUpdatedFile[pair.first]; assertResults(expectedUpdatedFilePath, expectedEditedFileContents, pair.second); } } diff --git a/test/helpers/position_assertions.h b/test/helpers/position_assertions.h index 07043316b3..e35d597a44 100644 --- a/test/helpers/position_assertions.h +++ b/test/helpers/position_assertions.h @@ -192,6 +192,29 @@ class ImportUsageAssertion final : public UsageAssertion { std::string_view symbol, std::vector versions); }; +// This is special, because most of the time "textDocument/definition" is tested with `def` and +// `usage` assertions. For most symbols, "Go to Definition" on a location returned by "Find All +// References" should jump back to the definition. +// +// But there are some "special" cases where that's not the case: where you can jump to def from one +// place to get to the definition, but you can never get there via find all references. +// +// Unless you know you're building such a special case, you probably want to use a normal `usage` +// assertion. +// +// # ^^^ go-to-def-special: symbol +class GoToDefSpecialAssertion final : public UsageAssertion { +public: + static std::shared_ptr make(std::string_view filename, std::unique_ptr &range, + int assertionLine, std::string_view assertionContents, + std::string_view assertionType); + + GoToDefSpecialAssertion(std::string_view filename, std::unique_ptr &range, int assertionLine, + std::string_view symbol, std::vector versions); + + std::string toString() const override; +}; + // # some-property: foo class StringPropertyAssertion final : public RangeAssertion { public: @@ -402,7 +425,7 @@ class ApplyCodeActionAssertion final : public RangeAssertion { std::string toString() const override; private: - std::optional> expectedFile(); + std::optional> expectedFile(std::string filename, std::string version); void assertResults(std::string expectedPath, std::string expectedContents, std::string actualContents); std::unique_ptr sortEdits(std::unique_ptr changes); }; diff --git a/test/hermetic_tar.sh b/test/hermetic_tar.sh deleted file mode 100644 index 29f3798e04..0000000000 --- a/test/hermetic_tar.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env bash - -# If we have gtar installed (darwin), use that instead -if hash gtar 2>/dev/null; then - alias tar=gtar -fi - -hermetic_tar() { - SRC="$1" - OUT="$2" - # Check if our tar supports --sort (we assume --sort support implies --mtime support) - if [[ $(tar --sort 2>&1) =~ requires\ an\ argument ]]; then - tar --sort=name --owner=0 --group=0 --numeric-owner --mtime='UTC 1970-01-01 00:00' -h -C "$SRC" -rf "$OUT" . - elif [[ $(tar --mtime 2>&1) =~ requires\ an\ argument ]]; then - (cd "$SRC" && find . -print0) | \ - LC_ALL=C sort -z | \ - tar -h -C "$SRC" --no-recursion --null -T - --owner=0 --group=0 --numeric-owner --mtime='UTC 1970-01-01 00:00' -rf "$OUT" . - else - # Oh well, no hermetic tar for you - tar -h -C "$SRC" -rf "$OUT" . - fi -} diff --git a/test/lsp/ProtocolTest.cc b/test/lsp/ProtocolTest.cc index 8b32c71657..ddd8977fed 100644 --- a/test/lsp/ProtocolTest.cc +++ b/test/lsp/ProtocolTest.cc @@ -241,7 +241,12 @@ vector> ProtocolTest::getDefinitions(std::string_view uri, return {}; } -void ProtocolTest::assertDiagnostics(vector> messages, vector expected) { +namespace { + +template +void assertDiagnostics(vector> messages, vector expected, + const UnorderedMap> &sourceFileContents, + map>> &diagnostics) { for (auto &msg : messages) { // Ignore typecheck run and sorbet/fence messages. They do not impact semantics. if (!isTypecheckRun(*msg) && !isSorbetFence(*msg)) { @@ -250,14 +255,25 @@ void ProtocolTest::assertDiagnostics(vector> messages, ve } // Convert ExpectedDiagnostic into ErrorAssertion objects. - vector> errorAssertions; + vector> errorAssertions; for (auto e : expected) { auto range = RangeAssertion::makeRange(e.line); - errorAssertions.push_back(ErrorAssertion::make(e.path, range, e.line, e.message, "error")); + errorAssertions.push_back(T::make(e.path, range, e.line, e.message, "error")); } // Use same logic as main test runner. - ErrorAssertion::checkAll(sourceFileContents, errorAssertions, diagnostics); + T::checkAll(sourceFileContents, errorAssertions, diagnostics); +} +} // namespace + +void ProtocolTest::assertErrorDiagnostics(vector> messages, + vector expected) { + assertDiagnostics(std::move(messages), expected, sourceFileContents, diagnostics); +} + +void ProtocolTest::assertUntypedDiagnostics(vector> messages, + vector expected) { + assertDiagnostics(std::move(messages), expected, sourceFileContents, diagnostics); } const CounterStateDatabase ProtocolTest::getCounters() { diff --git a/test/lsp/ProtocolTest.h b/test/lsp/ProtocolTest.h index d21c78d22f..0d0ef82437 100644 --- a/test/lsp/ProtocolTest.h +++ b/test/lsp/ProtocolTest.h @@ -95,7 +95,11 @@ class ProtocolTest { std::unique_ptr readAsync(); - void assertDiagnostics(std::vector> messages, std::vector expected); + void assertErrorDiagnostics(std::vector> messages, + std::vector expected); + + void assertUntypedDiagnostics(std::vector> messages, + std::vector expected); std::string readFile(std::string_view uri); diff --git a/test/lsp/cache_protocol_test_corpus.cc b/test/lsp/cache_protocol_test_corpus.cc index b23bc2e3ed..1e1c372044 100644 --- a/test/lsp/cache_protocol_test_corpus.cc +++ b/test/lsp/cache_protocol_test_corpus.cc @@ -65,12 +65,12 @@ TEST_CASE_FIXTURE(CacheProtocolTest, "LSPUsesCache") { writeFilesToFS({{relativeFilepath, fileContents}}); lspWrapper->opts->inputFileNames.push_back(filePath); - assertDiagnostics( + assertErrorDiagnostics( initializeLSP(), {{relativeFilepath, 4, "Expected `Integer` but found `String(\"hello\")` for method result type"}}); // Update the file on disk to a different version. This change should not be synced to disk. - assertDiagnostics(send(*openFile(relativeFilepath, updatedFileContents)), {}); + assertErrorDiagnostics(send(*openFile(relativeFilepath, updatedFileContents)), {}); } // LSP should have written cache to disk with file hashes from initialization. @@ -112,7 +112,7 @@ TEST_CASE_FIXTURE(CacheProtocolTest, "LSPUsesCache") { resetState(); lspWrapper->opts->inputFileNames.push_back(filePath); writeFilesToFS({{relativeFilepath, fileContents}}); - assertDiagnostics( + assertErrorDiagnostics( initializeLSP(), {{relativeFilepath, 4, "Expected `Integer` but found `String(\"hello\")` for method result type"}}); @@ -126,7 +126,7 @@ TEST_CASE_FIXTURE(CacheProtocolTest, "LSPUsesCache") { resetState(); lspWrapper->opts->inputFileNames.push_back(filePath); writeFilesToFS({{relativeFilepath, updatedFileContents}}); - assertDiagnostics(initializeLSP(), {}); + assertErrorDiagnostics(initializeLSP(), {}); auto counters = getCounters(); CHECK_EQ(counters.getCounter("types.input.files.kvstore.miss"), 1); @@ -172,7 +172,7 @@ TEST_CASE_FIXTURE(CacheProtocolTest, "LSPDoesNotUseCacheIfModified") { writeFilesToFS({{relativeFilepath, fileContents}}); lspWrapper->opts->inputFileNames.push_back(filePath); - assertDiagnostics( + assertErrorDiagnostics( initializeLSP(), {{relativeFilepath, 4, "Expected `Integer` but found `String(\"hello\")` for method result type"}}); } @@ -222,7 +222,7 @@ TEST_CASE_FIXTURE(CacheProtocolTest, "LSPDoesNotUseCacheIfModified") { writeFilesToFS({{relativeFilepath, updatedFileContents}}); lspWrapper->opts->inputFileNames.push_back(filePath); - assertDiagnostics(initializeLSP(), {}); + assertErrorDiagnostics(initializeLSP(), {}); // File was updated, so no cache hits. auto counters = getCounters(); @@ -250,7 +250,7 @@ TEST_CASE_FIXTURE(CacheProtocolTest, "LSPDoesNotUseCacheIfModified") { lspWrapper->opts->inputFileNames.push_back(filePath); writeFilesToFS({{relativeFilepath, fileContents}}); - assertDiagnostics( + assertErrorDiagnostics( initializeLSP(), {{relativeFilepath, 4, "Expected `Integer` but found `String(\"hello\")` for method result type"}}); diff --git a/test/lsp/incremental-lsp-changes/incremental-lsp-changes.rec b/test/lsp/incremental-lsp-changes/incremental-lsp-changes.rec index b5cabe5b16..20d6525ba1 100644 --- a/test/lsp/incremental-lsp-changes/incremental-lsp-changes.rec +++ b/test/lsp/incremental-lsp-changes/incremental-lsp-changes.rec @@ -7,5 +7,5 @@ Read: {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument Read: {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///Users/dmitry/stripe/pay-server/api/lib/compatibility/change/foo.rb","version":3},"contentChanges":[{"range":{"start":{"line":3,"character":12},"end":{"line":3,"character":12}},"rangeLength":0,"text":"+"}]}} Write: {"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":"file:///Users/dmitry/stripe/pay-server/api/lib/compatibility/change/foo.rb","diagnostics":[{"range":{"start":{"line":3,"character":12},"end":{"line":3,"character":13}},"severity":1,"code":7003,"codeDescription":{"href":"https://srb.help/7003"},"message":"Method `+` does not exist on `T.class_of(Foo)`","relatedInformation":[{"location":{"uri":"file:///Users/dmitry/stripe/pay-server/api/lib/compatibility/change/foo.rb","range":{"start":{"line":3,"character":8},"end":{"line":3,"character":11}}},"message":"Got `T.class_of(Foo)` originating from:"}]},{"range":{"start":{"line":3,"character":12},"end":{"line":3,"character":13}},"severity":1,"code":2001,"codeDescription":{"href":"https://srb.help/2001"},"message":"missing arg to \"+\" operator","relatedInformation":[]}]}} Read: {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///Users/dmitry/stripe/pay-server/api/lib/compatibility/change/foo.rb","version":4},"contentChanges":[{"range":{"start":{"line":3,"character":13},"end":{"line":3,"character":13}},"rangeLength":0,"text":"\"\""}]}} -Write: {"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":"file:///Users/dmitry/stripe/pay-server/api/lib/compatibility/change/foo.rb","diagnostics":[{"range":{"start":{"line":3,"character":8},"end":{"line":3,"character":11}},"severity":1,"code":7003,"codeDescription":{"href":"https://srb.help/7003"},"message":"Method `Foo` does not exist on `Foo` (fix available)","relatedInformation":[{"location":{"uri":"file:///Users/dmitry/stripe/pay-server/api/lib/compatibility/change/foo.rb","range":{"start":{"line":3,"character":8},"end":{"line":3,"character":11}}},"message":"\n Ruby uses `.new` to invoke a class's constructor"},{"location":{"uri":"file:///Users/dmitry/stripe/pay-server/api/lib/compatibility/change/foo.rb","range":{"start":{"line":3,"character":11},"end":{"line":3,"character":11}}},"message":"Insert `.new`"}]}]}} +Write: {"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":"file:///Users/dmitry/stripe/pay-server/api/lib/compatibility/change/foo.rb","diagnostics":[{"range":{"start":{"line":3,"character":8},"end":{"line":3,"character":11}},"severity":1,"code":7003,"codeDescription":{"href":"https://srb.help/7003"},"message":"Method `Foo` does not exist on `Foo` (fix available)","relatedInformation":[{"location":{"uri":"file:///Users/dmitry/stripe/pay-server/api/lib/compatibility/change/foo.rb","range":{"start":{"line":3,"character":8},"end":{"line":3,"character":11}}},"message":"Note:"},{"location":{"uri":"file:///Users/dmitry/stripe/pay-server/api/lib/compatibility/change/foo.rb","range":{"start":{"line":3,"character":8},"end":{"line":3,"character":11}}},"message":"Ruby uses `.new` to invoke a class's constructor"}]}]}} Read: {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///Users/dmitry/stripe/pay-server/api/lib/compatibility/change/foo.rb","version":5},"contentChanges":[{"range":{"start":{"line":3,"character":14},"end":{"line":3,"character":14}},"rangeLength":0,"text":"1"}]}} diff --git a/test/lsp/multithreaded_protocol_test_corpus.cc b/test/lsp/multithreaded_protocol_test_corpus.cc index 56fa5e2ed6..fb8796266a 100644 --- a/test/lsp/multithreaded_protocol_test_corpus.cc +++ b/test/lsp/multithreaded_protocol_test_corpus.cc @@ -47,7 +47,7 @@ class MultithreadedProtocolTest : public ProtocolTest { } // namespace TEST_CASE_FIXTURE(MultithreadedProtocolTest, "MultithreadedWrapperWorks") { - assertDiagnostics(initializeLSP(), {}); + assertErrorDiagnostics(initializeLSP(), {}); { auto initCounters = getCounters(); CHECK_EQ(initCounters.getCategoryCounter("lsp.messages.processed", "initialize"), 1); @@ -67,8 +67,8 @@ TEST_CASE_FIXTURE(MultithreadedProtocolTest, "MultithreadedWrapperWorks") { // Pause so that all latency timers for the above operations get reported. this_thread::sleep_for(chrono::milliseconds(2)); - assertDiagnostics(send(LSPMessage(make_unique("2.0", LSPMethod::RESUME, nullopt))), - {{"yolo1.rb", 3, "bear"}}); + assertErrorDiagnostics(send(LSPMessage(make_unique("2.0", LSPMethod::RESUME, nullopt))), + {{"yolo1.rb", 3, "bear"}}); auto counters = getCounters(); CHECK_EQ(counters.getCategoryCounter("lsp.messages.processed", "sorbet.workspaceEdit"), 1); @@ -84,39 +84,39 @@ TEST_CASE_FIXTURE(MultithreadedProtocolTest, "MultithreadedWrapperWorks") { TEST_CASE_FIXTURE(MultithreadedProtocolTest, "CancelsSlowPathWhenNewEditWouldTakeFastPathWithOldEdits") { auto initOptions = make_unique(); initOptions->enableTypecheckInfo = true; - assertDiagnostics( + assertErrorDiagnostics( initializeLSP(true /* supportsMarkdown */, true /* supportsCodeActionResolve */, move(initOptions)), {}); // Create three files. - assertDiagnostics(send(*openFile("foo.rb", "# typed: true\n" - "\n" - "class Foo\n" - "\n" - "end\n")), - {}); - assertDiagnostics(send(*openFile("bar.rb", "# typed: true\n" - "\n" - "class Bar\n" - "extend T::Sig\n" - "\n" - "sig{returns(String)}\n" - "def hello\n" - "\"hi\"\n" - "end\n" - "end\n")), - {}); + assertErrorDiagnostics(send(*openFile("foo.rb", "# typed: true\n" + "\n" + "class Foo\n" + "\n" + "end\n")), + {}); + assertErrorDiagnostics(send(*openFile("bar.rb", "# typed: true\n" + "\n" + "class Bar\n" + "extend T::Sig\n" + "\n" + "sig{returns(String)}\n" + "def hello\n" + "\"hi\"\n" + "end\n" + "end\n")), + {}); // baz calls the method defined in bar - assertDiagnostics(send(*openFile("baz.rb", "# typed: true\n" - "\n" - "class Baz\n" - "extend T::Sig\n" - "\n" - "sig{returns(String)}\n" - "def hello\n" - "Bar.new.hello\n" - "end\n" - "end\n")), - {}); + assertErrorDiagnostics(send(*openFile("baz.rb", "# typed: true\n" + "\n" + "class Baz\n" + "extend T::Sig\n" + "\n" + "sig{returns(String)}\n" + "def hello\n" + "Bar.new.hello\n" + "end\n" + "end\n")), + {}); // clear counters getCounters(); @@ -174,11 +174,11 @@ TEST_CASE_FIXTURE(MultithreadedProtocolTest, "CancelsSlowPathWhenNewEditWouldTak } // Send a no-op to clear out the pipeline. Should have errors in bar and baz, but not foo. - assertDiagnostics(send(LSPMessage(make_unique("2.0", LSPMethod::SorbetFence, 20))), - { - {"bar.rb", 7, "Expected `Integer` but found `String(\"hi\")` for method result type"}, - {"baz.rb", 7, "Expected `String` but found `Integer` for method result type"}, - }); + assertErrorDiagnostics(send(LSPMessage(make_unique("2.0", LSPMethod::SorbetFence, 20))), + { + {"bar.rb", 7, "Expected `Integer` but found `String(\"hi\")` for method result type"}, + {"baz.rb", 7, "Expected `String` but found `Integer` for method result type"}, + }); auto counters = getCounters(); // N.B.: lsp.messages.processed contains canceled slow paths. @@ -206,12 +206,12 @@ TEST_CASE_FIXTURE(MultithreadedProtocolTest, "CancelsSlowPathWhenNewEditWouldTak TEST_CASE_FIXTURE(MultithreadedProtocolTest, "CancelsSlowPathWhenNewEditWouldTakeSlowPath") { auto initOptions = make_unique(); initOptions->enableTypecheckInfo = true; - assertDiagnostics( + assertErrorDiagnostics( initializeLSP(true /* supportsMarkdown */, true /* supportsCodeActionResolve */, move(initOptions)), {}); // Initial state: Two empty files. - assertDiagnostics(send(*openFile("foo.rb", "")), {}); - assertDiagnostics(send(*openFile("bar.rb", "")), {}); + assertErrorDiagnostics(send(*openFile("foo.rb", "")), {}); + assertErrorDiagnostics(send(*openFile("bar.rb", "")), {}); // clear counters getCounters(); @@ -246,11 +246,11 @@ TEST_CASE_FIXTURE(MultithreadedProtocolTest, "CancelsSlowPathWhenNewEditWouldTak } // Send a no-op to clear out the pipeline. Should have one error per file. - assertDiagnostics(send(LSPMessage(make_unique("2.0", LSPMethod::SorbetFence, 20))), - { - {"foo.rb", 6, "Expected `Integer` but found `String` for method result type"}, - {"bar.rb", 6, "Expected `String` but found `Integer(10)` for method result type"}, - }); + assertErrorDiagnostics(send(LSPMessage(make_unique("2.0", LSPMethod::SorbetFence, 20))), + { + {"foo.rb", 6, "Expected `Integer` but found `String` for method result type"}, + {"bar.rb", 6, "Expected `String` but found `Integer(10)` for method result type"}, + }); auto counters = getCounters(); // N.B.: lsp.messages.processed contains canceled slow paths. @@ -270,11 +270,11 @@ TEST_CASE_FIXTURE(MultithreadedProtocolTest, "CancelsSlowPathWhenNewEditWouldTak TEST_CASE_FIXTURE(MultithreadedProtocolTest, "CanPreemptSlowPathWithHover") { auto initOptions = make_unique(); initOptions->enableTypecheckInfo = true; - assertDiagnostics( + assertErrorDiagnostics( initializeLSP(true /* supportsMarkdown */, true /* supportsCodeActionResolve */, move(initOptions)), {}); // Create a new file. - assertDiagnostics(send(*openFile("foo.rb", "")), {}); + assertErrorDiagnostics(send(*openFile("foo.rb", "")), {}); // clear counters getCounters(); @@ -313,7 +313,7 @@ TEST_CASE_FIXTURE(MultithreadedProtocolTest, "CanPreemptSlowPathWithHover") { REQUIRE_EQ(*status, SorbetTypecheckRunStatus::Ended); } - assertDiagnostics(send(LSPMessage(make_unique("2.0", LSPMethod::SorbetFence, 20))), {}); + assertErrorDiagnostics(send(LSPMessage(make_unique("2.0", LSPMethod::SorbetFence, 20))), {}); auto counters = getCounters(); CHECK_EQ(counters.getCategoryCounter("lsp.messages.processed", "sorbet.workspaceEdit"), 1); @@ -337,11 +337,11 @@ TEST_CASE_FIXTURE(MultithreadedProtocolTest, "CanPreemptSlowPathWithCodeAction") auto initOptions = make_unique(); initOptions->enableTypecheckInfo = true; - assertDiagnostics( + assertErrorDiagnostics( initializeLSP(true /* supportsMarkdown */, true /* supportsCodeActionResolve */, move(initOptions)), {}); // Create a new file. - assertDiagnostics(send(*openFile("foo.rb", "")), {}); + assertErrorDiagnostics(send(*openFile("foo.rb", "")), {}); // clear counters getCounters(); @@ -384,7 +384,7 @@ TEST_CASE_FIXTURE(MultithreadedProtocolTest, "CanPreemptSlowPathWithCodeAction") REQUIRE_EQ(*status, SorbetTypecheckRunStatus::Ended); } - assertDiagnostics(send(LSPMessage(make_unique("2.0", LSPMethod::SorbetFence, 20))), {}); + assertErrorDiagnostics(send(LSPMessage(make_unique("2.0", LSPMethod::SorbetFence, 20))), {}); auto counters = getCounters(); CHECK_EQ(counters.getCategoryCounter("lsp.messages.processed", "sorbet.workspaceEdit"), 1); @@ -402,11 +402,11 @@ TEST_CASE_FIXTURE(MultithreadedProtocolTest, "CanPreemptSlowPathWithCodeAction") TEST_CASE_FIXTURE(MultithreadedProtocolTest, "CanPreemptSlowPathWithHoverAndReturnsErrors") { auto initOptions = make_unique(); initOptions->enableTypecheckInfo = true; - assertDiagnostics( + assertErrorDiagnostics( initializeLSP(true /* supportsMarkdown */, true /* supportsCodeActionResolve */, move(initOptions)), {}); // Create a new file. - assertDiagnostics(send(*openFile("foo.rb", "")), {}); + assertErrorDiagnostics(send(*openFile("foo.rb", "")), {}); // Slow path: Edit foo to have a class with a documentation string and a method with an error. sendAsync(*changeFile("foo.rb", @@ -434,10 +434,10 @@ TEST_CASE_FIXTURE(MultithreadedProtocolTest, "CanPreemptSlowPathWithHoverAndRetu } // Send a no-op to clear out the pipeline. Should have one error in `foo.rb`. - assertDiagnostics(send(LSPMessage(make_unique("2.0", LSPMethod::SorbetFence, 20))), - { - {"foo.rb", 6, "Expected `String` but found `Integer(3)` for method result type"}, - }); + assertErrorDiagnostics(send(LSPMessage(make_unique("2.0", LSPMethod::SorbetFence, 20))), + { + {"foo.rb", 6, "Expected `String` but found `Integer(3)` for method result type"}, + }); checkDiagnosticTimes(getCounters().getTimings("last_diagnostic_latency"), 2, /* assertUniqueStartTimes */ false); } @@ -445,12 +445,12 @@ TEST_CASE_FIXTURE(MultithreadedProtocolTest, "CanPreemptSlowPathWithHoverAndRetu TEST_CASE_FIXTURE(MultithreadedProtocolTest, "CanPreemptSlowPathWithFastPath") { auto initOptions = make_unique(); initOptions->enableTypecheckInfo = true; - assertDiagnostics( + assertErrorDiagnostics( initializeLSP(true /* supportsMarkdown */, true /* supportsCodeActionResolve */, move(initOptions)), {}); // Create two new files. - assertDiagnostics(send(*openFile("foo.rb", "")), {}); - assertDiagnostics(send(*openFile("bar.rb", "")), {}); + assertErrorDiagnostics(send(*openFile("foo.rb", "")), {}); + assertErrorDiagnostics(send(*openFile("bar.rb", "")), {}); // Slow path: Edit foo to have a class with two methods and two errors, and add an error to bar. sendAsync(LSPMessage(make_unique("2.0", LSPMethod::PAUSE, nullopt))); @@ -477,11 +477,12 @@ TEST_CASE_FIXTURE(MultithreadedProtocolTest, "CanPreemptSlowPathWithFastPath") { // Send a no-op to clear out the pipeline. Should have two error now: bar.rb from slow path and foo.rb from fast // path. - assertDiagnostics(send(LSPMessage(make_unique("2.0", LSPMethod::SorbetFence, 20))), - { - {"foo.rb", 9, "Expected `Float` but found `String(\"not a float\")` for method result type"}, - {"bar.rb", 5, "Expected `String` but found `Integer(1)` for method result type"}, - }); + assertErrorDiagnostics( + send(LSPMessage(make_unique("2.0", LSPMethod::SorbetFence, 20))), + { + {"foo.rb", 9, "Expected `Float` but found `String(\"not a float\")` for method result type"}, + {"bar.rb", 5, "Expected `String` but found `Integer(1)` for method result type"}, + }); checkDiagnosticTimes(getCounters().getTimings("last_diagnostic_latency"), 5, /* assertUniqueStartTimes */ false); } @@ -489,12 +490,12 @@ TEST_CASE_FIXTURE(MultithreadedProtocolTest, "CanPreemptSlowPathWithFastPath") { TEST_CASE_FIXTURE(MultithreadedProtocolTest, "CanPreemptSlowPathWithFastPathThatFixesAllErrors") { auto initOptions = make_unique(); initOptions->enableTypecheckInfo = true; - assertDiagnostics( + assertErrorDiagnostics( initializeLSP(true /* supportsMarkdown */, true /* supportsCodeActionResolve */, move(initOptions)), {}); // Create two new files. - assertDiagnostics(send(*openFile("foo.rb", "")), {}); - assertDiagnostics(send(*openFile("bar.rb", "")), {}); + assertErrorDiagnostics(send(*openFile("foo.rb", "")), {}); + assertErrorDiagnostics(send(*openFile("bar.rb", "")), {}); // Slow path: Edit foo to have a class with an error that also causes an error in bar sendAsync(LSPMessage(make_unique("2.0", LSPMethod::PAUSE, nullopt))); @@ -539,7 +540,7 @@ TEST_CASE_FIXTURE(MultithreadedProtocolTest, "CanPreemptSlowPathWithFastPathThat "end\n", 3)); // Send a no-op to clear out the pipeline. Should have no errors at end of both typechecking runs. - assertDiagnostics(send(LSPMessage(make_unique("2.0", LSPMethod::SorbetFence, 20))), {}); + assertErrorDiagnostics(send(LSPMessage(make_unique("2.0", LSPMethod::SorbetFence, 20))), {}); // Need to re-evaluate the test strategy for timings // checkDiagnosticTimes(getCounters().getTimings("last_diagnostic_latency"), 5, @@ -549,16 +550,16 @@ TEST_CASE_FIXTURE(MultithreadedProtocolTest, "CanPreemptSlowPathWithFastPathThat TEST_CASE_FIXTURE(MultithreadedProtocolTest, "CanPreemptSlowPathWithFastPathAndThenCancelBoth") { auto initOptions = make_unique(); initOptions->enableTypecheckInfo = true; - assertDiagnostics( + assertErrorDiagnostics( initializeLSP(true /* supportsMarkdown */, true /* supportsCodeActionResolve */, move(initOptions)), {}); // Create three new files! foo.rb defines a class, bar.rb defines a class and method used in baz.rb. - assertDiagnostics(send(*openFile("foo.rb", "# typed: true\nclass Foo\nextend T::Sig\nend")), {}); - assertDiagnostics( + assertErrorDiagnostics(send(*openFile("foo.rb", "# typed: true\nclass Foo\nextend T::Sig\nend")), {}); + assertErrorDiagnostics( send(*openFile("bar.rb", "# typed: true\nclass Bar\nextend T::Sig\nsig{returns(String)}\ndef str\n'hi'\nend\nend\n")), {}); - assertDiagnostics( + assertErrorDiagnostics( send(*openFile( "baz.rb", "# typed: true\nclass Baz\nextend T::Sig\nsig{returns(String)}\ndef bar\nBar.new.str\nend\nend\n")), @@ -586,11 +587,11 @@ TEST_CASE_FIXTURE(MultithreadedProtocolTest, "CanPreemptSlowPathWithFastPathAndT } // Fast path [cancel]: Fix syntax error. Foo should not have any errors. - assertDiagnostics(send(*changeFile("foo.rb", "# typed: true\nclass Foo\nextend T::Sig\nend", 4)), - { - {"bar.rb", 5, "Expected `Integer` but found `String(\"hi\")` for method result type"}, - {"baz.rb", 5, "Expected `String` but found `Integer` for method result type"}, - }); + assertErrorDiagnostics(send(*changeFile("foo.rb", "# typed: true\nclass Foo\nextend T::Sig\nend", 4)), + { + {"bar.rb", 5, "Expected `Integer` but found `String(\"hi\")` for method result type"}, + {"baz.rb", 5, "Expected `String` but found `Integer` for method result type"}, + }); auto counters = getCounters(); checkDiagnosticTimes(counters.getTimings("last_diagnostic_latency"), 6, /* assertUniqueStartTimes */ false); @@ -604,16 +605,16 @@ TEST_CASE_FIXTURE(MultithreadedProtocolTest, "CanPreemptSlowPathWithFastPathAndT TEST_CASE_FIXTURE(MultithreadedProtocolTest, "CanPreemptSlowPathWithFastPathAndBothErrorsAreReported") { auto initOptions = make_unique(); initOptions->enableTypecheckInfo = true; - assertDiagnostics( + assertErrorDiagnostics( initializeLSP(true /* supportsMarkdown */, true /* supportsCodeActionResolve */, move(initOptions)), {}); // Create three new files! foo.rb defines a class, bar.rb defines a class and method used in baz.rb. - assertDiagnostics(send(*openFile("foo.rb", "# typed: true\nclass Foo\nextend T::Sig\nend")), {}); - assertDiagnostics( + assertErrorDiagnostics(send(*openFile("foo.rb", "# typed: true\nclass Foo\nextend T::Sig\nend")), {}); + assertErrorDiagnostics( send(*openFile("bar.rb", "# typed: true\nclass Bar\nextend T::Sig\nsig{returns(String)}\ndef str\n'hi'\nend\nend\n")), {}); - assertDiagnostics( + assertErrorDiagnostics( send(*openFile( "baz.rb", "# typed: true\nclass Baz\nextend T::Sig\nsig{returns(String)}\ndef bar\nBar.new.str\nend\nend\n")), @@ -639,12 +640,12 @@ TEST_CASE_FIXTURE(MultithreadedProtocolTest, "CanPreemptSlowPathWithFastPathAndB // We should receive errors for `foo`, `bar`, and `baz`. // Send a no-op to clear out the pipeline. - assertDiagnostics(send(LSPMessage(make_unique("2.0", LSPMethod::SorbetFence, 20))), - { - {"foo.rb", 2, "unexpected token"}, - {"bar.rb", 5, "Expected `Integer` but found `String(\"hi\")` for method result type"}, - {"baz.rb", 5, "Expected `String` but found `Integer` for method result type"}, - }); + assertErrorDiagnostics(send(LSPMessage(make_unique("2.0", LSPMethod::SorbetFence, 20))), + { + {"foo.rb", 2, "unexpected token"}, + {"bar.rb", 5, "Expected `Integer` but found `String(\"hi\")` for method result type"}, + {"baz.rb", 5, "Expected `String` but found `Integer` for method result type"}, + }); checkDiagnosticTimes(getCounters().getTimings("last_diagnostic_latency"), 5, /* assertUniqueStartTimes */ false); } @@ -652,18 +653,18 @@ TEST_CASE_FIXTURE(MultithreadedProtocolTest, "CanPreemptSlowPathWithFastPathAndB TEST_CASE_FIXTURE(MultithreadedProtocolTest, "CanCancelSlowPathWithFastPathThatReintroducesOldError") { auto initOptions = make_unique(); initOptions->enableTypecheckInfo = true; - assertDiagnostics( + assertErrorDiagnostics( initializeLSP(true /* supportsMarkdown */, true /* supportsCodeActionResolve */, move(initOptions)), {}); // foo stands alone - assertDiagnostics(send(*openFile("foo.rb", "# typed: true\nclass Foo\nextend T::Sig\nend\n")), {}); + assertErrorDiagnostics(send(*openFile("foo.rb", "# typed: true\nclass Foo\nextend T::Sig\nend\n")), {}); // bar defines method - assertDiagnostics( + assertErrorDiagnostics( send(*openFile("bar.rb", "# typed: true\nclass Bar\nextend T::Sig\nsig{returns(Integer)}\ndef str\n10\nend\nend\n")), {}); // baz uses it - assertDiagnostics( + assertErrorDiagnostics( send(*openFile( "baz.rb", "# typed: true\nclass Baz\nextend T::Sig\nsig{returns(String)}\ndef bar\nBar.new.str\nend\nend\n")), @@ -692,8 +693,8 @@ TEST_CASE_FIXTURE(MultithreadedProtocolTest, "CanCancelSlowPathWithFastPathThatR sendAsync(LSPMessage(make_unique("2.0", LSPMethod::RESUME, nullopt))); // Send a no-op to clear out the pipeline. Should have one error on baz.rb. - assertDiagnostics(send(LSPMessage(make_unique("2.0", LSPMethod::SorbetFence, 20))), - {{"baz.rb", 5, "Expected `String` but found `Integer` for method result type"}}); + assertErrorDiagnostics(send(LSPMessage(make_unique("2.0", LSPMethod::SorbetFence, 20))), + {{"baz.rb", 5, "Expected `String` but found `Integer` for method result type"}}); checkDiagnosticTimes(getCounters().getTimings("last_diagnostic_latency"), 7, /* assertUniqueStartTimes */ false); } @@ -701,11 +702,11 @@ TEST_CASE_FIXTURE(MultithreadedProtocolTest, "CanCancelSlowPathWithFastPathThatR TEST_CASE_FIXTURE(MultithreadedProtocolTest, "CanCancelSlowPathEvenIfAddsFile") { auto initOptions = make_unique(); initOptions->enableTypecheckInfo = true; - assertDiagnostics( + assertErrorDiagnostics( initializeLSP(true /* supportsMarkdown */, true /* supportsCodeActionResolve */, move(initOptions)), {}); // bar has no error - assertDiagnostics( + assertErrorDiagnostics( send(*openFile("bar.rb", "# typed: true\nclass Bar\nextend T::Sig\nsig{returns(Integer)}\ndef hi\n10\nend\nend\n")), {}); @@ -747,10 +748,10 @@ TEST_CASE_FIXTURE(MultithreadedProtocolTest, "CanCancelSlowPathEvenIfAddsFile") "end")); // Send fence to clear out the pipeline. - assertDiagnostics(send(LSPMessage(make_unique("2.0", LSPMethod::SorbetFence, 20))), - {{"foo.rb", 5, "Expected `Integer` but found `String(\"hi\")` for method result type"}, - {"bar.rb", 1, "Hint: this \"class\" token is not closed before the end of the file"}, - {"bar.rb", 6, "unexpected"}}); + assertErrorDiagnostics(send(LSPMessage(make_unique("2.0", LSPMethod::SorbetFence, 20))), + {{"foo.rb", 5, "Expected `Integer` but found `String(\"hi\")` for method result type"}, + {"bar.rb", 1, "Hint: this \"class\" token is not closed before the end of the file"}, + {"bar.rb", 6, "unexpected"}}); checkDiagnosticTimes(getCounters().getTimings("last_diagnostic_latency"), 5, /* assertUniqueStartTimes */ false); } @@ -758,22 +759,22 @@ TEST_CASE_FIXTURE(MultithreadedProtocolTest, "CanCancelSlowPathEvenIfAddsFile") TEST_CASE_FIXTURE(MultithreadedProtocolTest, "SlowFooThenFastBarThenUndoSlowFoo") { auto initOptions = make_unique(); initOptions->enableTypecheckInfo = true; - assertDiagnostics( + assertErrorDiagnostics( initializeLSP(true /* supportsMarkdown */, true /* supportsCodeActionResolve */, move(initOptions)), {}); - assertDiagnostics(send(*openFile("foo.rb", "# typed: true\n" - "class Foo\n" - "end\n")), - {}); - assertDiagnostics(send(*openFile("bar.rb", "# typed: true\n" - "class Bar\n" - " extend T::Sig\n" - " sig {returns(Integer)}\n" - " def example\n" - " 1+1\n" - " end\n" - "end\n")), - {}); + assertErrorDiagnostics(send(*openFile("foo.rb", "# typed: true\n" + "class Foo\n" + "end\n")), + {}); + assertErrorDiagnostics(send(*openFile("bar.rb", "# typed: true\n" + "class Bar\n" + " extend T::Sig\n" + " sig {returns(Integer)}\n" + " def example\n" + " 1+1\n" + " end\n" + "end\n")), + {}); // Slow path in foo.rb sendAsync(*changeFile("foo.rb", @@ -810,14 +811,14 @@ TEST_CASE_FIXTURE(MultithreadedProtocolTest, "SlowFooThenFastBarThenUndoSlowFoo" sendAsync(LSPMessage(make_unique("2.0", LSPMethod::RESUME, nullopt))); // Send a no-op to clear out the pipeline. Should have one error on baz.rb. - assertDiagnostics(send(LSPMessage(make_unique("2.0", LSPMethod::SorbetFence, 20))), - {{"bar.rb", 5, "Expected `String` but found `Integer` for method result type"}}); + assertErrorDiagnostics(send(LSPMessage(make_unique("2.0", LSPMethod::SorbetFence, 20))), + {{"bar.rb", 5, "Expected `String` but found `Integer` for method result type"}}); } TEST_CASE_FIXTURE(MultithreadedProtocolTest, "CanceledRequestsDontReportLatencyMetrics") { - assertDiagnostics(initializeLSP(), {}); + assertErrorDiagnostics(initializeLSP(), {}); // Create a new file. - assertDiagnostics( + assertErrorDiagnostics( send(*openFile("foo.rb", "# typed: true\n# A class that does things.\nclass Foo\nextend T::Sig\nend\n")), {}); // clear counters @@ -845,16 +846,16 @@ TEST_CASE_FIXTURE(MultithreadedProtocolTest, "CanceledRequestsDontReportLatencyM TEST_CASE_FIXTURE(MultithreadedProtocolTest, "ErrorIntroducedInSlowPathPreemptionByFastPathClearedByNewSlowPath") { auto initOptions = make_unique(); initOptions->enableTypecheckInfo = true; - assertDiagnostics( + assertErrorDiagnostics( initializeLSP(true /* supportsMarkdown */, true /* supportsCodeActionResolve */, move(initOptions)), {}); // Create new file - assertDiagnostics(send(*openFile("foo.rb", "# typed: true\n" - "module M\n" - "end\n" - "class Foo\n" - "end")), - {}); + assertErrorDiagnostics(send(*openFile("foo.rb", "# typed: true\n" + "module M\n" + "end\n" + "class Foo\n" + "end")), + {}); // Slow path: no errors sendAsync(*changeFile("foo.rb", @@ -905,17 +906,17 @@ TEST_CASE_FIXTURE(MultithreadedProtocolTest, "ErrorIntroducedInSlowPathPreemptio 4, false, 0)); // Send a no-op to clear out the pipeline. - assertDiagnostics(send(LSPMessage(make_unique("2.0", LSPMethod::SorbetFence, 20))), {}); + assertErrorDiagnostics(send(LSPMessage(make_unique("2.0", LSPMethod::SorbetFence, 20))), {}); } TEST_CASE_FIXTURE(MultithreadedProtocolTest, "StallInSlowPathWorks") { auto initOptions = make_unique(); initOptions->enableTypecheckInfo = true; - assertDiagnostics( + assertErrorDiagnostics( initializeLSP(true /* supportsMarkdown */, true /* supportsCodeActionResolve */, move(initOptions)), {}); // Create a simple file. - assertDiagnostics(send(*openFile("foo.rb", "")), {}); + assertErrorDiagnostics(send(*openFile("foo.rb", "")), {}); sendAsync(*changeFile("foo.rb", "# typed: true\n" "class Foo\n" diff --git a/test/lsp/protocol_test_corpus.cc b/test/lsp/protocol_test_corpus.cc index ec755a89ff..0490da49a8 100644 --- a/test/lsp/protocol_test_corpus.cc +++ b/test/lsp/protocol_test_corpus.cc @@ -12,23 +12,23 @@ using namespace sorbet::realmain::lsp; // Adds two new files that have errors, and asserts that Sorbet returns errors for both of them. TEST_CASE_FIXTURE(ProtocolTest, "AddFile") { - assertDiagnostics(initializeLSP(), {}); - assertDiagnostics(send(*openFile("yolo1.rb", "")), {}); + assertErrorDiagnostics(initializeLSP(), {}); + assertErrorDiagnostics(send(*openFile("yolo1.rb", "")), {}); ExpectedDiagnostic yolo1Diagnostic = {"yolo1.rb", 3, "Expected `Integer`"}; - assertDiagnostics( + assertErrorDiagnostics( send(*changeFile("yolo1.rb", "# typed: true\nclass Foo1\n def branch\n 1 + \"stuff\"\n end\nend\n", 2)), {yolo1Diagnostic}); - assertDiagnostics(send(*openFile("yolo2.rb", "")), {yolo1Diagnostic}); + assertErrorDiagnostics(send(*openFile("yolo2.rb", "")), {yolo1Diagnostic}); ExpectedDiagnostic yolo2Diagnostic = {"yolo2.rb", 4, "Expected `Integer`"}; - assertDiagnostics( + assertErrorDiagnostics( send(*changeFile("yolo2.rb", "# typed: true\nclass Foo2\n\n def branch\n 1 + \"stuff\"\n end\nend\n", 2)), {yolo1Diagnostic, yolo2Diagnostic}); // Slightly change text so that error changes line and contents. ExpectedDiagnostic yolo2Diagnostic2 = {"yolo2.rb", 5, "stuff3"}; - assertDiagnostics( + assertErrorDiagnostics( send( *changeFile("yolo2.rb", "# typed: true\nclass Foo2\n\n\n def branch\n 1 + \"stuff3\"\n end\nend\n", 3)), {yolo1Diagnostic, yolo2Diagnostic2}); @@ -36,18 +36,18 @@ TEST_CASE_FIXTURE(ProtocolTest, "AddFile") { // Write to the same file twice. Sorbet should only return errors from the second version. TEST_CASE_FIXTURE(ProtocolTest, "AddFileJoiningRequests") { - assertDiagnostics(initializeLSP(), {}); + assertErrorDiagnostics(initializeLSP(), {}); vector> requests; requests.push_back(openFile("yolo1.rb", "# typed: true\nclass Foo2\n def branch\n 2 + \"dog\"\n end\nend\n")); requests.push_back( changeFile("yolo1.rb", "# typed: true\nclass Foo1\n def branch\n 1 + \"bear\"\n end\nend\n", 3)); - assertDiagnostics(send(move(requests)), {{"yolo1.rb", 3, "bear"}}); + assertErrorDiagnostics(send(move(requests)), {{"yolo1.rb", 3, "bear"}}); } // Cancels requests before they are processed, and ensures that they are actually not processed. TEST_CASE_FIXTURE(ProtocolTest, "Cancellation") { - assertDiagnostics(initializeLSP(), {}); - assertDiagnostics( + assertErrorDiagnostics(initializeLSP(), {}); + assertErrorDiagnostics( send(*openFile("foo.rb", "#typed: true\nmodule Bar\n CONST = 2\n\n def self.meth(x)\n x\n " "end\nend\n\nlocal = 131\nlocaler = local + 2\nlocaler2 = localer + 2\nlocal3 = localer + " @@ -90,8 +90,8 @@ TEST_CASE_FIXTURE(ProtocolTest, "Cancellation") { // Asserts that Sorbet returns an empty result when requesting definitions in untyped Ruby files. TEST_CASE_FIXTURE(ProtocolTest, "DefinitionError") { - assertDiagnostics(initializeLSP(), {}); - assertDiagnostics(send(*openFile("foobar.rb", "class Foobar\n def bar\n 1\n end\nend\n\nbar\n")), {}); + assertErrorDiagnostics(initializeLSP(), {}); + assertErrorDiagnostics(send(*openFile("foobar.rb", "class Foobar\n def bar\n 1\n end\nend\n\nbar\n")), {}); auto defResponses = send(*getDefinition("foobar.rb", 6, 1)); INFO("Expected a single response to a definition request to an untyped document."); const auto numResponses = absl::c_count_if(defResponses, [](const auto &m) { return m->isResponse(); }); @@ -113,7 +113,7 @@ TEST_CASE_FIXTURE(ProtocolTest, "DefinitionError") { // Ensures that Sorbet merges didChanges that are interspersed with canceled requests. TEST_CASE_FIXTURE(ProtocolTest, "MergeDidChangeAfterCancellation") { - assertDiagnostics(initializeLSP(), {}); + assertErrorDiagnostics(initializeLSP(), {}); vector> requests; // File is fine at first. requests.push_back(openFile("foo.rb", "")); @@ -156,7 +156,7 @@ TEST_CASE_FIXTURE(ProtocolTest, "MergeDidChangeAfterCancellation") { FAIL_CHECK(fmt::format("Unexpected response:\n{}", msg->toJSON())); } } - assertDiagnostics({}, {{"foo.rb", 7, "Method `blah` does not exist"}}); + assertErrorDiagnostics({}, {{"foo.rb", 7, "Method `blah` does not exist"}}); CHECK_EQ(cancelRequestCount, 3); // Expected a diagnostic error for foo.rb CHECK_EQ(diagnosticCount, 1); @@ -164,7 +164,7 @@ TEST_CASE_FIXTURE(ProtocolTest, "MergeDidChangeAfterCancellation") { // Applies all consecutive file changes at once. TEST_CASE_FIXTURE(ProtocolTest, "MergesDidChangesAcrossFiles") { - assertDiagnostics(initializeLSP(), {}); + assertErrorDiagnostics(initializeLSP(), {}); vector> requests; // File is fine at first. requests.push_back(openFile("foo.rb", "")); @@ -192,14 +192,14 @@ TEST_CASE_FIXTURE(ProtocolTest, "MergesDidChangesAcrossFiles") { auto msgs = send(move(requests)); INFO("Expected only 4 diagnostic responses to the merged file changes"); CHECK_EQ(msgs.size(), 4); - assertDiagnostics(move(msgs), {{"bar.rb", 3, "Expected `Integer`"}, - {"baz.rb", 3, "Expected `Integer`"}, - {"bat.rb", 3, "Expected `Integer`"}, - {"foo.rb", 7, "Method `blah` does not exist"}}); + assertErrorDiagnostics(move(msgs), {{"bar.rb", 3, "Expected `Integer`"}, + {"baz.rb", 3, "Expected `Integer`"}, + {"bat.rb", 3, "Expected `Integer`"}, + {"foo.rb", 7, "Method `blah` does not exist"}}); } TEST_CASE_FIXTURE(ProtocolTest, "MergesDidChangesAcrossDelayableRequests") { - assertDiagnostics(initializeLSP(), {}); + assertErrorDiagnostics(initializeLSP(), {}); vector> requests; // Invalid: Returns false. requests.push_back(openFile("foo.rb", "# typed: true\n\nclass Opus::CIBot::Tasks::Foo\n extend T::Sig\n\n sig " @@ -222,7 +222,7 @@ TEST_CASE_FIXTURE(ProtocolTest, "MergesDidChangesAcrossDelayableRequests") { REQUIRE_GT(msgs.size(), 0); CHECK(msgs.at(0)->isNotification()); CHECK_EQ(msgs.at(0)->method(), LSPMethod::TextDocumentPublishDiagnostics); - assertDiagnostics({}, {{"foo.rb", 7, "Method `blah` does not exist"}}); + assertErrorDiagnostics({}, {{"foo.rb", 7, "Method `blah` does not exist"}}); INFO("Expected a diagnostic error, followed by two document symbol responses."); REQUIRE_EQ(msgs.size(), 3); @@ -231,7 +231,7 @@ TEST_CASE_FIXTURE(ProtocolTest, "MergesDidChangesAcrossDelayableRequests") { } TEST_CASE_FIXTURE(ProtocolTest, "DoesNotMergeFileChangesAcrossNonDelayableRequests") { - assertDiagnostics(initializeLSP(), {}); + assertErrorDiagnostics(initializeLSP(), {}); vector> requests; requests.push_back(openFile("foo.rb", "# typed: true\n\nclass Opus::CIBot::Tasks::Foo\n extend T::Sig\n\n sig " "{returns(Integer)}\n def bar\n false\n end\nend\n")); @@ -275,26 +275,27 @@ TEST_CASE_FIXTURE(ProtocolTest, "WorkspaceEditIgnoredWhenNotInitialized") { // Avoid using `openFile`, as it only works post-initialization. toSend.push_back(makeOpen("bar.rb", "# typed: true\nclass Foo1\n def branch\n 1 + \"stuff\"\n end\nend\n", 1)); // This update should be ignored. - assertDiagnostics(send(move(toSend)), {}); + assertErrorDiagnostics(send(move(toSend)), {}); // We shouldn't have any code errors post-initialization since the previous edit was ignored. - assertDiagnostics(initializeLSP(), {}); + assertErrorDiagnostics(initializeLSP(), {}); } TEST_CASE_FIXTURE(ProtocolTest, "InitializeAndShutdown") { - assertDiagnostics(initializeLSP(), {}); + assertErrorDiagnostics(initializeLSP(), {}); auto resp = send(LSPMessage(make_unique("2.0", nextId++, LSPMethod::Shutdown, JSONNullObject()))); INFO("Expected a single response to shutdown request."); REQUIRE_EQ(resp.size(), 1); auto &r = resp.at(0)->asResponse(); REQUIRE_EQ(r.requestMethod, LSPMethod::Shutdown); - assertDiagnostics(send(LSPMessage(make_unique("2.0", LSPMethod::Exit, JSONNullObject()))), {}); + assertErrorDiagnostics(send(LSPMessage(make_unique("2.0", LSPMethod::Exit, JSONNullObject()))), + {}); } // Some clients send an empty string for the root uri. TEST_CASE_FIXTURE(ProtocolTest, "EmptyRootUriInitialization") { // Manually reset rootUri before initializing. rootUri = ""; - assertDiagnostics(initializeLSP(), {}); + assertErrorDiagnostics(initializeLSP(), {}); // Manually construct an openFile with text that has a typechecking error. auto didOpenParams = make_unique(make_unique( @@ -329,9 +330,9 @@ TEST_CASE_FIXTURE(ProtocolTest, "MissingRootPathInitialization") { auto &resp = respMsg->asResponse(); CHECK_EQ(resp.requestMethod, LSPMethod::Initialize); } - assertDiagnostics(send(LSPMessage(make_unique("2.0", LSPMethod::Initialized, - make_unique()))), - {}); + assertErrorDiagnostics(send(LSPMessage(make_unique("2.0", LSPMethod::Initialized, + make_unique()))), + {}); // Manually construct an openFile with text that has a typechecking error. auto didOpenParams = make_unique(make_unique( @@ -366,9 +367,9 @@ TEST_CASE_FIXTURE(ProtocolTest, "MonacoInitialization") { auto &resp = respMsg->asResponse(); CHECK_EQ(resp.requestMethod, LSPMethod::Initialize); } - assertDiagnostics(send(LSPMessage(make_unique("2.0", LSPMethod::Initialized, - make_unique()))), - {}); + assertErrorDiagnostics(send(LSPMessage(make_unique("2.0", LSPMethod::Initialized, + make_unique()))), + {}); // Manually construct an openFile with text that has a typechecking error. auto didOpenParams = make_unique(make_unique( @@ -387,8 +388,8 @@ TEST_CASE_FIXTURE(ProtocolTest, "MonacoInitialization") { } TEST_CASE_FIXTURE(ProtocolTest, "CompletionOnNonClass") { - assertDiagnostics(initializeLSP(), {}); - assertDiagnostics(send(*openFile("yolo1.rb", "# typed: true\nclass A\nend\nA")), {}); + assertErrorDiagnostics(initializeLSP(), {}); + assertErrorDiagnostics(send(*openFile("yolo1.rb", "# typed: true\nclass A\nend\nA")), {}); // TODO: Once we have better helpers for completion, clean this up. auto completionParams = make_unique(make_unique(getUri("yolo1.rb")), @@ -403,23 +404,23 @@ TEST_CASE_FIXTURE(ProtocolTest, "CompletionOnNonClass") { // Ensures that unrecognized notifications are ignored. TEST_CASE_FIXTURE(ProtocolTest, "IgnoresUnrecognizedNotifications") { - assertDiagnostics(initializeLSP(), {}); - assertDiagnostics(sendRaw("{\"jsonrpc\":\"2.0\",\"method\":\"workspace/" - "didChangeConfiguration\",\"params\":{\"settings\":{\"ruby-typer\":{}}}}"), - {}); + assertErrorDiagnostics(initializeLSP(), {}); + assertErrorDiagnostics(sendRaw("{\"jsonrpc\":\"2.0\",\"method\":\"workspace/" + "didChangeConfiguration\",\"params\":{\"settings\":{\"ruby-typer\":{}}}}"), + {}); } // Ensures that notifications that have an improper params shape are handled gracefully / not responded to. TEST_CASE_FIXTURE(ProtocolTest, "IgnoresNotificationsThatDontTypecheck") { - assertDiagnostics(initializeLSP(), {}); - assertDiagnostics(sendRaw(R"({"jsonrpc":"2.0","method":"textDocument/didChange","params":{}})"), {}); + assertErrorDiagnostics(initializeLSP(), {}); + assertErrorDiagnostics(sendRaw(R"({"jsonrpc":"2.0","method":"textDocument/didChange","params":{}})"), {}); } // Ensures that unrecognized requests are responded to. TEST_CASE_FIXTURE(ProtocolTest, "RejectsUnrecognizedRequests") { - assertDiagnostics(initializeLSP(), {}); - auto responses = sendRaw("{\"jsonrpc\":\"2.0\",\"method\":\"workspace/" - "didChangeConfiguration\",\"id\":9001,\"params\":{\"settings\":{\"ruby-typer\":{}}}}"); + assertErrorDiagnostics(initializeLSP(), {}); + auto responses = sendRaw("{\"jsonrpc\":\"2.0\",\"method\":\"sorbet/" + "fooBar\",\"id\":9001,\"params\":{\"settings\":{\"highlightUntyped\": false}}}"); REQUIRE_EQ(responses.size(), 1); auto &response = responses.at(0); REQUIRE(response->isResponse()); @@ -433,7 +434,7 @@ TEST_CASE_FIXTURE(ProtocolTest, "RejectsUnrecognizedRequests") { // Ensures that requests that have an improper params shape are responded to with an error. TEST_CASE_FIXTURE(ProtocolTest, "RejectsRequestsThatDontTypecheck") { - assertDiagnostics(initializeLSP(), {}); + assertErrorDiagnostics(initializeLSP(), {}); auto responses = sendRaw("{\"jsonrpc\":\"2.0\",\"method\":\"textDocument/" "hover\",\"id\":9001,\"params\":{\"settings\":{\"ruby-typer\":{}}}}"); REQUIRE_EQ(responses.size(), 1); @@ -449,15 +450,15 @@ TEST_CASE_FIXTURE(ProtocolTest, "RejectsRequestsThatDontTypecheck") { // Ensures that the server ignores invalid JSON. TEST_CASE_FIXTURE(ProtocolTest, "SilentlyIgnoresInvalidJSONMessages") { - assertDiagnostics(initializeLSP(), {}); - assertDiagnostics(sendRaw("{"), {}); + assertErrorDiagnostics(initializeLSP(), {}); + assertErrorDiagnostics(sendRaw("{"), {}); } // If a client doesn't support markdown, send hover as plaintext. TEST_CASE_FIXTURE(ProtocolTest, "RespectsHoverTextLimitations") { - assertDiagnostics(initializeLSP(false /* supportsMarkdown */), {}); + assertErrorDiagnostics(initializeLSP(false /* supportsMarkdown */), {}); - assertDiagnostics(send(*openFile("foobar.rb", "# typed: true\n1\n")), {}); + assertErrorDiagnostics(send(*openFile("foobar.rb", "# typed: true\n1\n")), {}); auto hoverResponses = send(LSPMessage(make_unique( "2.0", nextId++, LSPMethod::TextDocumentHover, @@ -481,10 +482,10 @@ TEST_CASE_FIXTURE(ProtocolTest, "SorbetURIsWork") { auto initOptions = make_unique(); initOptions->supportsSorbetURIs = true; lspWrapper->opts->lspDirsMissingFromClient.emplace_back("/folder"); - assertDiagnostics(initializeLSP(supportsMarkdown, supportsCodeActionResolve, move(initOptions)), {}); + assertErrorDiagnostics(initializeLSP(supportsMarkdown, supportsCodeActionResolve, move(initOptions)), {}); string fileContents = "# typed: true\n[0,1,2,3].select {|x| x > 0}\ndef myMethod; end;\n"; - assertDiagnostics(send(*openFile("folder/foo.rb", fileContents)), {}); + assertErrorDiagnostics(send(*openFile("folder/foo.rb", fileContents)), {}); auto selectDefinitions = getDefinitions("folder/foo.rb", 1, 11); REQUIRE_EQ(selectDefinitions.size(), 1); @@ -588,22 +589,22 @@ TEST_CASE_FIXTURE(ProtocolTest, "DoesNotCrashOnFormattingNonWorkspaceURIs") { // Tests that Sorbet reports metrics about the request's response status for certain requests TEST_CASE_FIXTURE(ProtocolTest, "RequestReportsEmptyResultsMetrics") { - assertDiagnostics(initializeLSP(), {}); + assertErrorDiagnostics(initializeLSP(), {}); // Create a new file. - assertDiagnostics(send(*openFile("foo.rb", "# typed: true\n" - "class A\n" - "def foo; end\n" - "end\n" - "A.new.fo\n" - "A.new.no_completion_results\n" - "A.new.foo\n" - "T.unsafe(nil).foo\n" - "\n")), - { - {"foo.rb", 4, "does not exist"}, - {"foo.rb", 5, "does not exist"}, - }); + assertErrorDiagnostics(send(*openFile("foo.rb", "# typed: true\n" + "class A\n" + "def foo; end\n" + "end\n" + "A.new.fo\n" + "A.new.no_completion_results\n" + "A.new.foo\n" + "T.unsafe(nil).foo\n" + "\n")), + { + {"foo.rb", 4, "does not exist"}, + {"foo.rb", 5, "does not exist"}, + }); // clear counters getCounters(); @@ -664,34 +665,64 @@ TEST_CASE_FIXTURE(ProtocolTest, "RequestReportsEmptyResultsMetrics") { } TEST_CASE_FIXTURE(ProtocolTest, "ReportsSyntaxErrors") { - assertDiagnostics(initializeLSP(), {}); + assertErrorDiagnostics(initializeLSP(), {}); // Create a new file. - assertDiagnostics(send(*openFile("foo.rb", "# typed: true\n" - "class A\n" - "def foo; end\n" - "end\n" - "\n")), - {}); + assertErrorDiagnostics(send(*openFile("foo.rb", "# typed: true\n" + "class A\n" + "def foo; end\n" + "end\n" + "\n")), + {}); // clear counters getCounters(); - assertDiagnostics(send(*changeFile("foo.rb", - "# typed: true\n" - "class A\n" - "def foo(; end\n" - "end\n" - "\n", - 2)), - { - {"foo.rb", 1, "class definition in method body"}, - {"foo.rb", 2, "unexpected token \";\""}, - }); + assertErrorDiagnostics(send(*changeFile("foo.rb", + "# typed: true\n" + "class A\n" + "def foo(; end\n" + "end\n" + "\n", + 2)), + { + {"foo.rb", 1, "class definition in method body"}, + {"foo.rb", 2, "unexpected token \";\""}, + }); auto counters = getCounters(); CHECK_EQ(counters.getCategoryCounter("lsp.slow_path_reason", "syntax_error"), 1); CHECK_EQ(counters.getCategoryCounter("lsp.slow_path_reason", "changed_definition"), 0); } +TEST_CASE_FIXTURE(ProtocolTest, "DidChangeConfigurationNotificationUpdatesHighlightUntypedSetting") { + assertErrorDiagnostics(initializeLSP(), {}); + + // Create a new file. + assertErrorDiagnostics(send(*openFile("foo.rb", "# typed: true\n" + "class A\n" + "def foo; end\n" + "end\n" + "\n")), + {}); + auto settings = make_unique(); + settings->highlightUntyped = true; + auto config = make_unique(move(settings)); + send(*makeConfigurationChange(move(config))); + + assertUntypedDiagnostics(send(*changeFile("foo.rb", + "# typed: true\n" + "class A\n" + " def foo(x)\n;" + " x.foo\n" + " 5\n" + " end\n" + "end\n" + "\n", + 2)), + { + {"foo.rb", 3, "Call to method `foo` on `T.untyped`"}, + }); +} + } // namespace sorbet::test::lsp diff --git a/test/lsp/watchman_test_corpus.cc b/test/lsp/watchman_test_corpus.cc index 760c245964..785d2026cf 100644 --- a/test/lsp/watchman_test_corpus.cc +++ b/test/lsp/watchman_test_corpus.cc @@ -10,89 +10,89 @@ using namespace sorbet::realmain::lsp; // Adds a file to the file system with an error, and asserts that Sorbet returns an error. TEST_CASE_FIXTURE(ProtocolTest, "UpdateFileOnFileSystem") { - assertDiagnostics(initializeLSP(), {}); + assertErrorDiagnostics(initializeLSP(), {}); writeFilesToFS({{"foo.rb", "# typed: true\nclass Foo1\n def branch\n 1 + \"stuff\"\n end\nend\n"}}); ExpectedDiagnostic d = {"foo.rb", 3, "Expected `Integer`"}; - assertDiagnostics(send(*watchmanFileUpdate({"foo.rb"})), {d}); + assertErrorDiagnostics(send(*watchmanFileUpdate({"foo.rb"})), {d}); } // Creates an empty file and deletes it. TEST_CASE_FIXTURE(ProtocolTest, "CreateAndDeleteEmptyFile") { - assertDiagnostics(initializeLSP(), {}); + assertErrorDiagnostics(initializeLSP(), {}); writeFilesToFS({{"foo.rb", ""}}); - assertDiagnostics(send(*watchmanFileUpdate({"foo.rb"})), {}); + assertErrorDiagnostics(send(*watchmanFileUpdate({"foo.rb"})), {}); deleteFileFromFS("foo.rb"); - assertDiagnostics(send(*watchmanFileUpdate({"foo.rb"})), {}); + assertErrorDiagnostics(send(*watchmanFileUpdate({"foo.rb"})), {}); } // Adds a file with an error, and then deletes that file. Asserts that Sorbet no longer complains about the file. TEST_CASE_FIXTURE(ProtocolTest, "DeleteFileWithErrors") { - assertDiagnostics(initializeLSP(), {}); + assertErrorDiagnostics(initializeLSP(), {}); writeFilesToFS({{"foo.rb", "# typed: true\nclass Foo1\n def branch\n 1 + \"stuff\"\n end\nend\n"}}); ExpectedDiagnostic d = {"foo.rb", 3, "Expected `Integer`"}; - assertDiagnostics(send(*watchmanFileUpdate({"foo.rb"})), {d}); + assertErrorDiagnostics(send(*watchmanFileUpdate({"foo.rb"})), {d}); deleteFileFromFS("foo.rb"); - assertDiagnostics(send(*watchmanFileUpdate({"foo.rb"})), {}); + assertErrorDiagnostics(send(*watchmanFileUpdate({"foo.rb"})), {}); } // Informs Sorbet about a file update for a file it does not know about and is deleted on disk. Should be a no-op. TEST_CASE_FIXTURE(ProtocolTest, "DeleteFileUnknownToSorbet") { - assertDiagnostics(initializeLSP(), {}); - assertDiagnostics(send(*watchmanFileUpdate({"foo.rb"})), {}); + assertErrorDiagnostics(initializeLSP(), {}); + assertErrorDiagnostics(send(*watchmanFileUpdate({"foo.rb"})), {}); } // Updates a file, opens it in editor (but it's empty), closes file without saving to disk. TEST_CASE_FIXTURE(ProtocolTest, "IgnoresLSPFileUpdatesWhileFileIsOpen") { - assertDiagnostics(initializeLSP(), {}); + assertErrorDiagnostics(initializeLSP(), {}); ExpectedDiagnostic d = {"foo.rb", 3, "Expected `Integer`"}; writeFilesToFS({{"foo.rb", "# typed: true\nclass Foo1\n def branch\n 1 + \"stuff\"\n end\nend\n"}}); - assertDiagnostics(send(*watchmanFileUpdate({"foo.rb"})), {d}); + assertErrorDiagnostics(send(*watchmanFileUpdate({"foo.rb"})), {d}); // Diagnostics should update now that we've opened the file in editor and it's empty. - assertDiagnostics(send(*openFile("foo.rb", "")), {}); + assertErrorDiagnostics(send(*openFile("foo.rb", "")), {}); // File on disk is still buggy, but Sorbet should ignore disk updates while file is open in editor. - assertDiagnostics(send(*watchmanFileUpdate({"foo.rb"})), {}); + assertErrorDiagnostics(send(*watchmanFileUpdate({"foo.rb"})), {}); // Sorbet should pick up buggy disk version after user closes file. - assertDiagnostics(send(*closeFile("foo.rb")), {d}); + assertErrorDiagnostics(send(*closeFile("foo.rb")), {d}); } // Ensures that Sorbet correctly remembers that a file is not open in the editor when it combines a file close event // with another type of file update. TEST_CASE_FIXTURE(ProtocolTest, "CorrectlyUpdatesFileOpenStatusWhenClosedCombinedWithOtherUpdates") { - assertDiagnostics(initializeLSP(), {}); + assertErrorDiagnostics(initializeLSP(), {}); ExpectedDiagnostic d = {"foo.rb", 3, "Expected `Integer`"}; writeFilesToFS({{"foo.rb", "# typed: true\nclass Foo1\n def branch\n 1 + \"stuff\"\n end\nend\n"}}); - assertDiagnostics(send(*watchmanFileUpdate({"foo.rb"})), {d}); + assertErrorDiagnostics(send(*watchmanFileUpdate({"foo.rb"})), {d}); // Diagnostics should update now that we've opened the file in editor and it's empty. - assertDiagnostics(send(*openFile("foo.rb", "")), {}); + assertErrorDiagnostics(send(*openFile("foo.rb", "")), {}); // Close + add another update in one atomic action. vector> toSend; toSend.push_back(closeFile("foo.rb")); toSend.push_back(watchmanFileUpdate({"foo.rb"})); - assertDiagnostics(send(move(toSend)), {d}); + assertErrorDiagnostics(send(move(toSend)), {d}); // Ensure that Sorbet knows file is closed. - assertDiagnostics(send(*watchmanFileUpdate({"foo.rb"})), {d}); + assertErrorDiagnostics(send(*watchmanFileUpdate({"foo.rb"})), {d}); } // If file closes and is not on disk, Sorbet clears diagnostics. TEST_CASE_FIXTURE(ProtocolTest, "HandlesClosedAndDeletedFile") { - assertDiagnostics(initializeLSP(), {}); + assertErrorDiagnostics(initializeLSP(), {}); ExpectedDiagnostic d = {"foo.rb", 3, "Expected `Integer`"}; - assertDiagnostics( + assertErrorDiagnostics( send(*openFile("foo.rb", "# typed: true\nclass Foo1\n def branch\n 1 + \"stuff\"\n end\nend\n")), {d}); - assertDiagnostics(send(*closeFile("foo.rb")), {}); + assertErrorDiagnostics(send(*closeFile("foo.rb")), {}); } // Sorbet merges all pending watchman updates into a single update. TEST_CASE_FIXTURE(ProtocolTest, "MergesMultipleWatchmanUpdates") { - assertDiagnostics(initializeLSP(), {}); + assertErrorDiagnostics(initializeLSP(), {}); vector> requests; // If processed serially, these would cause slow path runs (new files). requests.push_back(watchmanFileUpdate({"foo.rb"})); @@ -104,11 +104,11 @@ TEST_CASE_FIXTURE(ProtocolTest, "MergesMultipleWatchmanUpdates") { string buggyFileContents = "# typed: true\nclass Foo1\n def branch\n 1 + \"stuff\"\n end\nend\n"; writeFilesToFS({{"foo.rb", buggyFileContents}, {"bar.rb", buggyFileContents}, {"baz.rb", buggyFileContents}}); - assertDiagnostics(send(move(requests)), { - {"foo.rb", 3, "Expected `Integer`"}, - {"bar.rb", 3, "Expected `Integer`"}, - {"baz.rb", 3, "Expected `Integer`"}, - }); + assertErrorDiagnostics(send(move(requests)), { + {"foo.rb", 3, "Expected `Integer`"}, + {"bar.rb", 3, "Expected `Integer`"}, + {"baz.rb", 3, "Expected `Integer`"}, + }); // getTypecheckCount tracks the number of times typechecking has run on the same clone from LSPLoop's // initialGS. It's reset to 1 after each slow path run, and incremented after every fast path. diff --git a/test/lsp_test_runner.cc b/test/lsp_test_runner.cc index 2d9ebc9439..e88f041e56 100644 --- a/test/lsp_test_runner.cc +++ b/test/lsp_test_runner.cc @@ -546,11 +546,16 @@ TEST_CASE("LSPTest") { opts->noStdlib = BooleanPropertyAssertion::getValue("no-stdlib", assertions).value_or(false); opts->ruby3KeywordArgs = BooleanPropertyAssertion::getValue("experimental-ruby3-keyword-args", assertions).value_or(false); + opts->typedSuper = BooleanPropertyAssertion::getValue("typed-super", assertions).value_or(true); + // TODO(jez) Allow suppressPayloadSuperclassRedefinitionFor in a testdata test assertion? opts->stripeMode = BooleanPropertyAssertion::getValue("stripe-mode", assertions).value_or(false); opts->outOfOrderReferenceChecksEnabled = BooleanPropertyAssertion::getValue("check-out-of-order-constant-references", assertions).value_or(false); opts->requiresAncestorEnabled = BooleanPropertyAssertion::getValue("enable-experimental-requires-ancestor", assertions).value_or(false); + opts->lspExtractToVariableEnabled = + BooleanPropertyAssertion::getValue("enable-experimental-lsp-extract-to-variable", assertions) + .value_or(false); opts->stripePackages = BooleanPropertyAssertion::getValue("enable-packager", assertions).value_or(false); if (opts->stripePackages) { @@ -564,13 +569,11 @@ TEST_CASE("LSPTest") { if (extraDirSlash.has_value()) { opts->extraPackageFilesDirectorySlashPrefixes.emplace_back(extraDirSlash.value()); } - opts->secondaryTestPackageNamespaces.emplace_back("Critic"); auto skipImportVisibility = - StringPropertyAssertion::getValue("skip-package-import-visibility-check-for", assertions); + StringPropertyAssertion::getValue("allow-relaxed-packager-checks-for", assertions); if (skipImportVisibility.has_value()) { - opts->skipPackageImportVisibilityCheckFor.emplace_back(skipImportVisibility.value()); + opts->allowRelaxedPackagerChecksFor.emplace_back(skipImportVisibility.value()); } - opts->secondaryTestPackageNamespaces.emplace_back("Critic"); } opts->disableWatchman = true; opts->rubyfmtPath = "test/testdata/lsp/rubyfmt-stub/rubyfmt"; @@ -607,6 +610,8 @@ TEST_CASE("LSPTest") { sorbetInitOptions->enableTypecheckInfo = true; sorbetInitOptions->highlightUntyped = BooleanPropertyAssertion::getValue("highlight-untyped-values", assertions).value_or(false); + sorbetInitOptions->enableTypedFalseCompletionNudges = + BooleanPropertyAssertion::getValue("enable-typed-false-completion-nudges", assertions).value_or(true); auto initializedResponses = initializeLSP(rootPath, rootUri, *lspWrapper, nextId, true, shouldUseCodeActionResolve, move(sorbetInitOptions)); INFO("Should not receive any response to 'initialized' message."); @@ -795,7 +800,7 @@ TEST_CASE("LSPTest") { // importUsageAssertions. UsageAssertion::check(test.sourceFileContents, *lspWrapper, nextId, symbol, *queryLoc, importUsageAssertions); - } else { + } else if (dynamic_pointer_cast(assertion) == nullptr) { // For a regular UsageAssertion, check that a reference request at this location returns // entryAssertions. UsageAssertion::check(test.sourceFileContents, *lspWrapper, nextId, symbol, *queryLoc, diff --git a/test/patch_require.rb b/test/patch_require.rb deleted file mode 100644 index 6dc14538e3..0000000000 --- a/test/patch_require.rb +++ /dev/null @@ -1,50 +0,0 @@ -# typed: true - -module Kernel - alias sorbet_old_require require - def require(name) - if File.exist?(name + ".rb") - name = name + ".rb" - end - if File.exist?(name) - tmpdir = ENV['llvmir'] - raise "no 'llvmir' in ENV" unless tmpdir - root_name = name - if name.start_with?('./') - root_name = root_name[2..-1] - end - # Our paths are terrible... - root_name = root_name.gsub(%r{.*test/testdata}, 'test/testdata') - - case RUBY_PLATFORM - when "x86_64-linux" - suffix = '.so' - when "x86_64-darwin18", "x86_64-darwin19", "x86_64-darwin20" - suffix = '.bundle' - else - raise "unknown platform: #{RUBY_PLATFORM}" - end - cext = File.join(tmpdir, root_name + suffix) - if File.exist?(cext) - # NOTE: we cross-talk to the payload through this variable. The payload doesn't know what the real path of the - # original file is, so we pass it through here. It's consumed in payload.c:sorbet_allocateRubyStackFrames. - $__sorbet_ruby_realpath = File.realpath(name) - $stderr.puts "SorbetLLVM using compiled: #{cext} for #{$__sorbet_ruby_realpath}" - return sorbet_old_require(cext) - end - if ENV['force_compile'] && File.exist?(name) && File.read(name).include?('# compiled: true') - raise "Missing compiled extension: #{cext}" - end - # $stderr.puts "SorbetLLVM interpreting: #{name}" - end - - sorbet_old_require(name) - end - - def require_relative(feature) - locations = Kernel.caller_locations(1, 2) - file = T.must(T.must(locations)[0]).absolute_path - absolute = File.expand_path(feature, File.dirname(T.must(file))) - require(absolute) - end -end diff --git a/test/pipeline_test.bzl b/test/pipeline_test.bzl index 1e6190b263..c93db51bc9 100644 --- a/test/pipeline_test.bzl +++ b/test/pipeline_test.bzl @@ -13,7 +13,7 @@ def dropExtension(p): return p.partition(".")[0] _TEST_SCRIPT = """#!/usr/bin/env bash -export ASAN_SYMBOLIZER_PATH=`pwd`/external/llvm_toolchain_12_0_0/bin/llvm-symbolizer +export ASAN_SYMBOLIZER_PATH=`pwd`/external/llvm_toolchain_15_0_7/bin/llvm-symbolizer set -x exec {runner} --single_test="{test}" """ @@ -99,10 +99,10 @@ def single_package_rbi_test(name, rb_files): end_to_end_rbi_test( name = name, rb_files = rb_files, - size = "small", - # This is to get the test to run on the compiler build job, - # so we can avoid building ruby on the test-static-sanitized job. - tags = ["compiler"], + # This is to get the test to run on the rbi-gen build job, because I + # can't figure out how to disable the leak sanitizer when running this. + tags = ["manual"], + size = "medium", ) _TEST_RUNNERS = { @@ -114,7 +114,7 @@ _TEST_RUNNERS = { } def pipeline_tests(suite_name, all_paths, test_name_prefix, extra_files = [], tags = []): - tests = {} # test_name-> {"path": String, "prefix": String, "sentinel": String} + tests = {} # test_name-> {"path": String, "prefix": String, "sentinel": String, "isPackage": bool} # The packager step needs folder-based steps since folder structure dictates package membership. # All immediate subdirs of `/packager/` are individual tests. @@ -139,6 +139,7 @@ def pipeline_tests(suite_name, all_paths, test_name_prefix, extra_files = [], ta "isMultiFile": False, "isDirectory": True, "disabled": "disabled" in test_name, + "isPackage": True, } tests[test_name] = data continue @@ -157,6 +158,7 @@ def pipeline_tests(suite_name, all_paths, test_name_prefix, extra_files = [], ta "isMultiFile": "__" in path, "isDirectory": False, "disabled": "disabled" in path, + "isPackage": False, } tests[test_name] = data @@ -165,6 +167,7 @@ def pipeline_tests(suite_name, all_paths, test_name_prefix, extra_files = [], ta fail(msg = "Unknown pipeline test type: {}".format(test_name_prefix)) enabled_tests = [] + enabled_packager_tests = [] disabled_tests = [] for name in tests.keys(): test_name = "test_{}/{}".format(test_name_prefix, name) @@ -179,6 +182,8 @@ def pipeline_tests(suite_name, all_paths, test_name_prefix, extra_files = [], ta disabled_tests.append(test_name) else: enabled_tests.append(test_name) + if tests[name]["isPackage"]: + enabled_packager_tests.append(test_name) data = [] data += extra_files @@ -190,6 +195,7 @@ def pipeline_tests(suite_name, all_paths, test_name_prefix, extra_files = [], ta data += [sentinel] data += native.glob(["{}.*.exp".format(prefix)]) data += native.glob(["{}.*.rbupdate".format(prefix)]) + data += native.glob(["{}.*.rbiupdate".format(prefix)]) data += native.glob(["{}.*.rbedited".format(prefix)]) data += native.glob(["{}.*.minimize.rbi".format(prefix)]) @@ -207,6 +213,12 @@ def pipeline_tests(suite_name, all_paths, test_name_prefix, extra_files = [], ta tests = enabled_tests, ) + if len(enabled_packager_tests) > 0: + native.test_suite( + name = "{}_packager".format(suite_name), + tests = enabled_packager_tests, + ) + if len(disabled_tests) > 0: native.test_suite( name = "{}_disabled".format(suite_name), diff --git a/test/pipeline_test_runner.cc b/test/pipeline_test_runner.cc index 16bb7c5d0c..8110b767b8 100644 --- a/test/pipeline_test_runner.cc +++ b/test/pipeline_test_runner.cc @@ -33,6 +33,7 @@ #include "main/autogen/data/definitions.h" #include "main/autogen/data/version.h" #include "main/minimize/minimize.h" +#include "main/pipeline/pipeline.h" #include "namer/namer.h" #include "packager/packager.h" #include "packager/rbi_gen.h" @@ -59,16 +60,18 @@ using namespace std; string singleTest; +constexpr string_view whitelistedTypedNoneTest = "missing_typed_sigil.rb"sv; +constexpr string_view packageFileName = "__package.rb"sv; + class CFGCollectorAndTyper { public: vector> cfgs; void preTransformMethodDef(core::Context ctx, ast::ExpressionPtr &tree) { auto &m = ast::cast_tree_nonnull(tree); - - if (m.symbol.data(ctx)->flags.isOverloaded || - (m.symbol.data(ctx)->flags.isAbstract && ctx.file.data(ctx).compiledLevel != core::CompiledLevel::True)) { + if (!infer::Inference::willRun(ctx, m.declLoc, m.symbol)) { return; } + auto cfg = cfg::CFGBuilder::buildFor(ctx.withOwner(m.symbol), m); auto symbol = cfg->symbol; cfg = infer::Inference::run(ctx.withOwner(symbol), move(cfg)); @@ -176,10 +179,138 @@ class ExpectationHandler { void clear(const core::GlobalState &gs) { got.clear(); errorQueue->flushAllErrors(gs); - errorCollector->drainErrors(); + auto _newErrors = errorCollector->drainErrors(); } }; +vector index(unique_ptr &gs, absl::Span files, + ExpectationHandler &handler, Expectations &test) { + vector trees; + for (auto file : files) { + auto fileName = FileOps::getFileName(file.data(*gs).path()); + if (fileName != whitelistedTypedNoneTest && (fileName != packageFileName || !file.data(*gs).source().empty()) && + file.data(*gs).source().find("# typed:") == string::npos) { + ADD_FAIL_CHECK_AT(file.data(*gs).path().data(), 1, "Add a `# typed: strict` line to the top of this file"); + } + + // if a file is typed: ignore, then we shouldn't try to parse + // it or do anything with it at all + if (file.data(*gs).strictLevel == core::StrictLevel::Ignore) { + ast::ParsedFile pf{ast::make_expression(), file}; + trees.emplace_back(move(pf)); + continue; + } + + unique_ptr nodes; + { + core::UnfreezeNameTable nameTableAccess(*gs); // enters original strings + + auto settings = parser::Parser::Settings{}; + nodes = parser::Parser::run(*gs, file, settings); + } + + handler.drainErrors(*gs); + handler.addObserved(*gs, "parse-tree", [&]() { return nodes->toString(*gs); }); + handler.addObserved(*gs, "parse-tree-whitequark", [&]() { return nodes->toWhitequark(*gs); }); + handler.addObserved(*gs, "parse-tree-json", [&]() { return nodes->toJSON(*gs); }); + + // Desugarer + ast::ParsedFile desugared; + { + core::UnfreezeNameTable nameTableAccess(*gs); // enters original strings + + core::MutableContext ctx(*gs, core::Symbols::root(), file); + desugared = testSerialize(*gs, ast::ParsedFile{ast::desugar::node2Tree(ctx, move(nodes)), file}); + } + + handler.addObserved(*gs, "desugar-tree", [&]() { return desugared.tree.toString(*gs); }); + handler.addObserved(*gs, "desugar-tree-raw", [&]() { return desugared.tree.showRaw(*gs); }); + + ast::ParsedFile localNamed; + + // Rewriter + ast::ParsedFile rewritten; + { + core::UnfreezeNameTable nameTableAccess(*gs); // enters original strings + + core::MutableContext ctx(*gs, core::Symbols::root(), desugared.file); + bool previous = gs->runningUnderAutogen; + gs->runningUnderAutogen = test.expectations.contains("autogen"); + rewritten = + testSerialize(*gs, ast::ParsedFile{rewriter::Rewriter::run(ctx, move(desugared.tree)), desugared.file}); + gs->runningUnderAutogen = previous; + } + + handler.addObserved(*gs, "rewrite-tree", [&]() { return rewritten.tree.toString(*gs); }); + handler.addObserved(*gs, "rewrite-tree-raw", [&]() { return rewritten.tree.showRaw(*gs); }); + + core::MutableContext ctx(*gs, core::Symbols::root(), desugared.file); + localNamed = testSerialize(*gs, local_vars::LocalVars::run(ctx, move(rewritten))); + + handler.addObserved(*gs, "index-tree", [&]() { return localNamed.tree.toString(*gs); }); + handler.addObserved(*gs, "index-tree-raw", [&]() { return localNamed.tree.showRaw(*gs); }); + + trees.emplace_back(move(localNamed)); + } + + return trees; +} + +void setupPackager(unique_ptr &gs, vector> &assertions) { + vector extraPackageFilesDirectoryUnderscorePrefixes; + vector extraPackageFilesDirectorySlashPrefixes; + vector skipRBIExportEnforcementDirs; + vector allowRelaxedPackagerChecksFor; + + auto extraDirUnderscore = + StringPropertyAssertion::getValue("extra-package-files-directory-prefix-underscore", assertions); + if (extraDirUnderscore.has_value()) { + extraPackageFilesDirectoryUnderscorePrefixes.emplace_back(extraDirUnderscore.value()); + } + + auto extraDirSlash = StringPropertyAssertion::getValue("extra-package-files-directory-prefix-slash", assertions); + if (extraDirSlash.has_value()) { + extraPackageFilesDirectorySlashPrefixes.emplace_back(extraDirSlash.value()); + } + + auto allowRelaxedPackager = StringPropertyAssertion::getValue("allow-relaxed-packager-checks-for", assertions); + if (allowRelaxedPackager.has_value()) { + allowRelaxedPackagerChecksFor.emplace_back(allowRelaxedPackager.value()); + } + + { + core::UnfreezeNameTable packageNS(*gs); + core::packages::UnfreezePackages unfreezeToEnterPackagerOptionsPackageDB = gs->unfreezePackages(); + gs->setPackagerOptions(extraPackageFilesDirectoryUnderscorePrefixes, extraPackageFilesDirectorySlashPrefixes, + {}, allowRelaxedPackagerChecksFor, "PACKAGE_ERROR_HINT"); + } +} + +void package(unique_ptr &gs, unique_ptr &workers, absl::Span trees, + ExpectationHandler &handler, vector> &assertions) { + auto enablePackager = BooleanPropertyAssertion::getValue("enable-packager", assertions).value_or(false); + + if (!enablePackager) { + return; + } + + // Packager runs over all trees. + packager::Packager::run(*gs, *workers, trees); + for (auto &tree : trees) { + handler.addObserved(*gs, "package-tree", [&]() { + return fmt::format("# -- {} --\n{}", tree.file.data(*gs).path(), tree.tree.toString(*gs)); + }); + } +} + +void name(core::GlobalState &gs, absl::Span trees, WorkerPool &workers) { + core::UnfreezeNameTable nameTableAccess(gs); // creates singletons and class names + core::UnfreezeSymbolTable symbolTableAccess(gs); // enters symbols + auto foundHashes = nullptr; + auto canceled = namer::Namer::run(gs, trees, workers, foundHashes); + ENFORCE(!canceled); +} + TEST_CASE("PerPhaseTest") { // NOLINT Expectations test = Expectations::getExpectations(singleTest); @@ -210,6 +341,8 @@ TEST_CASE("PerPhaseTest") { // NOLINT BooleanPropertyAssertion::getValue("enable-experimental-requires-ancestor", assertions).value_or(false); gs->ruby3KeywordArgs = BooleanPropertyAssertion::getValue("experimental-ruby3-keyword-args", assertions).value_or(false); + gs->typedSuper = BooleanPropertyAssertion::getValue("typed-super", assertions).value_or(true); + // TODO(jez) Allow allow suppressPayloadSuperclassRedefinitionFor in a testdata test assertion? if (!BooleanPropertyAssertion::getValue("stripe-mode", assertions).value_or(false)) { gs->suppressErrorClass(core::errors::Namer::MultipleBehaviorDefs.code); @@ -236,10 +369,8 @@ TEST_CASE("PerPhaseTest") { // NOLINT emptyGs = gs->deepCopy(); } - // Parser + // Read files vector files; - constexpr string_view whitelistedTypedNoneTest = "missing_typed_sigil.rb"sv; - constexpr string_view packageFileName = "__package.rb"sv; { core::UnfreezeFileTable fileTableAccess(*gs); @@ -254,119 +385,32 @@ TEST_CASE("PerPhaseTest") { // NOLINT files.emplace_back(fref); } } - vector trees; - ExpectationHandler handler(test, errorQueue, errorCollector); - - for (auto file : files) { - auto fileName = FileOps::getFileName(file.data(*gs).path()); - if (fileName != whitelistedTypedNoneTest && (fileName != packageFileName || !file.data(*gs).source().empty()) && - file.data(*gs).source().find("# typed:") == string::npos) { - ADD_FAIL_CHECK_AT(file.data(*gs).path().data(), 1, "Add a `# typed: strict` line to the top of this file"); - } - - // if a file is typed: ignore, then we shouldn't try to parse - // it or do anything with it at all - if (file.data(*gs).strictLevel == core::StrictLevel::Ignore) { - ast::ParsedFile pf{ast::make_expression(), file}; - trees.emplace_back(move(pf)); - continue; - } - - unique_ptr nodes; - { - core::UnfreezeNameTable nameTableAccess(*gs); // enters original strings - - auto settings = parser::Parser::Settings{}; - nodes = parser::Parser::run(*gs, file, settings); - } - - handler.drainErrors(*gs); - handler.addObserved(*gs, "parse-tree", [&]() { return nodes->toString(*gs); }); - handler.addObserved(*gs, "parse-tree-whitequark", [&]() { return nodes->toWhitequark(*gs); }); - handler.addObserved(*gs, "parse-tree-json", [&]() { return nodes->toJSON(*gs); }); - - // Desugarer - ast::ParsedFile desugared; - { - core::UnfreezeNameTable nameTableAccess(*gs); // enters original strings - - core::MutableContext ctx(*gs, core::Symbols::root(), file); - desugared = testSerialize(*gs, ast::ParsedFile{ast::desugar::node2Tree(ctx, move(nodes)), file}); - } - - handler.addObserved(*gs, "desugar-tree", [&]() { return desugared.tree.toString(*gs); }); - handler.addObserved(*gs, "desugar-tree-raw", [&]() { return desugared.tree.showRaw(*gs); }); - - ast::ParsedFile localNamed; - - // Rewriter - ast::ParsedFile rewriten; - { - core::UnfreezeNameTable nameTableAccess(*gs); // enters original strings - - core::MutableContext ctx(*gs, core::Symbols::root(), desugared.file); - bool previous = gs->runningUnderAutogen; - gs->runningUnderAutogen = test.expectations.contains("autogen"); - rewriten = - testSerialize(*gs, ast::ParsedFile{rewriter::Rewriter::run(ctx, move(desugared.tree)), desugared.file}); - gs->runningUnderAutogen = previous; - } - - handler.addObserved(*gs, "rewrite-tree", [&]() { return rewriten.tree.toString(*gs); }); - handler.addObserved(*gs, "rewrite-tree-raw", [&]() { return rewriten.tree.showRaw(*gs); }); - - core::MutableContext ctx(*gs, core::Symbols::root(), desugared.file); - localNamed = testSerialize(*gs, local_vars::LocalVars::run(ctx, move(rewriten))); - - handler.addObserved(*gs, "index-tree", [&]() { return localNamed.tree.toString(*gs); }); - handler.addObserved(*gs, "index-tree-raw", [&]() { return localNamed.tree.showRaw(*gs); }); - - trees.emplace_back(move(localNamed)); - } + ExpectationHandler handler(test, errorQueue, errorCollector); auto enablePackager = BooleanPropertyAssertion::getValue("enable-packager", assertions).value_or(false); + vector trees; + auto filesSpan = absl::Span(files); if (enablePackager) { - vector extraPackageFilesDirectoryUnderscorePrefixes; - vector extraPackageFilesDirectorySlashPrefixes; - vector secondaryTestPackageNamespaces = {"Critic"}; - vector skipRBIExportEnforcementDirs; - vector skipImportVisibilityCheckFor; - - auto extraDirUnderscore = - StringPropertyAssertion::getValue("extra-package-files-directory-prefix-underscore", assertions); - if (extraDirUnderscore.has_value()) { - extraPackageFilesDirectoryUnderscorePrefixes.emplace_back(extraDirUnderscore.value()); - } + setupPackager(gs, assertions); - auto extraDirSlash = - StringPropertyAssertion::getValue("extra-package-files-directory-prefix-slash", assertions); - if (extraDirSlash.has_value()) { - extraPackageFilesDirectorySlashPrefixes.emplace_back(extraDirSlash.value()); - } + auto numPackageFiles = realmain::pipeline::partitionPackageFiles(*gs, filesSpan); + auto inputPackageFiles = filesSpan.first(numPackageFiles); + filesSpan = filesSpan.subspan(numPackageFiles); - auto skipImportVisibility = - StringPropertyAssertion::getValue("skip-package-import-visibility-check-for", assertions); - if (skipImportVisibility.has_value()) { - skipImportVisibilityCheckFor.emplace_back(skipImportVisibility.value()); - } + trees = index(gs, inputPackageFiles, handler, test); - { - core::UnfreezeNameTable packageNS(*gs); - core::packages::UnfreezePackages unfreezeToEnterPackagerOptionsPackageDB = gs->unfreezePackages(); - gs->setPackagerOptions(secondaryTestPackageNamespaces, extraPackageFilesDirectoryUnderscorePrefixes, - extraPackageFilesDirectorySlashPrefixes, {}, skipImportVisibilityCheckFor, - "PACKAGE_ERROR_HINT"); - } + // First run: only the __package.rb files. This populates the packageDB + package(gs, workers, absl::Span(trees), handler, assertions); + name(*gs, absl::Span(trees), *workers); + } - // Packager runs over all trees. - trees = packager::Packager::run(*gs, *workers, move(trees)); - for (auto &tree : trees) { - handler.addObserved(*gs, "package-tree", [&]() { - return fmt::format("# -- {} --\n{}", tree.file.data(*gs).path(), tree.tree.toString(*gs)); - }); - } + auto nonPackageTrees = index(gs, filesSpan, handler, test); + package(gs, workers, absl::Span(nonPackageTrees), handler, assertions); + name(*gs, absl::Span(nonPackageTrees), *workers); + realmain::pipeline::unpartitionPackageFiles(trees, move(nonPackageTrees)); + if (enablePackager) { if (test.expectations.contains("rbi-gen")) { auto rbiGenGs = emptyGs->deepCopy(); rbiGenGs->errorQueue = make_shared(*logger, *logger, errorCollector); @@ -403,8 +447,7 @@ TEST_CASE("PerPhaseTest") { // NOLINT } // Initialize the package DB - packageTrees = packager::Packager::findPackages(*rbiGenGs, *workers, move(packageTrees)); - + packager::Packager::findPackages(*rbiGenGs, absl::Span(packageTrees)); packager::Packager::setPackageNameOnFiles(*rbiGenGs, packageTrees); packager::Packager::setPackageNameOnFiles(*rbiGenGs, trees); @@ -413,7 +456,8 @@ TEST_CASE("PerPhaseTest") { // NOLINT core::UnfreezeNameTable nameTableAccess(*rbiGenGs); // creates singletons and class names core::UnfreezeSymbolTable symbolTableAccess(*rbiGenGs); // enters symbols auto foundHashes = nullptr; - trees = move(namer::Namer::run(*rbiGenGs, move(trees), *workers, foundHashes).result()); + auto canceled = namer::Namer::run(*rbiGenGs, absl::Span(trees), *workers, foundHashes); + ENFORCE(!canceled); } // Resolver @@ -442,13 +486,6 @@ TEST_CASE("PerPhaseTest") { // NOLINT } } - { - core::UnfreezeNameTable nameTableAccess(*gs); // creates singletons and class names - core::UnfreezeSymbolTable symbolTableAccess(*gs); // enters symbols - auto foundHashes = nullptr; - trees = move(namer::Namer::run(*gs, move(trees), *workers, foundHashes).result()); - } - for (auto &tree : trees) { // Namer auto namedTree = testSerialize(*gs, move(tree)); @@ -698,7 +735,7 @@ TEST_CASE("PerPhaseTest") { // NOLINT // Allow later phases to have errors that we didn't test for errorQueue->flushAllErrors(*gs); - errorCollector->drainErrors(); + { auto _ = errorCollector->drainErrors(); } // now we test the incremental resolver @@ -752,6 +789,7 @@ TEST_CASE("PerPhaseTest") { // NOLINT fast_sort(trees, [](auto &lhs, auto &rhs) { return lhs.file < rhs.file; }); if (enablePackager) { + absl::c_stable_partition(trees, [&](const auto &pf) { return pf.file.isPackage(*gs); }); trees = packager::Packager::runIncremental(*gs, move(trees)); for (auto &tree : trees) { handler.addObserved(*gs, "package-tree", [&]() { @@ -760,7 +798,7 @@ TEST_CASE("PerPhaseTest") { // NOLINT } } - bool ranIncremantalNamer = false; + bool ranIncrementalNamer = false; { // namer for (auto &tree : trees) { @@ -774,8 +812,9 @@ TEST_CASE("PerPhaseTest") { // NOLINT // Here, to complement those tests, we just run Namer::run (not Namer::runIncremental) // to stress the codepath where Namer is not tasked with deleting anything when run for // the fast path. - ENFORCE(!ranIncremantalNamer); - vTmp = move(namer::Namer::run(*gs, move(vTmp), *workers, &foundHashes).result()); + ENFORCE(!ranIncrementalNamer); + auto canceled = namer::Namer::run(*gs, absl::Span(vTmp), *workers, &foundHashes); + ENFORCE(!canceled); tree = testSerialize(*gs, move(vTmp[0])); handler.addObserved(*gs, "name-tree", [&]() { return tree.tree.toString(*gs); }); @@ -784,7 +823,11 @@ TEST_CASE("PerPhaseTest") { // NOLINT } // resolver - trees = move(resolver::Resolver::runIncremental(*gs, move(trees), ranIncremantalNamer).result()); + { + core::UnfreezeNameTable nameTableAccess(*gs); + core::UnfreezeSymbolTable symbolTableAccess(*gs); + trees = move(resolver::Resolver::runIncremental(*gs, move(trees), ranIncrementalNamer, *workers).result()); + } if (enablePackager) { trees = packager::VisibilityChecker::run(*gs, *workers, move(trees)); @@ -799,7 +842,7 @@ TEST_CASE("PerPhaseTest") { // NOLINT // and drain all the remaining errors errorQueue->flushAllErrors(*gs); - errorCollector->drainErrors(); + { auto _ = errorCollector->drainErrors(); } { INFO("the incremental resolver should not add new symbols"); diff --git a/test/pkg_autocorrects_test.cc b/test/pkg_autocorrects_test.cc index 579fcb4756..661c661590 100644 --- a/test/pkg_autocorrects_test.cc +++ b/test/pkg_autocorrects_test.cc @@ -62,7 +62,7 @@ struct TestPackageFile { { // and then finally the packager! auto workers = WorkerPool::create(0, gs.tracer()); - parsedFiles = packager::Packager::run(gs, *workers, move(parsedFiles)); + packager::Packager::run(gs, *workers, absl::Span(parsedFiles)); } TestPackageFile pkgFile(move(parsedFiles.front()), move(parsedFiles[1])); diff --git a/test/rbi_gen_package_runner.cc b/test/rbi_gen_package_runner.cc new file mode 100644 index 0000000000..484db4884a --- /dev/null +++ b/test/rbi_gen_package_runner.cc @@ -0,0 +1,328 @@ +#include "absl/strings/match.h" +#include "absl/strings/str_replace.h" +#include "common/Subprocess.h" +#include "common/common.h" +#include "rapidjson/document.h" +#include "rapidjson/istreamwrapper.h" +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +namespace { + +using defer = shared_ptr; + +vector fromJsonStringArray(const rapidjson::Value &elems) { + vector result; + for (const auto &elem : elems.GetArray()) { + result.emplace_back(elem.GetString()); + } + + return result; +} + +rapidjson::Document parseJsonFile(string filename) { + ifstream ifs(filename); + rapidjson::IStreamWrapper isw(ifs); + rapidjson::Document document; + document.ParseStream(isw); + if (document.HasParseError()) { + throw new runtime_error("Failed to parse document: " + filename); + } + + return document; +} + +struct PackageInfo { + string name; + vector imports; + vector testImports; + vector files; + vector testFiles; + string mangledName; + + PackageInfo() = default; + PackageInfo(string name, vector imports, vector testImports, vector files, + vector testFiles) + : name(name), imports(imports), testImports(testImports), files(files), testFiles(testFiles), + mangledName(absl::StrReplaceAll(name, {{"::", "_"}}) + "_Package"s) {} + + static PackageInfo fromJson(const rapidjson::Value &d) { + return PackageInfo(d["name"].GetString(), fromJsonStringArray(d["imports"]), + fromJsonStringArray(d["testImports"]), fromJsonStringArray(d["files"]), + fromJsonStringArray(d["testFiles"])); + } +}; + +struct DependencyInfo { + vector packageRefs; + vector rbiRefs; + + DependencyInfo() = default; + DependencyInfo(vector packageRefs, vector rbiRefs) : packageRefs(packageRefs), rbiRefs(rbiRefs) {} + + static DependencyInfo load(string file) { + auto d = parseJsonFile(file); + return DependencyInfo(fromJsonStringArray(d["packageRefs"]), fromJsonStringArray(d["rbiRefs"])); + } +}; + +absl::flat_hash_map packageInfoToHash(vector packageInfo) { + absl::flat_hash_map result; + for (auto &info : packageInfo) { + result[info.name] = info; + } + + return result; +} + +struct PackageDB { + absl::flat_hash_map rbis; + absl::flat_hash_map deps; + absl::flat_hash_map info; + + PackageDB(string packageDir, vector packageInfo) + : rbis({}), deps({}), info(packageInfoToHash(packageInfo)) { + for (const auto &fullEntry : filesystem::directory_iterator(filesystem::path(packageDir))) { + const auto &entry = fullEntry.path().filename(); + if (entry == "." || entry == "..") { + continue; + } + + auto filepath = entry.native(); + if (absl::EndsWith(filepath, ".deps.json")) { + deps[filepath.substr(0, filepath.size() - 10)] = DependencyInfo::load(packageDir + "/" + filepath); + } else if (absl::EndsWith(filepath, ".package.rbi")) { + rbis[filepath.substr(0, filepath.size() - 12)] = filepath; + } else { + throw new runtime_error("Unknown filename in package directory " + filepath); + } + } + } + + string lookupRbiFor(PackageInfo info) { + return rbis.at(info.mangledName); + } + + DependencyInfo lookupDepsFor(PackageInfo info) { + return deps.at(info.mangledName); + } + + PackageInfo lookupPackageFor(string name) { + return info.at(name); + } +}; + +string copyFromOwnedCharStar(char *str) { + string result(str); + free(str); + return result; +} + +string mktempTemplate(string basename) { + string packageInfoFilename = basename + ".XXXXXXXXXX"; + if (const char *envTmpdir = getenv("TMPDIR")) { + packageInfoFilename = string(envTmpdir) + "/" + packageInfoFilename; + } else { + packageInfoFilename = "/tmp/" + packageInfoFilename; + } + + return packageInfoFilename; +} + +void chDirAndSpawn(string executable, vector arguments, optional stdinContents, + optional chdir) { + auto pwd = filesystem::current_path(); + defer _(nullptr, [&](...) { filesystem::current_path(pwd); }); + if (chdir.has_value()) { + filesystem::path chdirPath = chdir.value(); + filesystem::current_path(chdirPath); + } + auto result = sorbet::Subprocess::spawn(move(executable), move(arguments), nullopt); + + if (!result.has_value() || result->status != 0) { + throw runtime_error("Subprocess failed"); + } +} + +vector loadPackageInfo(string packageInfoFilename) { + auto document = parseJsonFile(packageInfoFilename); + + vector result; + for (const auto &package : document.GetArray()) { + result.emplace_back(PackageInfo::fromJson(package)); + } + + return result; +} + +void verifySinglePackageTypechecking(string sorbet, string root, string testDirectory, + absl::flat_hash_set requiredFiles, string rbiPackageDir, + absl::flat_hash_set rbiFiles) { + string tmpdirFilename = mktempTemplate("tmp"); + auto tmpdir = mkdtemp(tmpdirFilename.data()); + filesystem::path tmpdirPath = tmpdir; + + absl::flat_hash_set dirnames; + for (const auto &f : requiredFiles) { + dirnames.emplace(filesystem::path(f).parent_path()); + } + for (const auto &dirname : dirnames) { + filesystem::path target = tmpdirPath / filesystem::path(dirname); + filesystem::create_directories(target); + } + + auto pwd = filesystem::current_path(); + filesystem::current_path(filesystem::path(root)); + for (const auto &f : requiredFiles) { + auto fPath = filesystem::path(f); + auto dirname = fPath.parent_path(); + // For some reason, I couldn't get the filesystem::copy_file call to work, and I think it + // has something to do with bazel sandboxing. I didn't look into it further. + chDirAndSpawn("cp", + { + fPath.native(), + (tmpdirPath / dirname).native(), + }, + nullopt, nullopt); + } + filesystem::current_path(pwd); + + for (const auto &rbi : rbiFiles) { + auto source = filesystem::path(rbiPackageDir) / filesystem::path(rbi); + chDirAndSpawn("cp", + { + source.native(), + (filesystem::path(tmpdirPath) / filesystem::path(testDirectory)).native(), + }, + nullopt, nullopt); + } + + chDirAndSpawn(sorbet, + { + "--silence-dev-message", + "--stripe-mode", + "--ignore", + "__package.rb", + testDirectory, + }, + nullopt, tmpdir); +} + +} // namespace + +int main(int argc, char **argv) { + if (argc != 4) { + return 1; + } + + auto sorbet = copyFromOwnedCharStar(realpath(argv[1], nullptr)); + auto root = copyFromOwnedCharStar(realpath(argv[2], nullptr)); + auto testDirectory = string(argv[3]); + + vector packageInfo; + vector packageDirectories; + { + string packageInfoFilename = mktempTemplate("package_info"); + auto packageInfoFd = mkstemp(packageInfoFilename.data()); + if (packageInfoFd == -1) { + return 1; + } + defer _(nullptr, [&](...) { unlink(packageInfoFilename.c_str()); }); + close(packageInfoFd); + + chDirAndSpawn(sorbet, + { + "--silence-dev-message"s, + "--stripe-mode"s, + "--stripe-packages"s, + "--dump-package-info"s, + packageInfoFilename, + testDirectory, + }, + nullopt, root); + + packageInfo = loadPackageInfo(packageInfoFilename); + for (const auto &package : packageInfo) { + absl::flat_hash_set dirs; + for (const auto &f : package.files) { + dirs.emplace(filesystem::path(f).parent_path()); + } + + if (dirs.size() != 1) { + throw runtime_error("Package must be limited to a single directory"); + } + + packageDirectories.emplace_back(*dirs.begin()); + } + } + + { + string rbiPackageDirFilename = mktempTemplate("tmp"); + auto rbiPackageDir = mkdtemp(rbiPackageDirFilename.data()); + chDirAndSpawn(sorbet, + { + "--silence-dev-message", + "--stripe-mode", + "--package-rbi-generation", + "--package-rbi-dir", + rbiPackageDir, + "--ignore", + "__package.rb", + testDirectory, + }, + nullopt, root); + + PackageDB db(rbiPackageDir, packageInfo); + + for (const auto &info : packageInfo) { + absl::flat_hash_set requiredFiles; + absl::flat_hash_set rbiFiles; + absl::flat_hash_set visited; + deque worklist; + + for (const auto &file : info.files) { + requiredFiles.emplace(file); + } + for (const auto &file : info.testFiles) { + requiredFiles.emplace(file); + } + visited.emplace(info.name); + for (const auto &import_ : info.imports) { + worklist.emplace_back(import_); + } + for (const auto &import_ : info.testImports) { + worklist.emplace_back(import_); + } + + while (!worklist.empty()) { + auto pkg = worklist.front(); + worklist.pop_front(); + if (visited.count(pkg) == 1) { + continue; + } + + visited.emplace(pkg); + auto infoForDependency = db.lookupPackageFor(pkg); + rbiFiles.emplace(db.lookupRbiFor(infoForDependency)); + for (const auto &import_ : infoForDependency.imports) { + worklist.emplace_back(import_); + } + for (const auto &import_ : infoForDependency.testImports) { + worklist.emplace_back(import_); + } + } + + verifySinglePackageTypechecking(sorbet, root, testDirectory, requiredFiles, rbiPackageDir, rbiFiles); + } + } + + return 0; +} diff --git a/test/rbi_gen_package_runner.rb b/test/rbi_gen_package_runner.rb deleted file mode 100644 index 01a0840b5e..0000000000 --- a/test/rbi_gen_package_runner.rb +++ /dev/null @@ -1,282 +0,0 @@ -# frozen_string_literal: true -# typed: true - -require 'json' -require 'optparse' -require 'set' -require 'tempfile' - -require 'sorbet-runtime' - -class Module - include T::Sig -end - -class PackageInfo - sig {returns(String)} - attr_reader :name - - sig {returns(T::Array[String])} - attr_reader :imports - - sig {returns(T::Array[String])} - attr_reader :testImports - - sig {returns(T::Array[String])} - attr_reader :files - - sig {returns(T::Array[String])} - attr_reader :testFiles - - sig {returns(String)} - attr_reader :mangled_name - - sig {params(hash: T::Hash[T.untyped, T.untyped]).returns(PackageInfo)} - def self.from_hash(hash) - self.new(hash.fetch("name"), - hash.fetch("imports"), - hash.fetch("testImports"), - hash.fetch("files"), - hash.fetch("testFiles")) - end - - private_class_method :new - - sig {params( - name: String, - imports: T::Array[String], - testImports: T::Array[String], - files: T::Array[String], - testFiles: T::Array[String]).void} - def initialize(name, imports, testImports, files, testFiles) - @name = name - @imports = imports - @testImports = testImports - @files = files - @testFiles = testFiles - - @mangled_name = T.let("#{name.gsub('::', '_')}_Package", String) - end -end - -class DependencyInfo < T::Struct - const :packageRefs, T::Array[String] - const :rbiRefs, T::Array[String] - - def self.load(file) - from_hash(JSON.parse(File.read(file))) - end -end - -class PackageDB - sig {returns(T::Hash[String, String])} - attr_reader :rbis - - sig {returns(T::Hash[String, DependencyInfo])} - attr_reader :deps - - sig {returns(T::Hash[String, PackageInfo])} - attr_reader :info - - sig {params(package_dir: String, package_info: T::Array[PackageInfo]).void} - def initialize(package_dir, package_info) - @rbis = T.let({}, T::Hash[String, String]) - @deps = T.let({}, T::Hash[String, DependencyInfo]) - @info = T.let(package_info.to_h {|info| [info.name, info]}, T::Hash[String, PackageInfo]) - - Dir.new(package_dir).each do |entry| - next if entry == '.' or entry == '..' - - case - when entry =~ /([^.]+).deps.json$/ - @deps[$1] = DependencyInfo.load(File.join(package_dir, entry)) - when entry =~ /([^.]+).package.rbi$/ - @rbis[$1] = entry - else - raise "Unknown filename in package directory #{entry}" - end - end - end - - sig {params(info: PackageInfo).returns(String)} - def lookup_rbi_for(info) - rbis.fetch(info.mangled_name) - end - - sig {params(info: PackageInfo).returns(DependencyInfo)} - def lookup_deps_for(info) - deps.fetch(info.mangled_name) - end - - sig {params(name: String).returns(PackageInfo)} - def lookup_package_for(name) - info.fetch(name) - end -end - -module Runner - include T::Sig - - sig {params(command: T::Array[String], chdir: T.nilable(String)).void} - def self.check_call(command, chdir: nil) - opts = {} - if chdir - opts[:chdir] = chdir - end - pid = T.unsafe(Process).spawn({}, *command, opts) - Process.wait(pid) - status = $? - if status.to_i != 0 - raise "#{command.join(" ")} exited with #{status.to_i}" - end - end - - sig {params(package_info_file: Tempfile).returns(T::Array[PackageInfo])} - def self.load_package_info(package_info_file) - json = JSON.parse(File.read(package_info_file)) - - json.map do |package| - p = PackageInfo.from_hash(package) - end - end - - sig {params(sorbet: String, root: String, test_directory: String, - required_files: T::Set[String], rbi_package_dir: String, - rbi_files: T::Set[String]).void} - def self.verify_single_package_typechecking( - sorbet, root, test_directory, required_files, - rbi_package_dir, rbi_files) - Dir.mktmpdir do |tmpdir| - # Copy the package's Ruby files to the tmpdir, mirroring the directory - # structure. - dirnames = required_files.map {|f| File.dirname(f)}.uniq - dirnames.each do |dirname| - target = File.join(tmpdir, dirname) - FileUtils.mkdir_p(target) - end - - Dir.chdir(root) do |root| - required_files.each do |f| - dirname = File.dirname(f) - target = File.join(tmpdir, dirname) - FileUtils.cp(f, target) - end - end - - # Copy the RBI files into the root. - rbi_files.each do |rbi| - source = File.join(rbi_package_dir, rbi) - FileUtils.cp(source, File.join(tmpdir, test_directory)) - end - - check_call([ - sorbet, - '--silence-dev-message', - '--stripe-mode', - '--ignore', - '__package.rb', - test_directory - ], - chdir: tmpdir) - end - end - - sig {params(sorbet: String, root: String, test_directory: String).void} - def self.main(sorbet:, root:, test_directory:) - Tempfile.open('package_info') do |package_info_file| - check_call([ - sorbet, - '--silence-dev-message', - '--stripe-mode', - '--stripe-packages', - '--dump-package-info', - T.must(package_info_file.path), - test_directory - ], chdir: root) - - package_info = load_package_info(package_info_file) - - package_directories = package_info.map do |package| - dirs = package.files.map {|f| File.dirname(f)}.uniq - if dirs.size != 1 - raise "Package #{package.name} must be limited to a single directory" - end - - dirs[0] - end - - Dir.mktmpdir do |rbi_package_dir| - check_call([ - sorbet, - '--silence-dev-message', - '--stripe-mode', - '--package-rbi-generation', - '--package-rbi-dir', - rbi_package_dir, - '--ignore', - '__package.rb', - test_directory], - chdir: root) - - db = PackageDB.new(rbi_package_dir, package_info) - - package_info.map do |info| - required_files = T.let(Set.new, T::Set[String]) - rbi_files = T.let(Set.new, T::Set[String]) - visited = T.let(Set.new, T::Set[String]) - worklist = T.let([], T::Array[String]) - - # We always need the rb files from the package itself, but we should only - # need the RBI files from any transitive imports. - required_files.merge(info.files) - required_files.merge(info.testFiles) - visited.add(info.name) - worklist.append(*info.imports) - worklist.append(*info.testImports) - - while !worklist.empty? - pkg = T.must(worklist.pop) - next if visited.include?(pkg) - - visited.add(pkg) - info_for_dependency = db.lookup_package_for(pkg) - - rbi_files.add(db.lookup_rbi_for(info_for_dependency)) - - worklist.append(*info_for_dependency.imports) - worklist.append(*info_for_dependency.testImports) - end - - verify_single_package_typechecking(sorbet, root, test_directory, required_files, - rbi_package_dir, rbi_files) - end - end - end - end -end - -if __FILE__ == $0 - sorbet = T.let(nil, T.nilable(String)) - root = T.let(nil, T.nilable(String)) - test_directory = T.let(nil, T.nilable(String)) - - OptionParser.new do |opts| - opts.on '--sorbet=PATH', 'Path to the Sorbet executable' do |path| - sorbet = File.realpath(path) - end - - opts.on '--root=PATH', 'Path to the root of the Sorbet checkout' do |path| - root = File.realpath(path) - end - - opts.on '--test-directory=PATH', 'Relative path to the root of the checked directory' do |path| - test_directory = path - end - end.parse! - - Runner.main( - sorbet: T.must(sorbet), - root: T.must(root), - test_directory: T.must(test_directory) - ) -end diff --git a/test/run_compiled.sh b/test/run_compiled.sh deleted file mode 100755 index aa87c94572..0000000000 --- a/test/run_compiled.sh +++ /dev/null @@ -1,127 +0,0 @@ -#!/bin/bash - -set -euo pipefail - -pushd "$(dirname "$0")/.." > /dev/null - -source "test/logging.sh" - -debug= -llvmir="${llvmir:-}" -use_gdb= - -usage() { - cat << EOF -Usage: test/run_compiled.sh [options] file_1.rb - - -d Run the ruby interpreter under the debugger [lldb] - -g Run the ruby interpreter under the debugger [gdb] (implies -d) - -iPATH Store intermediate outputs in PATH - -EOF -} - -while getopts 'hdgi:' opt; do - case $opt in - h) - usage - exit 0 - ;; - - d) - debug=1 - ;; - - g) - debug=1 - use_gdb=1 - ;; - - i) - llvmir=$OPTARG - ;; - - *) - break; - ;; - esac -done - -shift $((OPTIND - 1)) - -if [ 1 -gt "$#" ]; then - echo "Usage: test/run_compiled.sh [-d] [ ...]" - echo - echo " NOTE: if the 'llvmir' environment variable is set, that will be used" - echo " for compiler output instead." - exit 1 -fi - -rb_file=$1 -rb_files=( "$@" ) - -if [ -z "${llvmir:-}" ]; then - llvmir=$(mktemp -d) - cleanup() { - rm -rf "$llvmir" - } - trap cleanup EXIT - - # Export llvmir so that run_sorbet picks it up. Real argument parsing in - # run_sorbet.sh would probably be better. - export llvmir - -elif [[ ! -d "$llvmir" ]]; then - fatal "llvm output directory '${llvmir}' does not exist" -fi - -compiled_out_dir=$llvmir -export compiled_out_dir - -# ensure that the extension is built -"test/run_sorbet.sh" -s "$llvmir" -i "$llvmir" "${rb_files[@]}" - -if [[ "$llvmir" != /* ]]; then - llvmir="$PWD/$llvmir" -fi - -ruby="./bazel-bin/external/sorbet_ruby_2_7_for_compiler/toolchain/bin/ruby" -sorbet_runtime="./gems/sorbet-runtime/lib/sorbet-runtime.rb" - -echo -info "Building Ruby..." - -if [ -n "$debug" ]; then - if [ -n "$use_gdb" ]; then - ./bazel build @sorbet_ruby_2_7_for_compiler//:ruby --config dbg --config=static-libs - command=("${GDB:-gdb}" "--args" "${ruby}") - else - ./bazel build @sorbet_ruby_2_7_for_compiler//:ruby --config dbg --config=static-libs - command=("${LLDB:-lldb}" "--" "${ruby}") - fi -else - ./bazel build @sorbet_ruby_2_7_for_compiler//:ruby -c opt - command=( "${ruby}" ) -fi - -# Use force_compile to make patch_require.rb fail if the compiled extension -# isn't found. -command=("${command[@]}" \ - "--disable=gems" \ - "--disable=did_you_mean" \ - -r "rubygems" \ - -r "$sorbet_runtime" \ - -r "./test/patch_require.rb" \ - -e "require './$rb_file'" \ - "$@" \ - ) - -echo -info "Running compiled Ruby output..." -info "├─ llvmir=\"$llvmir\" force_compile=1 ${command[*]}" - -if llvmir="$llvmir" force_compile=1 "${command[@]}"; then - success "└─ done." -else - fatal "└─ Non-zero exit. See above." -fi diff --git a/test/run_ruby.sh b/test/run_ruby.sh deleted file mode 100755 index 53924fa3d4..0000000000 --- a/test/run_ruby.sh +++ /dev/null @@ -1,98 +0,0 @@ -#!/bin/bash - -set -euo pipefail - -pushd "$(dirname "$0")/.." > /dev/null - -source "test/logging.sh" - -usage() { - cat << EOF -Usage: test/run_ruby.sh [options] file_1.rb - - -d Run the ruby interpreter under the debugger [lldb] - -g Run the ruby interpreter under the debugger [gdb] (implies -d) -EOF -} - -debug= -use_gdb= - -while getopts 'hdg' opt; do - case $opt in - h) - usage - exit 0 - ;; - - d) - debug=1 - ;; - - g) - debug=1 - use_gdb=1 - ;; - - *) - break; - ;; - esac -done - -shift $((OPTIND - 1)) - -rb_file=$1 -shift - -if [ -z "$rb_file" ]; then - usage - exit 1 -fi - -ruby="./bazel-bin/external/sorbet_ruby_2_7_for_compiler/toolchain/bin/ruby" -sorbet_runtime="./gems/sorbet-runtime/lib/sorbet-runtime.rb" - -echo -info "Building Ruby..." - -if [ -n "$debug" ]; then - if [ -n "$use_gdb" ]; then - ./bazel build @sorbet_ruby_2_7_for_compiler//:ruby --config dbg - command=("gdb" "--args" "${ruby}") - else - ./bazel build @sorbet_ruby_2_7_for_compiler//:ruby --config dbg - command=("lldb" "--" "${ruby}") - fi -else - ./bazel build @sorbet_ruby_2_7_for_compiler//:ruby -c opt - command=( "${ruby}" ) -fi - -# Use a temp directory for LLVMIR so we don't accidentally pick up changes from -# the environment -llvmir=$(mktemp -d) -cleanup() { - rm -r "$llvmir" -} -trap cleanup EXIT - -command=("${command[@]}" \ - "--disable=gems" \ - "--disable=did_you_mean" \ - -r "rubygems" \ - -r "$sorbet_runtime" \ - -r "./test/patch_require.rb" \ - -e "require './$rb_file'" \ - "$@" \ - ) - -echo -info "Running compiled Ruby output..." -info "├─ ${command[*]}" - -if llvmir="$llvmir" "${command[@]}"; then - success "└─ done." -else - fatal "└─ Non-zero exit. See above." -fi diff --git a/test/run_sorbet.sh b/test/run_sorbet.sh deleted file mode 100755 index a6f8fd8c91..0000000000 --- a/test/run_sorbet.sh +++ /dev/null @@ -1,139 +0,0 @@ -#!/bin/bash - -set -euo pipefail - -pushd "$(dirname "$0")/.." > /dev/null - -source "test/logging.sh" - -usage() { - cat < [ ...]" - - -h Show this message - -d Start sorbet under the debugger [lldb] - -g Start sorbet under the debugger [gdb] (implies -d) - -iPATH Place build outputs in PATH - - NOTE: when running this script with tools/scripts/remote-script, an explicit - output path must be provided with -i to enable sync-back. -EOF -} - -debug= -use_gdb= -if [ -n "${llvmir:-}" ]; then - explicit_llvmir=1 -else - llvmir='' -fi - -if [ -n "${compiled_out_dir:-}" ]; then - explicit_compiled_out_dir=1 -else - compiled_out_dir='' -fi - -while getopts ":hdgs:i:" opt; do - case $opt in - h) - usage - exit 0 - ;; - - d) - debug=1 - ;; - - g) - debug=1 - use_gdb=1 - ;; - - s) - explicit_compiled_out_dir=1 - compiled_out_dir="${OPTARG}" - ;; - - i) - explicit_llvmir=1 - llvmir="${OPTARG}" - ;; - - *) - break - ;; - esac -done - -shift $((OPTIND - 1)) - -orig_llvmir="$llvmir" -rb_files=( "$@" ) - -cleanup() { - if [ -n "${llvmir:-}" ] && [ -z "${explicit_llvmir:-}" ]; then - rm -rf "$llvmir" - fi - if [ -n "${compiled_out_dir:-}" ] && [ -z "${explicit_compiled_out_dir:-}" ]; then - rm -rf "compiled_out_dir" - fi -} -trap cleanup EXIT - -if [ -z "$llvmir" ]; then - llvmir="$(mktemp -d)" -elif [[ ! -d "$llvmir" ]]; then - fatal "llvm output directory '${llvmir}' does not exist" -elif [[ "$llvmir" != /* ]]; then - llvmir="$PWD/$llvmir" -fi - -if [ -z "$compiled_out_dir" ]; then - compiled_out_dir="$(mktemp -d)" -elif [[ ! -d "$compiled_out_dir" ]]; then - fatal ".so output directory '${compiled_out_dir}' does not exist" -elif [[ "$compiled_out_dir" != /* ]]; then - compiled_out_dir="$PWD/$compiled_out_dir" -fi - -echo -info "Building SorbetLLVM..." -if [ -n "$debug" ]; then - ./bazel build //compiler:sorbet --config dbg --config=static-libs -else - ./bazel build //compiler:sorbet -c opt -fi - -if [ -n "$debug" ]; then - if [ -n "$use_gdb" ]; then - command=( "${GDB:-gdb}" "--args" "./bazel-bin/compiler/sorbet" ) - else - command=( "${LLDB:-lldb}" "--" "./bazel-bin/compiler/sorbet" ) - fi -else - command=( "bazel-bin/compiler/sorbet" ) -fi - -command=( "${command[@]}" --silence-dev-message "--compiled-out-dir=$compiled_out_dir" "--llvm-ir-dir=$llvmir" \ - "${rb_files[@]}" ) - -echo -info "Running SorbetLLVM to generate LLVM + shared object..." -info "├─ ${command[*]}" - -if "${command[@]}"; then - success "└─ successfully generated LLVM output." - - if [[ -n "${EMIT_SYNCBACK:-}" && -n "$explicit_llvmir" ]]; then - echo '### BEGIN SYNCBACK ###' - for source in "${rb_files[@]}"; do - # compgen returns non-zero when the glob doesn't match - compgen -G "${orig_llvmir}/${source}.*ll" || true - done - echo '### END SYNCBACK ###' - fi - -else - fatal "└─ compiling to LLVM failed. See above." -fi diff --git a/test/sandbox/nvim/README.md b/test/sandbox/nvim/README.md new file mode 100644 index 0000000000..8715fe88b3 --- /dev/null +++ b/test/sandbox/nvim/README.md @@ -0,0 +1,8 @@ +# test/sandbox/nvim + +``` +nvim --noplugin -u init.lua example.rb +``` + +Will use the Sorbet built into `../../../bazel-bin/main/sorbet` (whatever was +most recently built). diff --git a/test/sandbox/nvim/init.lua b/test/sandbox/nvim/init.lua new file mode 100644 index 0000000000..b9890c4a99 --- /dev/null +++ b/test/sandbox/nvim/init.lua @@ -0,0 +1,46 @@ +-- This config is hacky and only meant as a quick and dirty way to test Sorbet in Neovim +-- If you actually want to integrate Sorbet LSP in Neovim, see the nvim-lspconfig project. + +vim.cmd.colorscheme('slate') +vim.g.mapleader = vim.api.nvim_replace_termcodes('', false, false, true) +vim.o.number = true +vim.o.signcolumn = 'yes' + +vim.api.nvim_create_autocmd('LspAttach', { + group = vim.api.nvim_create_augroup('UserLspConfig', {}), + callback = function(ev) + -- Enable completion triggered by + vim.bo[ev.buf].omnifunc = 'v:lua.vim.lsp.omnifunc' + + -- Buffer local mappings. + -- See `:help vim.lsp.*` for documentation on any of the below functions + local opts = { buffer = ev.buf } + vim.keymap.set('n', 'gD', vim.lsp.buf.declaration, opts) + vim.keymap.set('n', 'gd', vim.lsp.buf.definition, opts) + vim.keymap.set('n', 'K', vim.lsp.buf.hover, opts) + vim.keymap.set('n', 't', vim.lsp.buf.type_definition, opts) + vim.keymap.set('n', 'rn', vim.lsp.buf.rename, opts) + vim.keymap.set({ 'n', 'v' }, '.', vim.lsp.buf.code_action, opts) + vim.keymap.set('n', 'r', vim.lsp.buf.references, opts) + vim.keymap.set('n', 'e', vim.diagnostic.open_float, opts) + end, +}) + +vim.lsp.start_client({ + name = 'sorbet', + cmd = { + '../../../bazel-bin/main/sorbet', + '--lsp', + '--debug-log-file=sorbet-nvim.log', + '--disable-watchman', + '--dir=.', + }, + root_dir = vim.fn.expand('%:p:h'), +}) + +vim.api.nvim_create_autocmd('FileType', { + pattern = {'ruby'}, + callback = function() + vim.lsp.buf_attach_client(0, 1) + end +}) diff --git a/test/sandbox/vscode/.vscode/extensions.json b/test/sandbox/vscode/.vscode/extensions.json new file mode 100644 index 0000000000..9cbb7b6e1d --- /dev/null +++ b/test/sandbox/vscode/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + "recommendations": [ + "sorbet.sorbet-vscode-extension" + ], +} diff --git a/test/sandbox/vscode/.vscode/settings.json b/test/sandbox/vscode/.vscode/settings.json new file mode 100644 index 0000000000..6fb240425b --- /dev/null +++ b/test/sandbox/vscode/.vscode/settings.json @@ -0,0 +1,20 @@ +{ + "sorbet.userLspConfigs": [ + { + "id": "path", + "name": "Sorbet (path)", + "description": "Sorbet on PATH, with experimental features", + "cwd": "${workspaceFolder}", + "command": [ + "../../../bazel-bin/main/sorbet", + "--debug-log-file=sorbet-vscode.log", + "--lsp", + "--dir=.", + ] + } + ], + // Sorbet will restart when any of these files change. + "sorbet.configFilePatterns": [ + "../../../bazel-bin/main/sorbet", + ] +} diff --git a/test/sandbox/vscode/README.md b/test/sandbox/vscode/README.md new file mode 100644 index 0000000000..1d5e8f6e5f --- /dev/null +++ b/test/sandbox/vscode/README.md @@ -0,0 +1,16 @@ +# test/sandbox/vscode + +``` +code --new-window . +``` + +You will have to use the `> Sorbet: Configure` command to select the "Sorbet +(path)" configuration, which will use the Sorbet built into +`../../../bazel-bin/main/sorbet` (whatever was most recently built). + +NOTE: launching VS Code this way will default to using the Sorbet VS Code +extension from the marketplace. See `vscode_extension/README.md` folder for +instructions on how to test local changes to the VS Code extension. + +This config is meant just as a quick-and-dirty way to test changes to the Sorbet +language server if no client changes are needed. diff --git a/test/scip/testdata/encrypted_prop.snapshot.rb b/test/scip/testdata/encrypted_prop.snapshot.rb index 808bf45035..c148890090 100644 --- a/test/scip/testdata/encrypted_prop.snapshot.rb +++ b/test/scip/testdata/encrypted_prop.snapshot.rb @@ -15,13 +15,15 @@ class Opus::DB::Model::Mixins::Encryptable::EncryptedValue < Chalk::ODM::Documen # ^^^^^^^^^^^^^^ definition [..] Opus#DB#Model#Mixins#Encryptable#EncryptedValue# # ^^^^^ reference [..] Chalk# # ^^^ reference [..] Chalk#ODM# -# ^^^^^^^^ definition [..] Chalk#ODM#Document# +# ^^^^^^^^ reference [..] Chalk#ODM#Document# end class EncryptedProp # ^^^^^^^^^^^^^ definition [..] EncryptedProp# include T::Props # ^^^^^^^ reference [..] Module#include(). +# ^ reference [..] T# +# ^^^^^ reference [..] T#Props# def self.encrypted_prop(opts={}); end # ^^^^^^^^^^^^^^ definition [..] ``#encrypted_prop(). encrypted_prop :foo diff --git a/test/scip/testdata/field_inheritance.rb b/test/scip/testdata/field_inheritance.rb index 0f66f90053..7878836329 100644 --- a/test/scip/testdata/field_inheritance.rb +++ b/test/scip/testdata/field_inheritance.rb @@ -67,6 +67,7 @@ def self.set_x return end + # BUG: Emitting a definition for 'self' here seems wrong. class << self def set_y @@d1_y = @@d1_v diff --git a/test/scip/testdata/field_inheritance.snapshot.rb b/test/scip/testdata/field_inheritance.snapshot.rb index 4703f3ff9f..56a12cfeb1 100644 --- a/test/scip/testdata/field_inheritance.snapshot.rb +++ b/test/scip/testdata/field_inheritance.snapshot.rb @@ -22,7 +22,7 @@ def set_ivar class C2 < C1 # ^^ definition [..] C2# -# ^^ definition [..] C1# +# ^^ reference [..] C1# def get_inherited_ivar # ^^^^^^^^^^^^^^^^^^ definition [..] C2#get_inherited_ivar(). return @f + @h @@ -56,7 +56,7 @@ def get_new_ivar class C3 < C2 # ^^ definition [..] C3# -# ^^ definition [..] C2# +# ^^ reference [..] C2# def refs # ^^^^ definition [..] C3#refs(). @f = @g + @i @@ -136,7 +136,9 @@ def self.set_x return end + # BUG: Emitting a definition for 'self' here seems wrong. class << self +# ^^^^ definition [..] ``# def set_y # ^^^^^ definition [..] ``#set_y(). @@d1_y = @@d1_v @@ -149,7 +151,7 @@ def set_y class D2 < D1 # ^^ definition [..] D2# -# ^^ definition [..] D1# +# ^^ reference [..] D1# def self.get # ^^^ definition [..] ``#get(). @@d2_x = @@d1_v + @@d1_x @@ -168,7 +170,7 @@ def self.get class D3 < D2 # ^^ definition [..] D3# -# ^^ definition [..] D2# +# ^^ reference [..] D2# def self.get_2 # ^^^^^ definition [..] ``#get_2(). @@d1_v + @@d1_x @@ -237,7 +239,7 @@ class DD1 class DD2 < DD1 # ^^^ definition [..] DD2# -# ^^^ definition [..] DD1# +# ^^^ reference [..] DD1# def self.get_x # ^^^^^ definition [..] ``#get_x(). @@x @@ -271,7 +273,7 @@ def self.set_y class E2 < E1 # ^^ definition [..] E2# -# ^^ definition [..] E1# +# ^^ reference [..] E1# @x = 0 # ^^ definition [..] ``#`@x`. diff --git a/test/scip/testdata/flatfile_dsl.snapshot.rb b/test/scip/testdata/flatfile_dsl.snapshot.rb index d822656455..53a0104e56 100644 --- a/test/scip/testdata/flatfile_dsl.snapshot.rb +++ b/test/scip/testdata/flatfile_dsl.snapshot.rb @@ -14,7 +14,7 @@ def self.field(*_); end class Flatfile < Record # ^^^^^^^^ definition [..] Flatfile# -# ^^^^^^ definition [..] Record# +# ^^^^^^ reference [..] Record# flatfile do # ^^^^^^^^ reference [..] ``#flatfile(). from 1..2, :foo diff --git a/test/scip/testdata/hoverdocs.snapshot.rb b/test/scip/testdata/hoverdocs.snapshot.rb index bd6ec65d4f..985cb7bc7a 100644 --- a/test/scip/testdata/hoverdocs.snapshot.rb +++ b/test/scip/testdata/hoverdocs.snapshot.rb @@ -17,7 +17,7 @@ def m1 # ^^ definition [..] C1#m1(). # documentation # | ```ruby -# | sig {returns(T.untyped)} +# | sig { returns(T.untyped) } # | def m1 # | ``` end @@ -28,7 +28,7 @@ def m2 # ^^ definition [..] C1#m2(). # documentation # | ```ruby -# | sig {returns(T::Boolean)} +# | sig { returns(T::Boolean) } # | def m2 # | ``` true @@ -41,7 +41,7 @@ def m3(c, b) # ^^ definition [..] C1#m3(). # documentation # | ```ruby -# | sig {params(c: T.untyped, b: T.untyped).returns(T::Boolean)} +# | sig { params(c: T.untyped, b: T.untyped).returns(T::Boolean) } # | def m3(c, b) # | ``` # ^ definition local 1~#2519626513 @@ -65,7 +65,7 @@ def m4(xs) # ^^ definition [..] C1#m4(). # documentation # | ```ruby -# | sig {params(xs: T.untyped).returns(T.untyped)} +# | sig { params(xs: T.untyped).returns(T.untyped) } # | def m4(xs) # | ``` # documentation @@ -88,7 +88,7 @@ def m5 # ^^ definition [..] C1#m5(). # documentation # | ```ruby -# | sig {returns(T::Boolean)} +# | sig { returns(T::Boolean) } # | def m5 # | ``` # documentation @@ -106,7 +106,7 @@ def m6(c, b) # ^^ definition [..] C1#m6(). # documentation # | ```ruby -# | sig {params(c: T.untyped, b: T.untyped).returns(T::Boolean)} +# | sig { params(c: T.untyped, b: T.untyped).returns(T::Boolean) } # | def m6(c, b) # | ``` # documentation @@ -179,7 +179,7 @@ def n1 # ^^ definition [..] M1#M2#n1(). # documentation # | ```ruby -# | sig {returns(T::Boolean)} +# | sig { returns(T::Boolean) } # | def n1 # | ``` # documentation @@ -192,7 +192,7 @@ def n2 # ^^ definition [..] M1#M2#n2(). # documentation # | ```ruby -# | sig {returns(T.untyped)} +# | sig { returns(T.untyped) } # | def n2 # | ``` # documentation @@ -206,7 +206,7 @@ def f1 # ^^ definition [..] Object#f1(). # documentation # | ```ruby -# | sig {returns(T.untyped)} +# | sig { returns(T.untyped) } # | def f1 # | ``` # documentation @@ -228,7 +228,7 @@ def f2 # ^^ definition [..] Object#f2(). # documentation # | ```ruby -# | sig {returns(T::Integer (unresolved))} +# | sig { returns(T::Integer (unresolved)) } # | def f2 # | ``` # documentation @@ -240,7 +240,7 @@ def f3 # undocumented global function # ^^ definition [..] Object#f3(). # documentation # | ```ruby -# | sig {returns(T.untyped)} +# | sig { returns(T.untyped) } # | def f3 # | ``` end @@ -258,7 +258,7 @@ def f4 # another undocumented global function # ^^ definition [..] Object#f4(). # documentation # | ```ruby -# | sig {returns(T::Integer (unresolved))} +# | sig { returns(T::Integer (unresolved)) } # | def f4 # | ``` return 10 @@ -278,7 +278,7 @@ def p1 # ^^ definition [..] K1#p1(). # documentation # | ```ruby -# | sig {returns(T.untyped)} +# | sig { returns(T.untyped) } # | def p1 # | ``` # documentation @@ -303,7 +303,7 @@ def self.p2 # ^^ definition [..] ``#p2(). # documentation # | ```ruby -# | sig {returns(T.untyped)} +# | sig { returns(T.untyped) } # | def self.p2 # | ``` # documentation @@ -327,13 +327,7 @@ class K2 < K1 # | ``` # documentation # | Subclass -# ^^ definition [..] K1# -# documentation -# | ```ruby -# | class K1 -# | ``` -# documentation -# | Parent class +# ^^ reference [..] K1# # doc comment on class var ooh @z = 9 # ^^ definition [..] ``#`@z`. @@ -349,7 +343,7 @@ def p1 # ^^ definition [..] K2#p1(). # documentation # | ```ruby -# | sig {returns(T.untyped)} +# | sig { returns(T.untyped) } # | def p1 # | ``` # documentation diff --git a/test/scip/testdata/inheritance.snapshot.rb b/test/scip/testdata/inheritance.snapshot.rb index 80cbd0698f..651f65b7bf 100644 --- a/test/scip/testdata/inheritance.snapshot.rb +++ b/test/scip/testdata/inheritance.snapshot.rb @@ -52,7 +52,7 @@ def write_f(a) class Z3 < Z1 # ^^ definition [..] Z3# -# ^^ definition [..] Z1# +# ^^ reference [..] Z1# extend T::Sig # ^^^^^^ reference [..] Kernel#extend(). @@ -68,7 +68,7 @@ def read_f_plus_1? class Z4 < Z3 # ^^ definition [..] Z4# -# ^^ definition [..] Z3# +# ^^ reference [..] Z3# extend T::Sig # ^^^^^^ reference [..] Kernel#extend(). diff --git a/test/scip/testdata/loops_and_conditionals.snapshot.rb b/test/scip/testdata/loops_and_conditionals.snapshot.rb index b15dbad5a3..61b1f91c91 100644 --- a/test/scip/testdata/loops_and_conditionals.snapshot.rb +++ b/test/scip/testdata/loops_and_conditionals.snapshot.rb @@ -81,8 +81,8 @@ def case(x, y) x = 2 # ^ reference (write) local 1~#2602907825 when (3 == (x = 1)) +# ^^^^^^^ reference local 1~#2602907825 # ^ reference (write) local 1~#2602907825 -# ^^^^^ reference local 1~#2602907825 x = 0 # ^ reference (write) local 1~#2602907825 else diff --git a/test/scip/testdata/method_inheritance.snapshot.rb b/test/scip/testdata/method_inheritance.snapshot.rb index 8f4f8e39f9..2aea57918c 100644 --- a/test/scip/testdata/method_inheritance.snapshot.rb +++ b/test/scip/testdata/method_inheritance.snapshot.rb @@ -13,7 +13,7 @@ def m2 class C2 < C1 # ^^ definition [..] C2# -# ^^ definition [..] C1# +# ^^ reference [..] C1# def m2 # ^^ definition [..] C2#m2(). end @@ -28,7 +28,7 @@ def m3 class C3 < C2 # ^^ definition [..] C3# -# ^^ definition [..] C2# +# ^^ reference [..] C2# def m4 # ^^ definition [..] C3#m4(). m1 diff --git a/test/scip/testdata/minitest.snapshot.rb b/test/scip/testdata/minitest.snapshot.rb index dfdada292c..98cee0721b 100644 --- a/test/scip/testdata/minitest.snapshot.rb +++ b/test/scip/testdata/minitest.snapshot.rb @@ -24,7 +24,7 @@ def outside_method # ^^ definition [..] MyTest#C2. # ^^^^^^^^^^^^^^^^^^^^^^^ reference [..] Kernel# # ^^^^^^^^^^^^^^^^^^^^^^^ reference [..] Kernel#raise(). -# ^^^^^^^ definition local 9~#119448696 +# ^^^^^^^ definition local 3~#119448696 # ^^^^^^^ reference [..] Integer# end @@ -38,6 +38,7 @@ def outside_method end describe "some inner tests" do +# ^^^^^^^^^^^^^^^^^^ reference [..] MyTest# # ^^^^^^^^^^^^^^^^^^ definition [..] MyTest#``# def inside_method # ^^^^^^^^^^^^^ definition [..] MyTest#``#inside_method(). @@ -54,7 +55,7 @@ def instance_helper; end # ^^^^^^^^^^^^^^^ definition [..] MyTest#instance_helper(). before do -# ^^^^^^ definition [..] MyTest#initialize(). +# ^^^^^^ definition [..] MyTest#``(). @foo = T.let(3, Integer) instance_helper end @@ -70,6 +71,7 @@ def self.random_method end describe Object do +# ^^^^^^ reference [..] MyTest# # ^^^^^^ definition [..] MyTest#``# it Object do # ^^^^^^ definition [..] MyTest#``#``(). @@ -94,10 +96,12 @@ def self.it(*args) end describe "a non-ideal situation" do +# ^^^^^^^^^^^^^^^^^^^^^^^ reference [..] MyTest# # ^^^^^^^^^^^^^^^^^^^^^^^ definition [..] MyTest#``# it "contains nested describes" do # ^^^^^^^^^^^^^^^^^^^^^^^^^^^ definition [..] MyTest#``#``(). describe "nobody should write this but we should still parse it" do +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ reference [..] MyTest#``# # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ definition [..] MyTest#``#``# end end diff --git a/test/scip/testdata/mixin.snapshot.rb b/test/scip/testdata/mixin.snapshot.rb index dac321db93..4ee65c05de 100644 --- a/test/scip/testdata/mixin.snapshot.rb +++ b/test/scip/testdata/mixin.snapshot.rb @@ -10,6 +10,7 @@ class C1 # ^^ definition [..] C1# include M # ^^^^^^^ reference [..] Module#include(). +# ^ reference [..] M# # ^ reference [..] M# def f; puts 'C1.f'; end # ^ definition [..] C1#f(). @@ -19,15 +20,16 @@ def f; puts 'C1.f'; end # f refers to C1.f class C2 < C1 # ^^ definition [..] C2# -# ^^ definition [..] C1# +# ^^ reference [..] C1# end # f refers to C1.f class C3 < C1 # ^^ definition [..] C3# -# ^^ definition [..] C1# +# ^^ reference [..] C1# include M # ^^^^^^^ reference [..] Module#include(). +# ^ reference [..] M# # ^ reference [..] M# end @@ -42,6 +44,7 @@ class D2 # ^^ definition [..] D2# include M # ^^^^^^^ reference [..] Module#include(). +# ^ reference [..] M# # ^ reference [..] M# end @@ -83,6 +86,7 @@ class C # ^ definition [..] T0#C# include M # ^^^^^^^ reference [..] Module#include(). +# ^ reference [..] T0#M# # ^ reference [..] T0#M# def set_f_1; @f = 1; end # ^^^^^^^ definition [..] T0#C#set_f_1(). @@ -111,6 +115,7 @@ module M1 # ^^ definition [..] T1#M1# include M0 # ^^^^^^^ reference [..] Module#include(). +# ^^ reference [..] T1#M0# # ^^ reference [..] T1#M0# end @@ -118,6 +123,7 @@ class C # ^ definition [..] T1#C# include M1 # ^^^^^^^ reference [..] Module#include(). +# ^^ reference [..] T1#M1# # ^^ reference [..] T1#M1# def set_f_1; @f = 1; end # ^^^^^^^ definition [..] T1#C#set_f_1(). @@ -146,6 +152,7 @@ class C # ^ definition [..] T2#C# include M # ^^^^^^^ reference [..] Module#include(). +# ^ reference [..] T2#M# # ^ reference [..] T2#M# def get_f; @f; end # ^^^^^ definition [..] T2#C#get_f(). @@ -169,6 +176,7 @@ module M1 # ^^ definition [..] T3#M1# include M0 # ^^^^^^^ reference [..] Module#include(). +# ^^ reference [..] T3#M0# # ^^ reference [..] T3#M0# end @@ -176,6 +184,7 @@ class C # ^ definition [..] T3#C# include M1 # ^^^^^^^ reference [..] Module#include(). +# ^^ reference [..] T3#M1# # ^^ reference [..] T3#M1# def get_f; @f; end # ^^^^^ definition [..] T3#C#get_f(). @@ -205,9 +214,10 @@ def set_f_2; @f = 2; end class C1 < C0 # ^^ definition [..] T4#C1# -# ^^ definition [..] T4#C0# +# ^^ reference [..] T4#C0# include M # ^^^^^^^ reference [..] Module#include(). +# ^ reference [..] T4#M# # ^ reference [..] T4#M# def set_f_1; @f = 1; end # ^^^^^^^ definition [..] T4#C1#set_f_1(). @@ -238,6 +248,7 @@ module M1 # ^^ definition [..] T5#M1# include M0 # ^^^^^^^ reference [..] Module#include(). +# ^^ reference [..] T5#M0# # ^^ reference [..] T5#M0# end @@ -251,9 +262,10 @@ def set_f_2; @f = 2; end class C1 < C0 # ^^ definition [..] T5#C1# -# ^^ definition [..] T5#C0# +# ^^ reference [..] T5#C0# include M # ^^^^^^^ reference [..] Module#include(). +# ^ reference [..] M# # ^ reference [..] M# def set_f_1; @f = 1; end # ^^^^^^^ definition [..] T5#C1#set_f_1(). @@ -290,7 +302,7 @@ def set_f_1; @f = 1; end class C1 < C0 # ^^ definition [..] T6#C1# -# ^^ definition [..] T6#C0# +# ^^ reference [..] T6#C0# def get_f; @f; end # ^^^^^ definition [..] T6#C1#get_f(). # ^^ reference [..] T6#C1#`@f`. @@ -314,6 +326,7 @@ module M1 # ^^ definition [..] T7#M1# include M0 # ^^^^^^^ reference [..] Module#include(). +# ^^ reference [..] T7#M0# # ^^ reference [..] T7#M0# end @@ -327,7 +340,7 @@ def set_f_1; @f = 1; end class C1 < C0 # ^^ definition [..] T7#C1# -# ^^ definition [..] T7#C0# +# ^^ reference [..] T7#C0# def get_f; @f; end # ^^^^^ definition [..] T7#C1#get_f(). # ^^ reference [..] T7#C1#`@f`. @@ -351,6 +364,7 @@ class C0 # ^^ definition [..] T8#C0# include M # ^^^^^^^ reference [..] Module#include(). +# ^ reference [..] T8#M# # ^ reference [..] T8#M# def set_f_1; @f = 1; end # ^^^^^^^ definition [..] T8#C0#set_f_1(). @@ -361,7 +375,7 @@ def set_f_1; @f = 1; end class C1 < C0 # ^^ definition [..] T8#C1# -# ^^ definition [..] T8#C0# +# ^^ reference [..] T8#C0# def set_f_2; @f = 2; end # ^^^^^^^ definition [..] T8#C1#set_f_2(). # ^^ definition [..] T8#C1#`@f`. @@ -391,6 +405,7 @@ class C0 # ^^ definition [..] T9#C0# include M # ^^^^^^^ reference [..] Module#include(). +# ^ reference [..] T9#M# # ^ reference [..] T9#M# def set_f_1; @f = 1; end # ^^^^^^^ definition [..] T9#C0#set_f_1(). @@ -401,7 +416,7 @@ def set_f_1; @f = 1; end class C1 < C0 # ^^ definition [..] T9#C1# -# ^^ definition [..] T9#C0# +# ^^ reference [..] T9#C0# def get_f; @f; end # ^^^^^ definition [..] T9#C1#get_f(). # ^^ reference [..] T9#C1#`@f`. @@ -425,12 +440,13 @@ class C0 # ^^ definition [..] T10#C0# include M # ^^^^^^^ reference [..] Module#include(). +# ^ reference [..] T10#M# # ^ reference [..] T10#M# end class C1 < C0 # ^^ definition [..] T10#C1# -# ^^ definition [..] T10#C0# +# ^^ reference [..] T10#C0# def set_f_2; @f = 2; end # ^^^^^^^ definition [..] T10#C1#set_f_2(). # ^^ definition [..] T10#C1#`@f`. @@ -457,12 +473,13 @@ class C0 # ^^ definition [..] T11#C0# include M # ^^^^^^^ reference [..] Module#include(). +# ^ reference [..] T11#M# # ^ reference [..] T11#M# end class C1 < C0 # ^^ definition [..] T11#C1# -# ^^ definition [..] T11#C0# +# ^^ reference [..] T11#C0# def get_f; @f; end # ^^^^^ definition [..] T11#C1#get_f(). # ^^ reference [..] T11#C1#`@f`. @@ -493,9 +510,11 @@ module M2 # ^^ definition [..] T12#M2# include M0 # ^^^^^^^ reference [..] Module#include(). +# ^^ reference [..] T12#M0# # ^^ reference [..] T12#M0# include M1 # ^^^^^^^ reference [..] Module#include(). +# ^^ reference [..] T12#M1# # ^^ reference [..] T12#M1# def set_f_2; @f = 2; end # ^^^^^^^ definition [..] T12#M2#set_f_2(). @@ -508,6 +527,7 @@ class C # ^ definition [..] T12#C# include M2 # ^^^^^^^ reference [..] Module#include(). +# ^^ reference [..] T12#M2# # ^^ reference [..] T12#M2# def set_f_3; @f = 3; end # ^^^^^^^ definition [..] T12#C#set_f_3(). @@ -544,9 +564,11 @@ module M2 # ^^ definition [..] T13#M2# include M0 # ^^^^^^^ reference [..] Module#include(). +# ^^ reference [..] T13#M0# # ^^ reference [..] T13#M0# include M1 # ^^^^^^^ reference [..] Module#include(). +# ^^ reference [..] T13#M1# # ^^ reference [..] T13#M1# def set_f_2; @f = 2; end # ^^^^^^^ definition [..] T13#M2#set_f_2(). @@ -559,6 +581,7 @@ class C # ^ definition [..] T13#C# include M2 # ^^^^^^^ reference [..] Module#include(). +# ^^ reference [..] T13#M2# # ^^ reference [..] T13#M2# def get_f; @f; end # ^^^^^ definition [..] T13#C#get_f(). @@ -590,9 +613,11 @@ module M2 # ^^ definition [..] T14#M2# include M0 # ^^^^^^^ reference [..] Module#include(). +# ^^ reference [..] T14#M0# # ^^ reference [..] T14#M0# include M1 # ^^^^^^^ reference [..] Module#include(). +# ^^ reference [..] T14#M1# # ^^ reference [..] T14#M1# end @@ -600,6 +625,7 @@ class C # ^ definition [..] T14#C# include M2 # ^^^^^^^ reference [..] Module#include(). +# ^^ reference [..] T14#M2# # ^^ reference [..] T14#M2# def set_f_3; @f = 3; end # ^^^^^^^ definition [..] T14#C#set_f_3(). @@ -636,9 +662,11 @@ module M2 # ^^ definition [..] T15#M2# include M0 # ^^^^^^^ reference [..] Module#include(). +# ^^ reference [..] T15#M0# # ^^ reference [..] T15#M0# include M1 # ^^^^^^^ reference [..] Module#include(). +# ^^ reference [..] T15#M1# # ^^ reference [..] T15#M1# end @@ -646,6 +674,7 @@ class C # ^ definition [..] T15#C# include M2 # ^^^^^^^ reference [..] Module#include(). +# ^^ reference [..] T15#M2# # ^^ reference [..] T15#M2# def get_f; @f; end # ^^^^^ definition [..] T15#C#get_f(). @@ -677,9 +706,11 @@ class C # ^ definition [..] T16#C# include M0 # ^^^^^^^ reference [..] Module#include(). +# ^^ reference [..] T16#M0# # ^^ reference [..] T16#M0# include M1 # ^^^^^^^ reference [..] Module#include(). +# ^^ reference [..] T16#M1# # ^^ reference [..] T16#M1# def set_f_2; @f = 2; end # ^^^^^^^ definition [..] T16#C#set_f_2(). @@ -716,9 +747,11 @@ class C # ^ definition [..] T17#C# include M0 # ^^^^^^^ reference [..] Module#include(). +# ^^ reference [..] T17#M0# # ^^ reference [..] T17#M0# include M1 # ^^^^^^^ reference [..] Module#include(). +# ^^ reference [..] T17#M1# # ^^ reference [..] T17#M1# def get_f; @f; end # ^^^^^ definition [..] T17#C#get_f(). @@ -745,6 +778,7 @@ class C # ^ definition [..] W0#C# include M # ^^^^^^^ reference [..] Module#include(). +# ^ reference [..] W0#M# # ^ reference [..] W0#M# def set_f; @f = 0; end # ^^^^^ definition [..] W0#C#set_f(). @@ -769,6 +803,7 @@ module M1 # ^^ definition [..] W1#M1# include M0 # ^^^^^^^ reference [..] Module#include(). +# ^^ reference [..] W1#M0# # ^^ reference [..] W1#M0# end @@ -776,6 +811,7 @@ class C # ^ definition [..] W1#C# include M1 # ^^^^^^^ reference [..] Module#include(). +# ^^ reference [..] W1#M1# # ^^ reference [..] W1#M1# def set_f; @f = 0; end # ^^^^^ definition [..] W1#C#set_f(). @@ -806,9 +842,10 @@ def get_f; @f; end class C1 < C0 # ^^ definition [..] W2#C1# -# ^^ definition [..] W2#C0# +# ^^ reference [..] W2#C0# include M # ^^^^^^^ reference [..] Module#include(). +# ^ reference [..] W2#M# # ^ reference [..] W2#M# def get_fp1; @f + 1; end # ^^^^^^^ definition [..] W2#C1#get_fp1(). @@ -838,9 +875,10 @@ def set_f; @f = 0; end class C1 < C0 # ^^ definition [..] W3#C1# -# ^^ definition [..] W3#C0# +# ^^ reference [..] W3#C0# include M # ^^^^^^^ reference [..] Module#include(). +# ^ reference [..] W3#M# # ^ reference [..] W3#M# def get_fp1; @f + 1; end # ^^^^^^^ definition [..] W3#C1#get_fp1(). @@ -864,6 +902,7 @@ module M1 # ^^ definition [..] W4#M1# include M0 # ^^^^^^^ reference [..] Module#include(). +# ^^ reference [..] W4#M0# # ^^ reference [..] W4#M0# def set_f; @f = 0; end # ^^^^^ definition [..] W4#M1#set_f(). @@ -876,6 +915,7 @@ class C # ^ definition [..] W4#C# include M1 # ^^^^^^^ reference [..] Module#include(). +# ^^ reference [..] W4#M1# # ^^ reference [..] W4#M1# def get_fp1; @f + 1; end # ^^^^^^^ definition [..] W4#C#get_fp1(). @@ -905,9 +945,11 @@ class C # ^ definition [..] W5#C# include M0 # ^^^^^^^ reference [..] Module#include(). +# ^^ reference [..] W5#M0# # ^^ reference [..] W5#M0# include M1 # ^^^^^^^ reference [..] Module#include(). +# ^^ reference [..] W5#M1# # ^^ reference [..] W5#M1# def get_fp1; @f + 1; end # ^^^^^^^ definition [..] W5#C#get_fp1(). diff --git a/test/scip/testdata/non_existent.snapshot.rb b/test/scip/testdata/non_existent.snapshot.rb index 8fde9a1350..4afe92e578 100644 --- a/test/scip/testdata/non_existent.snapshot.rb +++ b/test/scip/testdata/non_existent.snapshot.rb @@ -6,5 +6,5 @@ class D class C < ::D # ^ definition [..] C# -# ^ definition [..] D# +# ^ reference [..] D# end diff --git a/test/scip/testdata/prop.snapshot.rb b/test/scip/testdata/prop.snapshot.rb index 4b4d711448..b60d0fb477 100644 --- a/test/scip/testdata/prop.snapshot.rb +++ b/test/scip/testdata/prop.snapshot.rb @@ -6,6 +6,8 @@ class SomeODM # ^^^^^^ reference [..] Kernel#extend(). include T::Props # ^^^^^^^ reference [..] Module#include(). +# ^ reference [..] T# +# ^^^^^ reference [..] T#Props# prop :foo, String # ^^^ definition [..] SomeODM#`foo=`(). @@ -39,6 +41,8 @@ class AdvancedODM # ^^^^^^^^^^^ definition [..] AdvancedODM# include T::Props # ^^^^^^^ reference [..] Module#include(). +# ^ reference [..] T# +# ^^^^^ reference [..] T#Props# prop :default, String, default: "" # ^^^^^^^ definition [..] AdvancedODM#`default=`(). # ^^^^^^^ definition [..] AdvancedODM#default(). @@ -125,6 +129,8 @@ class PropHelpers # ^^^^^^^^^^^ definition [..] PropHelpers# include T::Props # ^^^^^^^ reference [..] Module#include(). +# ^ reference [..] T# +# ^^^^^ reference [..] T#Props# def self.token_prop(opts={}); end # ^^^^^^^^^^ definition [..] ``#token_prop(). def self.created_prop(opts={}); end @@ -145,6 +151,8 @@ class PropHelpers2 # ^^^^^^^^^^^^ definition [..] PropHelpers2# include T::Props # ^^^^^^^ reference [..] Module#include(). +# ^ reference [..] T# +# ^^^^^ reference [..] T#Props# def self.timestamped_token_prop(opts={}); end # ^^^^^^^^^^^^^^^^^^^^^^ definition [..] ``#timestamped_token_prop(). def self.created_prop(opts={}); end diff --git a/test/scip/testdata/rescue.snapshot.rb b/test/scip/testdata/rescue.snapshot.rb index 32d4416ca9..783d23a424 100644 --- a/test/scip/testdata/rescue.snapshot.rb +++ b/test/scip/testdata/rescue.snapshot.rb @@ -2,7 +2,7 @@ class MyError < StandardError # ^^^^^^^ definition [..] MyError# -# ^^^^^^^^^^^^^ definition [..] StandardError# +# ^^^^^^^^^^^^^ reference [..] StandardError# end def handle(e) @@ -22,15 +22,15 @@ def f # ^^^^^ reference [..] Kernel#raise(). rescue MyError => e1 # ^^^^^^^ reference [..] MyError# -# ^^ definition local 1~#3809224601 +# ^^ definition local 2~#3809224601 handle(e1) # ^^^^^^ reference [..] Object#handle(). -# ^^ reference local 1~#3809224601 +# ^^ reference local 2~#3809224601 rescue StandardError => e2 # ^^^^^^^^^^^^^ reference [..] StandardError# -# ^^ definition local 3~#3809224601 +# ^^ definition local 4~#3809224601 handle(e2) # ^^^^^^ reference [..] Object#handle(). -# ^^ reference local 3~#3809224601 +# ^^ reference local 4~#3809224601 end end diff --git a/test/scip/testdata/singleton.snapshot.rb b/test/scip/testdata/singleton.snapshot.rb index 62d2ee46d1..818fec3a99 100644 --- a/test/scip/testdata/singleton.snapshot.rb +++ b/test/scip/testdata/singleton.snapshot.rb @@ -4,18 +4,20 @@ class A # ^ definition [..] A# include Singleton # ^^^^^^^ reference [..] Module#include(). +# ^^^^^^^^^ reference [..] Singleton# # ^^^^^^^^^ reference [..] Singleton# end # Singleton supports inheritance, turning the sub-class into a singleton as well. class B < A; end # ^ definition [..] B# -# ^ definition [..] A# +# ^ reference [..] A# class C # ^ definition [..] C# include Singleton # ^^^^^^^ reference [..] Module#include(). +# ^^^^^^^^^ reference [..] Singleton# # ^^^^^^^^^ reference [..] Singleton# extend T::Helpers # ^^^^^^ reference [..] Kernel#extend(). diff --git a/test/scip/testdata/struct.snapshot.rb b/test/scip/testdata/struct.snapshot.rb index 2eeac29b92..9e2deba69f 100644 --- a/test/scip/testdata/struct.snapshot.rb +++ b/test/scip/testdata/struct.snapshot.rb @@ -5,7 +5,7 @@ class S < T::Struct # ^ definition [..] S# # ^ definition [..] S#initialize(). # ^ reference [..] T# -# ^^^^^^ definition [..] T#Struct# +# ^^^^^^ reference [..] T#Struct# prop :prop_i, Integer # ^^^^^^ definition [..] S#`prop_i=`(). # ^^^^^^ definition [..] S#prop_i(). @@ -47,8 +47,11 @@ def f end POINT = Struct.new(:x, :y) do +#^^^^^ reference [..] POINT# #^^^^^ definition [..] POINT# -#^^^^^^^^^^^^^^^^^^^^ definition [..] Struct# +#^^^^^ definition [..] POINT# +#^^^^^^^^^^^^^^^^^^^^ reference [..] Struct# +#^^^^^^^^^^^^^^^^^^^^ definition local 2~#119448696 #^^^^^^^^^^^^^^^^^^^^ definition local 5~#119448696 #^^^^^^^^^^^^^^^^^^^^ definition [..] POINT#initialize(). # ^ definition [..] POINT#`x=`(). diff --git a/test/scip/testdata/test_case.snapshot.rb b/test/scip/testdata/test_case.snapshot.rb index 8548e2850c..2a7601792e 100644 --- a/test/scip/testdata/test_case.snapshot.rb +++ b/test/scip/testdata/test_case.snapshot.rb @@ -8,7 +8,7 @@ class ActiveSupport::TestCase class MyTest < ActiveSupport::TestCase # ^^^^^^ definition [..] MyTest# # ^^^^^^^^^^^^^ reference [..] ActiveSupport# -# ^^^^^^^^ definition [..] ActiveSupport#TestCase# +# ^^^^^^^^ reference [..] ActiveSupport#TestCase# extend T::Sig # ^^^^^^ reference [..] Kernel#extend(). # Helper instance method @@ -27,7 +27,7 @@ def self.test(*args, &block) end setup do -# ^^^^^ definition [..] MyTest#initialize(). +# ^^^^^ definition [..] MyTest#``(). @a = T.let(1, Integer) end @@ -44,7 +44,7 @@ def self.test(*args, &block) class NoMatchTest < ActiveSupport::TestCase # ^^^^^^^^^^^ definition [..] NoMatchTest# # ^^^^^^^^^^^^^ reference [..] ActiveSupport# -# ^^^^^^^^ definition [..] ActiveSupport#TestCase# +# ^^^^^^^^ reference [..] ActiveSupport#TestCase# extend T::Sig # ^^^^^^ reference [..] Kernel#extend(). @@ -75,7 +75,7 @@ def assert_equal(a, b); end # ^^^^^^^^^^^^ definition [..] NoParentClass#assert_equal(). setup do -# ^^^^^ definition [..] NoParentClass#initialize(). +# ^^^^^ definition [..] NoParentClass#``(). @a = T.let(1, Integer) end diff --git a/test/scip/testdata/type_change.snapshot.rb b/test/scip/testdata/type_change.snapshot.rb index f498fec32f..08cb2fd731 100644 --- a/test/scip/testdata/type_change.snapshot.rb +++ b/test/scip/testdata/type_change.snapshot.rb @@ -5,7 +5,7 @@ def assign_different_branches(b) # ^^^^^^^^^^^^^^^^^^^^^^^^^ definition [..] Object#assign_different_branches(). # documentation # | ```ruby -# | sig {params(b: T.untyped).returns(T.untyped)} +# | sig { params(b: T.untyped).returns(T.untyped) } # | def assign_different_branches(b) # | ``` # ^ definition local 1~#3317016627 @@ -35,7 +35,7 @@ def change_different_branches(b) # ^^^^^^^^^^^^^^^^^^^^^^^^^ definition [..] Object#change_different_branches(). # documentation # | ```ruby -# | sig {params(b: T.untyped).returns(T.untyped)} +# | sig { params(b: T.untyped).returns(T.untyped) } # | def change_different_branches(b) # | ``` # ^ definition local 1~#2122680152 @@ -71,7 +71,7 @@ def loop_type_change(bs) # ^^^^^^^^^^^^^^^^ definition [..] Object#loop_type_change(). # documentation # | ```ruby -# | sig {params(bs: T.untyped).returns(T.untyped)} +# | sig { params(bs: T.untyped).returns(T.untyped) } # | def loop_type_change(bs) # | ``` # ^^ definition local 1~#4057334513 @@ -141,7 +141,7 @@ def change_type(b) # ^^^^^^^^^^^ definition [..] C#change_type(). # documentation # | ```ruby -# | sig {params(b: T.untyped).returns(T.untyped)} +# | sig { params(b: T.untyped).returns(T.untyped) } # | def change_type(b) # | ``` # ^ definition local 1~#2066187318 @@ -213,16 +213,12 @@ class D < C # | ```ruby # | class D < C # | ``` -# ^ definition [..] C# -# documentation -# | ```ruby -# | class C -# | ``` +# ^ reference [..] C# def change_type(b) # ^^^^^^^^^^^ definition [..] D#change_type(). # documentation # | ```ruby -# | sig {params(b: T.untyped).returns(T.untyped)} +# | sig { params(b: T.untyped).returns(T.untyped) } # | def change_type(b) # | ``` # ^ definition local 1~#2066187318 diff --git a/test/scip/testdata/type_docs.snapshot.rb b/test/scip/testdata/type_docs.snapshot.rb index 3d88235724..33e465bc52 100644 --- a/test/scip/testdata/type_docs.snapshot.rb +++ b/test/scip/testdata/type_docs.snapshot.rb @@ -17,7 +17,7 @@ def js_add(x, y) # ^^^^^^ definition [..] M#js_add(). # documentation # | ```ruby -# | sig {params(x: Integer, y: String).returns(String)} +# | sig { params(x: Integer, y: String).returns(String) } # | def js_add(x, y) # | ``` # ^ definition local 1~#1239553962 diff --git a/test/scip_test_runner.cc b/test/scip_test_runner.cc index e39834bcd8..5e2b732eb5 100644 --- a/test/scip_test_runner.cc +++ b/test/scip_test_runner.cc @@ -16,6 +16,7 @@ #include "absl/strings/match.h" #include "absl/strings/str_replace.h" #include "absl/strings/str_split.h" +#include "absl/types/span.h" #include "spdlog/sinks/stdout_color_sinks.h" #include "ast/ast.h" @@ -221,9 +222,16 @@ TEST_CASE("GemInference") { checkGem(notSorbetRBI, "mygem", "33"); } -// Copied from pipeline_test_runner.cc -class CFGCollectorAndTyper { // TODO(varun): Copy this over to scip_test_runner.cc +// Based on a mix of pipeline_test_runner.cc and pipeline.cc +class CFGCollectorAndTyper { public: + void postTransformClassDef(core::Context ctx, ast::ExpressionPtr &tree) { + auto &c = ast::cast_tree_nonnull(tree); + for (auto &extension : ctx.state.semanticExtensions) { + extension->typecheckClass(ctx, ctx.file, c); + } + } + vector> cfgs; void preTransformMethodDef(core::Context ctx, ast::ExpressionPtr &tree) { auto &m = ast::cast_tree_nonnull(tree); @@ -568,7 +576,8 @@ void test_one_gem(Expectations &test, const TestSettings &settings) { auto workers = WorkerPool::create(0, gs.tracer()); sorbet::core::UnfreezeSymbolTable st(gs); - trees = move(namer::Namer::run(gs, move(trees), *workers, nullptr).result()); + bool wasTypecheckingCanceled = namer::Namer::run(gs, absl::MakeSpan(trees), *workers, nullptr); + ENFORCE(!wasTypecheckingCanceled); trees = move(resolver::Resolver::run(gs, move(trees), *workers).result()); for (auto &extension : gs.semanticExtensions) { diff --git a/test/test_corpus_runner.sh b/test/test_corpus_runner.sh deleted file mode 100755 index 410793cbe3..0000000000 --- a/test/test_corpus_runner.sh +++ /dev/null @@ -1,246 +0,0 @@ -#!/bin/bash -set -euo pipefail - -# shellcheck source-path=SCRIPTDIR/.. -source "test/logging.sh" - -# Argument Parsing ############################################################# - -# Positional arguments -rbout=${1/--expected_output=/} - -# TODO(trevor): Remove this once stderr is being checked -# shellcheck disable=SC2034 -rberr=${2/--expected_err=/} -rbexit=${3/--expected_exit_code=/} -rbcode=$(< "$rbexit") -build_dir=${4/--build_dir=/} -ruby=${5/--ruby=/} -expect_fail= -case "${6/--expect_fail=/}" in - True) expect_fail=1;; - False) ;; #ok - *) fatal "Expected --expect-fail=(True|False), got $6" ;; -esac -sorbet_exit=${7/--sorbet_exit=} -sorbet_out=${8/--sorbet_out=} -shift 8 - -# sources make up the remaining argumenets -rbmain=$1 -rb=( "$@" ) - -# Environment Setup ############################################################ - -root="$PWD" - -# Test stdout/stderr logs -stdout="$(mktemp)" -stderr="$(mktemp)" - -# Test wrapper -runfile="$(mktemp)" - -# Filtered versions of stderrs -stderr_filtered=$(mktemp) -rberr_filtered=$(mktemp) - -cleanup() { - rm -f "$stdout" "$stderr" "$runfile" "$stderr_filtered" "$rberr_filtered" -} -trap cleanup EXIT - -# Main ######################################################################### - -echo "" -attn "Troubleshooting? Use these helpers interactively at the shell:" -info "├─ test/run_ruby.sh ${rb[0]}" -info "├─ test/run_sorbet.sh ${rb[0]}" -info "└─ test/run_compiled.sh ${rb[0]}" -info "" -attn "Or these to attach a debugger:" -info "├─ test/run_ruby.sh -d ${rb[0]}" -info "├─ test/run_sorbet.sh -d ${rb[0]}" -info "└─ test/run_compiled.sh -d ${rb[0]}" - -echo "" -info "Testing ruby..." -if ! $ruby -e 'puts (require "set")' > /dev/null; then - fatal "└─ Ruby is not functioning: bazel-bin/$ruby" -else - success "└─ path: bazel-bin/$ruby" -fi - -echo "" -info "Pre-computed output of running interpreted:" -info "├─ stdout: bazel-out/k8-opt/bin/$rbout" -info "├─ stderr: bazel-out/k8-opt/bin/$rberr" -info "└─ exit code: $rbcode" - -indent_and_nest() { - sed -e 's/^/ │/' -} - -something_failed() { - if [ -n "$expect_fail" ]; then - echo "" - success "Disabled test failed as expected." - info "To make this failing test fail the build, move it out of the disabled folder." - echo "" - exit 0 - else - echo "" - error "Test failed." - echo "" - exit 1 - fi -} - -echo "" -info "Compiled artifacts (.so/.bundle, .ll, .llo)..." -info "├─ from: ${build_dir}" -info "├─ contents:" -find "$build_dir/" -type f | indent_and_nest -success "└─ done." - -echo "" -info "Checking sorbet build dir..." -if [ "$(< "$sorbet_exit")" -ne 0 ]; then - error "├─ Sorbet failed when generating archive:" - < "$sorbet_exit" indent_and_nest - error "└─ output is above." - - something_failed -fi -if [ -z "$(find "$build_dir/" -name '*.so' -o -name '*.bundle')" ]; then - if ! grep -q '# typed:' "${rb[@]}"; then - attn "├─ No '# typed: ...' sigil(s) in input files" - fi - - if ! grep -q '# compiled:' "${rb[@]}"; then - attn "├─ No '# compiled: ...' sigil(s) in input files" - fi - - info "├─ console output:" - < "$sorbet_out" indent_and_nest - - error '└─ no shared object produced. See above for potential reasons why.' - - something_failed -fi -success "└─ done." - -# NOTE: running the test could be split out into its own genrule, the test just -# needs to validate that the output matches. -echo "" -info "Running compiled version with preamble..." - -# NOTE: using a temp file here, as that will cause ruby to not print the name of -# the main file in a stack trace. -echo "require './$rbmain'" > "$runfile" - -set +e -# NOTE: the llvmir environment variable must have a leading `./`, otherwise the -# require will trigger path search. -force_compile=1 llvmir="$PWD/${build_dir}/" $ruby \ - --disable=gems \ - --disable=did_you_mean \ - -r "rubygems" \ - -r "${root}/gems/sorbet-runtime/lib/sorbet-runtime.rb" \ - -r "${root}/test/patch_require.rb" \ - "$runfile" \ - 1> "$stdout" 2> "$stderr" -code=$? -set -e - -info "├─ stdout: $stdout" -info "├─ stderr: $stderr" -success "└─ exit code: $code" - -shorten_bazel() { - sed -e "s+_bazel_$USER/[^ ]*com_stripe_ruby_typer/+bazel/.../com_stripe_ruby_typer/+" -} - -something_failed= - -echo "" -info "Checking return codes match..." -if [[ "$code" != "$rbcode" ]]; then - error "├─ return codes don't match." - error "├─ Ruby: ${rbcode}" - error "└─ Compiled: ${code}" - something_failed=1 -else - success "└─ codes match." -fi - -echo "" -info "Checking stdouts match..." -if ! diff -au "$rbout" "$stdout" > stdout.diff; then - attn "├─ Diff (interpreted vs compiled)" - < stdout.diff indent_and_nest - info "├─ stdout (interpreted)" - < "$rbout" shorten_bazel | indent_and_nest - info "├─ stdout (compiled)" - < "$stdout" shorten_bazel | indent_and_nest - error "└─ stdouts don't match. See above." - something_failed=1 -else - success "└─ stdouts match." -fi - - -echo "" -info "Checking stderrs match..." - -filter_stderr() { - sed -e '/^SorbetLLVM using compiled/d' | \ - shorten_bazel | \ - sed -e 's+/[^ ]*/tmp\.[[:alnum:]]*+/.../tmp.XXXXXXXXXX+' -} - -if grep -q '^# skip_stderr_check$' "$rbmain"; then - attn "└─ skipping stderr check." -else - filter_stderr < "$rberr" > "$rberr_filtered" - filter_stderr < "$stderr" > "$stderr_filtered" - if ! diff -au "$rberr_filtered" "$stderr_filtered" > stderr.diff; then - attn "├─ Diff (interpreted vs compiled, filtered):" - < stderr.diff indent_and_nest - attn "├─ Full compiled output (except bazel paths shortened):" - < "$stderr" shorten_bazel | indent_and_nest - error "└─ stderrs don't match. See above." - something_failed=1 - else - success "└─ stderrs match." - fi -fi - -if [ -n "$something_failed" ]; then - something_failed -else - if [ -z "$expect_fail" ]; then - echo "" - success "Test passed." - info "├─ stdout (interpreted):" - < "$rbout" indent_and_nest - info "├─ stderr (interpreted):" - < "$rberr" shorten_bazel | indent_and_nest - success "└─ 🎉" - - echo "" - else - echo "" - error "Disabled test did not fail." - info "This could mean that a recent change has made this test start passing." - info "If that's the case, great! Please move this test out of the disabled folder to catch future regressions." - info "├─ stdout (interpreted):" - < "$rbout" indent_and_nest - info "├─ stderr (interpreted):" - < "$rberr" shorten_bazel | indent_and_nest - error "└─ ❌" - - echo "" - exit 1 - fi -fi diff --git a/test/test_single_package_runner.sh b/test/test_single_package_runner.sh index 58534d4b43..3e4d7d17b7 100755 --- a/test/test_single_package_runner.sh +++ b/test/test_single_package_runner.sh @@ -25,9 +25,6 @@ else fi # --- end runfiles.bash initialization --- }}} -# shellcheck source-path=SCRIPTDIR/.. -source "test/logging.sh" - # Argument Parsing ############################################################# test_directory=$1 @@ -37,10 +34,9 @@ test_directory=$1 root="$PWD" sorbet="$(rlocation com_stripe_ruby_typer/main/sorbet)" -ruby="$(rlocation sorbet_ruby_2_7/toolchain/bin/ruby)" -sorbet_runtime="$(dirname $(rlocation com_stripe_ruby_typer/gems/sorbet-runtime/lib/sorbet-runtime.rb))" -rbi_gen_package_runner="$(rlocation com_stripe_ruby_typer/test/rbi_gen_package_runner.rb)" +rbi_gen_package_runner="$(rlocation com_stripe_ruby_typer/test/single_package_runner_cc)" # Main ######################################################################### -exec "$ruby" -I "${sorbet_runtime}" "${rbi_gen_package_runner}" --sorbet "${sorbet}" --root="$root" --test-directory="${test_directory}" +set -x +exec "${rbi_gen_package_runner}" "${sorbet}" "$root" "${test_directory}" diff --git a/test/testdata/autogen/duplicate_refs.rb b/test/testdata/autogen/duplicate_refs.rb new file mode 100644 index 0000000000..54770bcdf8 --- /dev/null +++ b/test/testdata/autogen/duplicate_refs.rb @@ -0,0 +1,12 @@ +# typed: true +class Module; include T::Sig; end + +class A < T::Struct + prop :foo, Integer +end + +class B + # AttrReader rewriter doesn't run in autogen + sig { returns(T.nilable(Integer)) } + attr_reader :bar +end diff --git a/test/testdata/autogen/duplicate_refs.rb.autogen.exp b/test/testdata/autogen/duplicate_refs.rb.autogen.exp new file mode 100644 index 0000000000..0c23499f17 --- /dev/null +++ b/test/testdata/autogen/duplicate_refs.rb.autogen.exp @@ -0,0 +1,82 @@ +# ParsedFile: test/testdata/autogen/duplicate_refs.rb +requires: [] +## defs: +[def id=0] + type=module + defines_behavior=0 + is_empty=0 +[def id=1] + type=class + defines_behavior=1 + is_empty=1 + defining_ref=[Module] +[def id=2] + type=class + defines_behavior=1 + is_empty=0 + defining_ref=[A] + parent_ref=[T Struct] +[def id=3] + type=class + defines_behavior=1 + is_empty=0 + defining_ref=[B] +## refs: +[ref id=0] + scope=[] + name=[Module] + nesting=[] + resolved=[Module] + loc=test/testdata/autogen/duplicate_refs.rb:2 + is_defining_ref=1 +[ref id=1] + scope=[Module] + name=[T Sig] + nesting=[[Module]] + resolved=[T Sig] + loc=test/testdata/autogen/duplicate_refs.rb:2 + is_defining_ref=0 + parent_of=[Module] +[ref id=2] + scope=[] + name=[A] + nesting=[] + resolved=[A] + loc=test/testdata/autogen/duplicate_refs.rb:4 + is_defining_ref=1 +[ref id=3] + scope=[] + name=[T Struct] + nesting=[] + resolved=[T Struct] + loc=test/testdata/autogen/duplicate_refs.rb:4 + is_defining_ref=0 + parent_of=[A] +[ref id=4] + scope=[A] + name=[Integer] + nesting=[[A]] + resolved=[Integer] + loc=test/testdata/autogen/duplicate_refs.rb:5 + is_defining_ref=0 +[ref id=5] + scope=[] + name=[B] + nesting=[] + resolved=[B] + loc=test/testdata/autogen/duplicate_refs.rb:8 + is_defining_ref=1 +[ref id=6] + scope=[B] + name=[T] + nesting=[[B]] + resolved=[T] + loc=test/testdata/autogen/duplicate_refs.rb:10 + is_defining_ref=0 +[ref id=7] + scope=[B] + name=[Integer] + nesting=[[B]] + resolved=[Integer] + loc=test/testdata/autogen/duplicate_refs.rb:10 + is_defining_ref=0 diff --git a/test/testdata/autogen/global_scope.rb b/test/testdata/autogen/global_scope.rb new file mode 100644 index 0000000000..d7e3c796f6 --- /dev/null +++ b/test/testdata/autogen/global_scope.rb @@ -0,0 +1,4 @@ +# typed: true +class Bar +end +FOO = ::Bar diff --git a/test/testdata/autogen/global_scope.rb.autogen.exp b/test/testdata/autogen/global_scope.rb.autogen.exp new file mode 100644 index 0000000000..866ad130b5 --- /dev/null +++ b/test/testdata/autogen/global_scope.rb.autogen.exp @@ -0,0 +1,40 @@ +# ParsedFile: test/testdata/autogen/global_scope.rb +requires: [] +## defs: +[def id=0] + type=module + defines_behavior=0 + is_empty=0 +[def id=1] + type=class + defines_behavior=0 + is_empty=1 + defining_ref=[Bar] +[def id=2] + type=alias + defines_behavior=1 + is_empty=0 + defining_ref=[FOO] + aliased_ref=[Bar] +## refs: +[ref id=0] + scope=[] + name=[Bar] + nesting=[] + resolved=[Bar] + loc=test/testdata/autogen/global_scope.rb:2 + is_defining_ref=1 +[ref id=1] + scope=[] + name=[FOO] + nesting=[] + resolved=[FOO] + loc=test/testdata/autogen/global_scope.rb:4 + is_defining_ref=1 +[ref id=2] + scope=[] + name=[Bar] + nesting=[] + resolved=[Bar] + loc=test/testdata/autogen/global_scope.rb:4 + is_defining_ref=0 diff --git a/test/testdata/cfg/array.rb.cfg-text.exp b/test/testdata/cfg/array.rb.cfg-text.exp index 44d0b7b2e1..01826287f4 100644 --- a/test/testdata/cfg/array.rb.cfg-text.exp +++ b/test/testdata/cfg/array.rb.cfg-text.exp @@ -1,10 +1,7 @@ method ::># { -bb0[rubyRegionId=0, firstDead=5](): +bb0[rubyRegionId=0, firstDead=2](): : T.class_of() = cast(: NilClass, T.class_of()); - $6: T.class_of(Sorbet::Private::Static) = alias - $8: T.class_of(TestArray) = alias - $4: Sorbet::Private::Static::Void = $6: T.class_of(Sorbet::Private::Static).keep_for_ide($8: T.class_of(TestArray)) : T.noreturn = return $2: NilClass -> bb1 diff --git a/test/testdata/cfg/blocks.rb.cfg-text.exp b/test/testdata/cfg/blocks.rb.cfg-text.exp index 34aaaaecf4..1c85c11652 100644 --- a/test/testdata/cfg/blocks.rb.cfg-text.exp +++ b/test/testdata/cfg/blocks.rb.cfg-text.exp @@ -1,10 +1,7 @@ method ::># { -bb0[rubyRegionId=0, firstDead=5](): +bb0[rubyRegionId=0, firstDead=2](): : T.class_of() = cast(: NilClass, T.class_of()); - $6: T.class_of(Sorbet::Private::Static) = alias - $8: T.class_of(BlockTest) = alias - $4: Sorbet::Private::Static::Void = $6: T.class_of(Sorbet::Private::Static).keep_for_ide($8: T.class_of(BlockTest)) : T.noreturn = return $2: NilClass -> bb1 diff --git a/test/testdata/cfg/dealias_with_return.rb.cfg-text.exp b/test/testdata/cfg/dealias_with_return.rb.cfg-text.exp index 66969336cc..596e84587b 100644 --- a/test/testdata/cfg/dealias_with_return.rb.cfg-text.exp +++ b/test/testdata/cfg/dealias_with_return.rb.cfg-text.exp @@ -3,8 +3,8 @@ method ::Object#a { bb0[rubyRegionId=0, firstDead=-1](): : Object = cast(: NilClass, Object); $6: T.class_of() = alias > - $4: T.untyped = - $4 -> (T.untyped ? bb3 : bb4) + $4: T.nilable(Exception) = + $4 -> (T.nilable(Exception) ? bb3 : bb4) # backedges # - bb4(rubyRegionId=1) @@ -16,9 +16,9 @@ bb1[rubyRegionId=0, firstDead=-1](): # backedges # - bb0(rubyRegionId=0) -bb3[rubyRegionId=2, firstDead=-1]($4: T.untyped, $6: T.class_of()): +bb3[rubyRegionId=2, firstDead=-1]($4: Exception, $6: T.class_of()): $9: T.class_of(StandardError) = alias - $10: T::Boolean = $9: T.class_of(StandardError).===($4: T.untyped) + $10: T::Boolean = $9: T.class_of(StandardError).===($4: Exception) $10 -> (T::Boolean ? bb7 : bb8) # backedges @@ -36,7 +36,7 @@ bb6[rubyRegionId=3, firstDead=-1](a: T.nilable(Integer), $11: T.ni # backedges # - bb3(rubyRegionId=2) -bb7[rubyRegionId=2, firstDead=-1]($6: T.class_of()): +bb7[rubyRegionId=2, firstDead=-1]($4: StandardError, $6: T.class_of()): $4: NilClass = nil $7: Sorbet::Private::Static::Void = $6: T.class_of().($4: NilClass) a: Integer(2) = 2 diff --git a/test/testdata/cfg/default_args_cases.rb.cfg-text.exp b/test/testdata/cfg/default_args_cases.rb.cfg-text.exp index 132da1f818..f18245e6d7 100644 --- a/test/testdata/cfg/default_args_cases.rb.cfg-text.exp +++ b/test/testdata/cfg/default_args_cases.rb.cfg-text.exp @@ -1,10 +1,7 @@ method ::># { -bb0[rubyRegionId=0, firstDead=5](): +bb0[rubyRegionId=0, firstDead=2](): : T.class_of() = cast(: NilClass, T.class_of()); - $6: T.class_of(Sorbet::Private::Static) = alias - $8: T.class_of(Test) = alias - $4: Sorbet::Private::Static::Void = $6: T.class_of(Sorbet::Private::Static).keep_for_ide($8: T.class_of(Test)) : T.noreturn = return $2: NilClass -> bb1 diff --git a/test/testdata/cfg/examples.rb.cfg-text.exp b/test/testdata/cfg/examples.rb.cfg-text.exp index 3e1175699f..756eadd052 100644 --- a/test/testdata/cfg/examples.rb.cfg-text.exp +++ b/test/testdata/cfg/examples.rb.cfg-text.exp @@ -1,10 +1,7 @@ method ::># { -bb0[rubyRegionId=0, firstDead=5](): +bb0[rubyRegionId=0, firstDead=2](): : T.class_of() = cast(: NilClass, T.class_of()); - $6: T.class_of(Sorbet::Private::Static) = alias - $8: T.class_of(Examples) = alias - $4: Sorbet::Private::Static::Void = $6: T.class_of(Sorbet::Private::Static).keep_for_ide($8: T.class_of(Examples)) : T.noreturn = return $2: NilClass -> bb1 diff --git a/test/testdata/cfg/examples.rb.flatten-tree.exp b/test/testdata/cfg/examples.rb.flatten-tree.exp index a3fa885a6c..3237f317d6 100644 --- a/test/testdata/cfg/examples.rb.flatten-tree.exp +++ b/test/testdata/cfg/examples.rb.flatten-tree.exp @@ -1,12 +1,9 @@ begin - class <>> < (::) + + def self.<$CENSORED>() - begin - - ::Sorbet::Private::Static.keep_for_ide(::Examples) - - end + end end class ::Examples<> < (::) diff --git a/test/testdata/cfg/hash.rb.cfg-text.exp b/test/testdata/cfg/hash.rb.cfg-text.exp index c5efdf7b30..17f91558e3 100644 --- a/test/testdata/cfg/hash.rb.cfg-text.exp +++ b/test/testdata/cfg/hash.rb.cfg-text.exp @@ -1,10 +1,7 @@ method ::># { -bb0[rubyRegionId=0, firstDead=5](): +bb0[rubyRegionId=0, firstDead=2](): : T.class_of() = cast(: NilClass, T.class_of()); - $6: T.class_of(Sorbet::Private::Static) = alias - $8: T.class_of(TestHash) = alias - $4: Sorbet::Private::Static::Void = $6: T.class_of(Sorbet::Private::Static).keep_for_ide($8: T.class_of(TestHash)) : T.noreturn = return $2: NilClass -> bb1 diff --git a/test/testdata/cfg/ivar_assign.rb.cfg-text.exp b/test/testdata/cfg/ivar_assign.rb.cfg-text.exp index 93eeff9eb5..f92469c70b 100644 --- a/test/testdata/cfg/ivar_assign.rb.cfg-text.exp +++ b/test/testdata/cfg/ivar_assign.rb.cfg-text.exp @@ -1,10 +1,7 @@ method ::># { -bb0[rubyRegionId=0, firstDead=5](): +bb0[rubyRegionId=0, firstDead=2](): : T.class_of() = cast(: NilClass, T.class_of()); - $6: T.class_of(Sorbet::Private::Static) = alias - $8: T.class_of(TestIVar) = alias - $4: Sorbet::Private::Static::Void = $6: T.class_of(Sorbet::Private::Static).keep_for_ide($8: T.class_of(TestIVar)) : T.noreturn = return $2: NilClass -> bb1 diff --git a/test/testdata/cfg/raw_test.rb.cfg-raw.exp b/test/testdata/cfg/raw_test.rb.cfg-raw.exp index 3133848fea..94d9c9c273 100644 --- a/test/testdata/cfg/raw_test.rb.cfg-raw.exp +++ b/test/testdata/cfg/raw_test.rb.cfg-raw.exp @@ -7,7 +7,7 @@ subgraph "cluster_::>#" { color = blue; "bb::>#_0" [ - shape = invhouse; + shape = cds; color = black; label = "block[id=0]()\lBinding {\l bind = VariableUseSite {\l  variable = >,\l  type = T.class_of(),\l },\l value = Cast {\l  cast = T.cast,\l  value = VariableUseSite {\l   variable = >,\l   type = NilClass,\l  },\l  type = T.class_of(),\l },\l}\lBinding {\l bind = VariableUseSite {\l  variable = >$4,\l  type = Integer(0),\l },\l value = Literal { value = Integer(0) },\l}\lBinding {\l bind = VariableUseSite {\l  variable = >$2,\l  type = NilClass,\l },\l value = Send {\l  recv = : T.class_of(),\l  fun = ,\l  args = (VariableUseSite {\l   variable = >$4,\l   type = Integer(0),\l  }),\l },\l}\lBinding {\l bind = VariableUseSite {\l  variable = >,\l  type = T.noreturn,\l },\l value = Return {\l  what = VariableUseSite {\l   variable = >$2,\l   type = NilClass,\l  },\l },\l}\lVariableUseSite { variable = > }\l" ]; diff --git a/test/testdata/cfg/rescue.rb.cfg-text.exp b/test/testdata/cfg/rescue.rb.cfg-text.exp index c0e84d8fa1..b2d61e7bc5 100644 --- a/test/testdata/cfg/rescue.rb.cfg-text.exp +++ b/test/testdata/cfg/rescue.rb.cfg-text.exp @@ -3,8 +3,8 @@ method ::Object#main { bb0[rubyRegionId=0, firstDead=-1](): : Object = cast(: NilClass, Object); $6: T.class_of() = alias > - $3: T.untyped = - $3 -> (T.untyped ? bb3 : bb4) + $3: T.nilable(Exception) = + $3 -> (T.nilable(Exception) ? bb3 : bb4) # backedges # - bb6(rubyRegionId=3) @@ -15,21 +15,21 @@ bb1[rubyRegionId=0, firstDead=-1](): # backedges # - bb0(rubyRegionId=0) # - bb4(rubyRegionId=1) -bb3[rubyRegionId=2, firstDead=-1](: Object, $2: T.untyped, $3: T.untyped, $6: T.class_of()): - $9: T.class_of(StandardError) = alias - $10: T::Boolean = $9: T.class_of(StandardError).===($3: T.untyped) - $10 -> (T::Boolean ? bb7 : bb8) +bb3[rubyRegionId=2, firstDead=-1](: Object, $2: T.untyped, $3: Exception, $6: T.class_of()): + $12: T.class_of(StandardError) = alias + $13: T::Boolean = $12: T.class_of(StandardError).===($3: Exception) + $13 -> (T::Boolean ? bb7 : bb8) # backedges # - bb0(rubyRegionId=0) bb4[rubyRegionId=1, firstDead=-1](: Object, $6: T.class_of()): $2: T.untyped = : Object.a() - $3: T.untyped = - $3 -> (T.untyped ? bb3 : bb5) + $3: T.nilable(Exception) = + $3 -> (T.nilable(Exception) ? bb3 : bb5) # backedges # - bb4(rubyRegionId=1) -bb5[rubyRegionId=4, firstDead=-1](: Object): +bb5[rubyRegionId=4, firstDead=-1](: Object, $3: NilClass): $2: T.untyped = : Object.c() -> bb6 @@ -37,22 +37,24 @@ bb5[rubyRegionId=4, firstDead=-1](: Object): # - bb5(rubyRegionId=4) # - bb7(rubyRegionId=2) # - bb8(rubyRegionId=2) -bb6[rubyRegionId=3, firstDead=-1](: Object, $2: T.untyped, $12: T.nilable(TrueClass)): - $13: T.untyped = : Object.d() - $12 -> (T.nilable(TrueClass) ? bb1 : bb9) +bb6[rubyRegionId=3, firstDead=-1](: Object, $2: T.untyped, $3: T.nilable(Exception), $15: T.nilable(TrueClass)): + $8: T.class_of(T) = alias + $2: T.untyped = $8: T.class_of(T).unsafe($3: T.nilable(Exception)) + $16: T.untyped = : Object.d() + $15 -> (T.nilable(TrueClass) ? bb1 : bb9) # backedges # - bb3(rubyRegionId=2) -bb7[rubyRegionId=2, firstDead=-1](: Object, $6: T.class_of()): +bb7[rubyRegionId=2, firstDead=-1](: Object, $3: StandardError, $6: T.class_of()): $3: NilClass = nil - $7: Sorbet::Private::Static::Void = $6: T.class_of().($3: NilClass) + $10: Sorbet::Private::Static::Void = $6: T.class_of().($3: NilClass) $2: T.untyped = : Object.b() -> bb6 # backedges # - bb3(rubyRegionId=2) -bb8[rubyRegionId=2, firstDead=-1](: Object, $2: T.untyped): - $12: TrueClass = true +bb8[rubyRegionId=2, firstDead=-1](: Object, $2: T.untyped, $3: Exception): + $15: TrueClass = true -> bb6 # backedges diff --git a/test/testdata/cfg/rescue_bad_class.rb.flatten-tree.exp b/test/testdata/cfg/rescue_bad_class.rb.flatten-tree.exp index e5a9db22fa..c51520c38c 100644 --- a/test/testdata/cfg/rescue_bad_class.rb.flatten-tree.exp +++ b/test/testdata/cfg/rescue_bad_class.rb.flatten-tree.exp @@ -1,15 +1,11 @@ -begin - - class <>> < (::) - def foo() - - rescue Unresolved: :: => e - - end +class <>> < (::) + def foo() + + rescue Unresolved: :: => e + + end - def self.<$CENSORED>() - - end + def self.<$CENSORED>() + end - end diff --git a/test/testdata/cfg/rescue_complex.rb b/test/testdata/cfg/rescue_complex.rb index 5b69e41a87..4af604c2a8 100644 --- a/test/testdata/cfg/rescue_complex.rb +++ b/test/testdata/cfg/rescue_complex.rb @@ -11,6 +11,11 @@ def untyped_exceptions(); [Exception]; end sig {returns(T::Array[T.class_of(Exception)])} def typed_exceptions(); [Exception]; end + sig {returns([T.class_of(TypeError), T.class_of(ArgumentError)])} + def tuple_exceptions + [TypeError, ArgumentError] + end + def initialize @ex = T.let(nil, T.nilable(StandardError)) end @@ -42,7 +47,7 @@ def multiple_rescue_classes_varuse() baz end - T.reveal_type(baz) # error: Revealed type: `T.untyped` + T.reveal_type(baz) # error: Revealed type: `T.nilable(T.any(LoadError, SocketError))` end def rescue_loop() @@ -61,7 +66,7 @@ def rescue_untyped_splat() begin meth rescue *untyped_exceptions => e - T.reveal_type(e) # error: Revealed type: `T.untyped` + T.reveal_type(e) # error: Revealed type: `Exception` end end @@ -69,7 +74,15 @@ def rescue_typed_splat() begin meth rescue *typed_exceptions => e - T.reveal_type(e) # error: Revealed type: `T.untyped` + T.reveal_type(e) # error: Revealed type: `Exception` + end + end + + def rescue_typed_splat() + begin + meth + rescue *tuple_exceptions => e + T.reveal_type(e) # error: Revealed type: `Exception` end end diff --git a/test/testdata/cfg/rescue_complex.rb.cfg-text.exp b/test/testdata/cfg/rescue_complex.rb.cfg-text.exp index 80f58a2fc1..8a0849ba80 100644 --- a/test/testdata/cfg/rescue_complex.rb.cfg-text.exp +++ b/test/testdata/cfg/rescue_complex.rb.cfg-text.exp @@ -1,10 +1,7 @@ method ::># { -bb0[rubyRegionId=0, firstDead=5](): +bb0[rubyRegionId=0, firstDead=2](): : T.class_of() = cast(: NilClass, T.class_of()); - $6: T.class_of(Sorbet::Private::Static) = alias - $8: T.class_of(TestRescue) = alias - $4: Sorbet::Private::Static::Void = $6: T.class_of(Sorbet::Private::Static).keep_for_ide($8: T.class_of(TestRescue)) : T.noreturn = return $2: NilClass -> bb1 @@ -125,6 +122,24 @@ bb1[rubyRegionId=0, firstDead=-1](): } +method ::TestRescue#tuple_exceptions { + +bb0[rubyRegionId=0, firstDead=6](): + : TestRescue = cast(: NilClass, TestRescue); + $4: T.class_of(TypeError) = alias + $6: T.class_of(ArgumentError) = alias + $7: T.class_of() = alias > + $2: [T.class_of(TypeError), T.class_of(ArgumentError)] = $7: T.class_of().($4: T.class_of(TypeError), $6: T.class_of(ArgumentError)) + : T.noreturn = return $2: [T.class_of(TypeError), T.class_of(ArgumentError)] + -> bb1 + +# backedges +# - bb0(rubyRegionId=0) +bb1[rubyRegionId=0, firstDead=-1](): + -> bb1 + +} + method ::TestRescue#initialize { bb0[rubyRegionId=0, firstDead=10](): @@ -152,8 +167,8 @@ method ::TestRescue#multiple_rescue { bb0[rubyRegionId=0, firstDead=-1](): : TestRescue = cast(: NilClass, TestRescue); $5: T.class_of() = alias > - $3: T.untyped = - $3 -> (T.untyped ? bb3 : bb4) + $3: T.nilable(Exception) = + $3 -> (T.nilable(Exception) ? bb3 : bb4) # backedges # - bb6(rubyRegionId=3) @@ -164,17 +179,17 @@ bb1[rubyRegionId=0, firstDead=-1](): # backedges # - bb0(rubyRegionId=0) # - bb4(rubyRegionId=1) -bb3[rubyRegionId=2, firstDead=-1](: TestRescue, $2: T.untyped, $3: T.untyped, $5: T.class_of()): +bb3[rubyRegionId=2, firstDead=-1](: TestRescue, $2: T.untyped, $3: Exception, $5: T.class_of()): $8: T.class_of(StandardError) = alias - $9: T::Boolean = $8: T.class_of(StandardError).===($3: T.untyped) + $9: T::Boolean = $8: T.class_of(StandardError).===($3: Exception) $9 -> (T::Boolean ? bb7 : bb8) # backedges # - bb0(rubyRegionId=0) bb4[rubyRegionId=1, firstDead=-1](: TestRescue, $5: T.class_of()): $2: T.untyped = : TestRescue.meth() - $3: T.untyped = - $3 -> (T.untyped ? bb3 : bb5) + $3: T.nilable(Exception) = + $3 -> (T.nilable(Exception) ? bb3 : bb5) # backedges # - bb4(rubyRegionId=1) @@ -191,7 +206,7 @@ bb6[rubyRegionId=3, firstDead=-1]($2: T.untyped, : TestRescue, $5: T.class_of()): +bb7[rubyRegionId=2, firstDead=-1](: TestRescue, $3: StandardError, $5: T.class_of()): $3: NilClass = nil $6: Sorbet::Private::Static::Void = $5: T.class_of().($3: NilClass) $2: T.untyped = : TestRescue.baz() @@ -199,14 +214,14 @@ bb7[rubyRegionId=2, firstDead=-1](: TestRescue, $5: T.class_of(: TestRescue, $2: T.untyped, $3: T.untyped, $5: T.class_of()): +bb8[rubyRegionId=2, firstDead=-1](: TestRescue, $2: T.untyped, $3: Exception, $5: T.class_of()): $13: T.class_of(StandardError) = alias - $14: T::Boolean = $13: T.class_of(StandardError).===($3: T.untyped) + $14: T::Boolean = $13: T.class_of(StandardError).===($3: Exception) $14 -> (T::Boolean ? bb9 : bb10) # backedges # - bb8(rubyRegionId=2) -bb9[rubyRegionId=2, firstDead=-1](: TestRescue, $5: T.class_of()): +bb9[rubyRegionId=2, firstDead=-1](: TestRescue, $3: StandardError, $5: T.class_of()): $3: NilClass = nil $11: Sorbet::Private::Static::Void = $5: T.class_of().($3: NilClass) $2: T.untyped = : TestRescue.bar() @@ -231,8 +246,8 @@ method ::TestRescue#multiple_rescue_classes { bb0[rubyRegionId=0, firstDead=-1](): : TestRescue = cast(: NilClass, TestRescue); $5: T.class_of() = alias > - $3: T.untyped = - $3 -> (T.untyped ? bb3 : bb4) + $3: T.nilable(Exception) = + $3 -> (T.nilable(Exception) ? bb3 : bb4) # backedges # - bb6(rubyRegionId=3) @@ -243,18 +258,17 @@ bb1[rubyRegionId=0, firstDead=-1](): # backedges # - bb0(rubyRegionId=0) # - bb4(rubyRegionId=1) -bb3[rubyRegionId=2, firstDead=-1]($2: T.untyped, $3: T.untyped, $5: T.class_of()): - baz: T.untyped = $3 +bb3[rubyRegionId=2, firstDead=-1]($2: T.untyped, $3: Exception, $5: T.class_of()): $8: T.untyped = alias - $9: T.untyped = $8: T.untyped.===(baz: T.untyped) + $9: T.untyped = $8: T.untyped.===($3: Exception) $9 -> (T.untyped ? bb7 : bb8) # backedges # - bb0(rubyRegionId=0) bb4[rubyRegionId=1, firstDead=-1](: TestRescue, $5: T.class_of()): $2: T.untyped = : TestRescue.meth() - $3: T.untyped = - $3 -> (T.untyped ? bb3 : bb5) + $3: T.nilable(Exception) = + $3 -> (T.nilable(Exception) ? bb3 : bb5) # backedges # - bb4(rubyRegionId=1) @@ -271,17 +285,18 @@ bb6[rubyRegionId=3, firstDead=-1]($2: T.untyped, $5: T.class_of(), baz: T.untyped): +bb7[rubyRegionId=2, firstDead=-1]($3: Exception, $5: T.class_of()): + baz: Exception = $3 $3: NilClass = nil $6: Sorbet::Private::Static::Void = $5: T.class_of().($3: NilClass) - $2: T.untyped = baz + $2: Exception = baz -> bb6 # backedges # - bb3(rubyRegionId=2) -bb8[rubyRegionId=2, firstDead=-1]($2: T.untyped, $5: T.class_of(), baz: T.untyped): +bb8[rubyRegionId=2, firstDead=-1]($2: T.untyped, $3: Exception, $5: T.class_of()): $11: T.untyped = alias - $12: T.untyped = $11: T.untyped.===(baz: T.untyped) + $12: T.untyped = $11: T.untyped.===($3: Exception) $12 -> (T.untyped ? bb7 : bb9) # backedges @@ -303,8 +318,8 @@ method ::TestRescue#multiple_rescue_classes_varuse { bb0[rubyRegionId=0, firstDead=-1](): : TestRescue = cast(: NilClass, TestRescue); $6: T.class_of() = alias > - $4: T.untyped = - $4 -> (T.untyped ? bb3 : bb4) + $4: T.nilable(Exception) = + $4 -> (T.nilable(Exception) ? bb3 : bb4) # backedges # - bb6(rubyRegionId=3) @@ -315,18 +330,17 @@ bb1[rubyRegionId=0, firstDead=-1](): # backedges # - bb0(rubyRegionId=0) # - bb4(rubyRegionId=1) -bb3[rubyRegionId=2, firstDead=-1]($4: T.untyped, $6: T.class_of()): - baz: T.untyped = $4 +bb3[rubyRegionId=2, firstDead=-1]($4: Exception, $6: T.class_of()): $9: T.class_of(LoadError) = alias - $10: T::Boolean = $9: T.class_of(LoadError).===(baz: T.untyped) + $10: T::Boolean = $9: T.class_of(LoadError).===($4: Exception) $10 -> (T::Boolean ? bb7 : bb8) # backedges # - bb0(rubyRegionId=0) bb4[rubyRegionId=1, firstDead=-1](: TestRescue, $6: T.class_of()): $3: T.untyped = : TestRescue.meth() - $4: T.untyped = - $4 -> (T.untyped ? bb3 : bb5) + $4: T.nilable(Exception) = + $4 -> (T.nilable(Exception) ? bb3 : bb5) # backedges # - bb4(rubyRegionId=1) @@ -337,36 +351,37 @@ bb5[rubyRegionId=4, firstDead=-1](): # - bb5(rubyRegionId=4) # - bb7(rubyRegionId=2) # - bb9(rubyRegionId=2) -bb6[rubyRegionId=3, firstDead=-1](baz: T.untyped, $14: T.nilable(TrueClass)): +bb6[rubyRegionId=3, firstDead=-1](baz: T.nilable(T.any(LoadError, SocketError)), $14: T.nilable(TrueClass)): $14 -> (T.nilable(TrueClass) ? bb1 : bb10) # backedges # - bb3(rubyRegionId=2) # - bb8(rubyRegionId=2) -bb7[rubyRegionId=2, firstDead=-1]($6: T.class_of(), baz: T.any(LoadError, SocketError)): +bb7[rubyRegionId=2, firstDead=-1]($4: T.any(LoadError, SocketError), $6: T.class_of()): + baz: T.any(LoadError, SocketError) = $4 $4: NilClass = nil $7: Sorbet::Private::Static::Void = $6: T.class_of().($4: NilClass) -> bb6 # backedges # - bb3(rubyRegionId=2) -bb8[rubyRegionId=2, firstDead=-1]($6: T.class_of(), baz: T.untyped): +bb8[rubyRegionId=2, firstDead=-1]($4: Exception, $6: T.class_of()): $12: T.class_of(SocketError) = alias - $13: T::Boolean = $12: T.class_of(SocketError).===(baz: T.untyped) + $13: T::Boolean = $12: T.class_of(SocketError).===($4: Exception) $13 -> (T::Boolean ? bb7 : bb9) # backedges # - bb8(rubyRegionId=2) -bb9[rubyRegionId=2, firstDead=-1](baz: T.untyped): +bb9[rubyRegionId=2, firstDead=-1](): $14: TrueClass = true -> bb6 # backedges # - bb6(rubyRegionId=3) -bb10[rubyRegionId=0, firstDead=3](baz: T.untyped): +bb10[rubyRegionId=0, firstDead=3](baz: T.nilable(T.any(LoadError, SocketError))): $17: T.class_of(T) = alias - $2: T.untyped = $17: T.class_of(T).reveal_type(baz: T.untyped) - : T.noreturn = return $2: T.untyped + $2: T.nilable(T.any(LoadError, SocketError)) = $17: T.class_of(T).reveal_type(baz: T.nilable(T.any(LoadError, SocketError))) + : T.noreturn = return $2: T.nilable(T.any(LoadError, SocketError)) -> bb1 } @@ -412,17 +427,16 @@ bb5[rubyRegionId=1, firstDead=-1](: TestRescue, ex: T.nilable(StandardErro : TestRescue = loadSelf(loop) ex: NilClass = nil $17: T.class_of() = alias > - $15: T.untyped = - $15 -> (T.untyped ? bb7 : bb8) + $15: T.nilable(Exception) = + $15 -> (T.nilable(Exception) ? bb7 : bb8) # backedges # - bb5(rubyRegionId=1) # - bb8(rubyRegionId=2) -bb7[rubyRegionId=3, firstDead=-1](: TestRescue, ex: NilClass, $11: Sorbet::Private::Static::Void, $12: TestRescue, $13: T.untyped, $15: T.untyped, $17: T.class_of(), $22: NilClass): +bb7[rubyRegionId=3, firstDead=-1](: TestRescue, ex: NilClass, $11: Sorbet::Private::Static::Void, $12: TestRescue, $13: T.untyped, $15: Exception, $17: T.class_of(), $22: NilClass): # outerLoops: 1 - ex: T.untyped = $15 $20: T.class_of(StandardError) = alias - $21: T::Boolean = $20: T.class_of(StandardError).===(ex: T.untyped) + $21: T::Boolean = $20: T.class_of(StandardError).===($15: Exception) $21 -> (T::Boolean ? bb11 : bb12) # backedges @@ -430,8 +444,8 @@ bb7[rubyRegionId=3, firstDead=-1](: TestRescue, ex: NilClass, : TestRescue, ex: NilClass, $11: Sorbet::Private::Static::Void, $12: TestRescue, $17: T.class_of(), $22: NilClass): # outerLoops: 1 $13: T.untyped = : TestRescue.meth() - $15: T.untyped = - $15 -> (T.untyped ? bb7 : bb9) + $15: T.nilable(Exception) = + $15 -> (T.nilable(Exception) ? bb7 : bb9) # backedges # - bb8(rubyRegionId=2) @@ -443,21 +457,22 @@ bb9[rubyRegionId=5, firstDead=-1](: TestRescue, ex: NilClass, : TestRescue, ex: T.untyped, $11: Sorbet::Private::Static::Void, $12: TestRescue, $13: T.untyped, $22: T.nilable(TrueClass)): +bb10[rubyRegionId=4, firstDead=-1](: TestRescue, ex: T.nilable(StandardError), $11: Sorbet::Private::Static::Void, $12: TestRescue, $13: T.untyped, $22: T.nilable(TrueClass)): # outerLoops: 1 $22 -> (T.nilable(TrueClass) ? bb1 : bb13) # backedges # - bb7(rubyRegionId=3) -bb11[rubyRegionId=3, firstDead=-1](: TestRescue, ex: StandardError, $11: Sorbet::Private::Static::Void, $12: TestRescue, $13: T.untyped, $17: T.class_of(), $22: NilClass): +bb11[rubyRegionId=3, firstDead=-1](: TestRescue, ex: NilClass, $11: Sorbet::Private::Static::Void, $12: TestRescue, $13: T.untyped, $15: StandardError, $17: T.class_of(), $22: NilClass): # outerLoops: 1 + ex: StandardError = $15 $15: NilClass = nil $18: Sorbet::Private::Static::Void = $17: T.class_of().($15: NilClass) -> bb10 # backedges # - bb7(rubyRegionId=3) -bb12[rubyRegionId=3, firstDead=-1](: TestRescue, ex: T.untyped, $11: Sorbet::Private::Static::Void, $12: TestRescue, $13: T.untyped): +bb12[rubyRegionId=3, firstDead=-1](: TestRescue, ex: NilClass, $11: Sorbet::Private::Static::Void, $12: TestRescue, $13: T.untyped): # outerLoops: 1 $22: TrueClass = true -> bb10 @@ -476,8 +491,8 @@ method ::TestRescue#rescue_untyped_splat { bb0[rubyRegionId=0, firstDead=-1](): : TestRescue = cast(: NilClass, TestRescue); $5: T.class_of() = alias > - $3: T.untyped = - $3 -> (T.untyped ? bb3 : bb4) + $3: T.nilable(Exception) = + $3 -> (T.nilable(Exception) ? bb3 : bb4) # backedges # - bb6(rubyRegionId=3) @@ -488,20 +503,19 @@ bb1[rubyRegionId=0, firstDead=-1](): # backedges # - bb0(rubyRegionId=0) # - bb4(rubyRegionId=1) -bb3[rubyRegionId=2, firstDead=-1](: TestRescue, $2: T.untyped, $3: T.untyped, $5: T.class_of()): - e: T.untyped = $3 +bb3[rubyRegionId=2, firstDead=-1](: TestRescue, $2: T.untyped, $3: Exception, $5: T.class_of()): $9: T.class_of() = alias > $10: T.untyped = : TestRescue.untyped_exceptions() $7: T.untyped = $9: T.class_of().($10: T.untyped) - $12: T.untyped = $7: T.untyped.===(e: T.untyped) + $12: T.untyped = $7: T.untyped.===($3: Exception) $12 -> (T.untyped ? bb7 : bb8) # backedges # - bb0(rubyRegionId=0) bb4[rubyRegionId=1, firstDead=-1](: TestRescue, $5: T.class_of()): $2: T.untyped = : TestRescue.meth() - $3: T.untyped = - $3 -> (T.untyped ? bb3 : bb5) + $3: T.nilable(Exception) = + $3 -> (T.nilable(Exception) ? bb3 : bb5) # backedges # - bb4(rubyRegionId=1) @@ -517,11 +531,12 @@ bb6[rubyRegionId=3, firstDead=-1]($2: T.untyped, $5: T.class_of(), e: T.untyped): +bb7[rubyRegionId=2, firstDead=-1]($3: Exception, $5: T.class_of()): + e: Exception = $3 $3: NilClass = nil $6: Sorbet::Private::Static::Void = $5: T.class_of().($3: NilClass) $14: T.class_of(T) = alias - $2: T.untyped = $14: T.class_of(T).reveal_type(e: T.untyped) + $2: Exception = $14: T.class_of(T).reveal_type(e: Exception) -> bb6 # backedges @@ -543,8 +558,8 @@ method ::TestRescue#rescue_typed_splat { bb0[rubyRegionId=0, firstDead=-1](): : TestRescue = cast(: NilClass, TestRescue); $5: T.class_of() = alias > - $3: T.untyped = - $3 -> (T.untyped ? bb3 : bb4) + $3: T.nilable(Exception) = + $3 -> (T.nilable(Exception) ? bb3 : bb4) # backedges # - bb6(rubyRegionId=3) @@ -555,20 +570,19 @@ bb1[rubyRegionId=0, firstDead=-1](): # backedges # - bb0(rubyRegionId=0) # - bb4(rubyRegionId=1) -bb3[rubyRegionId=2, firstDead=-1](: TestRescue, $2: T.untyped, $3: T.untyped, $5: T.class_of()): - e: T.untyped = $3 +bb3[rubyRegionId=2, firstDead=-1](: TestRescue, $2: T.untyped, $3: Exception, $5: T.class_of()): $9: T.class_of() = alias > $10: T::Array[T.class_of(Exception)] = : TestRescue.typed_exceptions() $7: T::Array[T.class_of(Exception)] = $9: T.class_of().($10: T::Array[T.class_of(Exception)]) - $12: T::Boolean = $7: T::Array[T.class_of(Exception)].===(e: T.untyped) + $12: T::Boolean = $7: T::Array[T.class_of(Exception)].===($3: Exception) $12 -> (T::Boolean ? bb7 : bb8) # backedges # - bb0(rubyRegionId=0) bb4[rubyRegionId=1, firstDead=-1](: TestRescue, $5: T.class_of()): $2: T.untyped = : TestRescue.meth() - $3: T.untyped = - $3 -> (T.untyped ? bb3 : bb5) + $3: T.nilable(Exception) = + $3 -> (T.nilable(Exception) ? bb3 : bb5) # backedges # - bb4(rubyRegionId=1) @@ -584,11 +598,12 @@ bb6[rubyRegionId=3, firstDead=-1]($2: T.untyped, $5: T.class_of(), e: T.untyped): +bb7[rubyRegionId=2, firstDead=-1]($3: Exception, $5: T.class_of()): + e: Exception = $3 $3: NilClass = nil $6: Sorbet::Private::Static::Void = $5: T.class_of().($3: NilClass) $14: T.class_of(T) = alias - $2: T.untyped = $14: T.class_of(T).reveal_type(e: T.untyped) + $2: Exception = $14: T.class_of(T).reveal_type(e: Exception) -> bb6 # backedges @@ -605,13 +620,13 @@ bb9[rubyRegionId=0, firstDead=1]($2: T.untyped): } -method ::TestRescue#parse_rescue_ensure { +method ::TestRescue#rescue_typed_splat { bb0[rubyRegionId=0, firstDead=-1](): : TestRescue = cast(: NilClass, TestRescue); $5: T.class_of() = alias > - $3: T.untyped = - $3 -> (T.untyped ? bb3 : bb4) + $3: T.nilable(Exception) = + $3 -> (T.nilable(Exception) ? bb3 : bb4) # backedges # - bb6(rubyRegionId=3) @@ -622,43 +637,112 @@ bb1[rubyRegionId=0, firstDead=-1](): # backedges # - bb0(rubyRegionId=0) # - bb4(rubyRegionId=1) -bb3[rubyRegionId=2, firstDead=-1](: TestRescue, $2: T.untyped, $3: T.untyped, $5: T.class_of()): - $8: T.class_of(StandardError) = alias - $9: T::Boolean = $8: T.class_of(StandardError).===($3: T.untyped) - $9 -> (T::Boolean ? bb7 : bb8) +bb3[rubyRegionId=2, firstDead=-1](: TestRescue, $2: T.untyped, $3: Exception, $5: T.class_of()): + $9: T.class_of() = alias > + $10: [T.class_of(TypeError), T.class_of(ArgumentError)] = : TestRescue.tuple_exceptions() + $7: [T.class_of(TypeError), T.class_of(ArgumentError)] = $9: T.class_of().($10: [T.class_of(TypeError), T.class_of(ArgumentError)]) + $12: T::Boolean = $7: [T.class_of(TypeError), T.class_of(ArgumentError)].===($3: Exception) + $12 -> (T::Boolean ? bb7 : bb8) # backedges # - bb0(rubyRegionId=0) bb4[rubyRegionId=1, firstDead=-1](: TestRescue, $5: T.class_of()): $2: T.untyped = : TestRescue.meth() - $3: T.untyped = - $3 -> (T.untyped ? bb3 : bb5) + $3: T.nilable(Exception) = + $3 -> (T.nilable(Exception) ? bb3 : bb5) # backedges # - bb4(rubyRegionId=1) -bb5[rubyRegionId=4, firstDead=-1](: TestRescue, $2: T.untyped): +bb5[rubyRegionId=4, firstDead=-1]($2: T.untyped): -> bb6 # backedges # - bb5(rubyRegionId=4) # - bb7(rubyRegionId=2) # - bb8(rubyRegionId=2) -bb6[rubyRegionId=3, firstDead=-1](: TestRescue, $2: T.untyped, $11: T.nilable(TrueClass)): - $12: T.untyped = : TestRescue.bar() - $11 -> (T.nilable(TrueClass) ? bb1 : bb9) +bb6[rubyRegionId=3, firstDead=-1]($2: T.untyped, $16: T.nilable(TrueClass)): + $16 -> (T.nilable(TrueClass) ? bb1 : bb9) # backedges # - bb3(rubyRegionId=2) -bb7[rubyRegionId=2, firstDead=-1](: TestRescue, $5: T.class_of()): +bb7[rubyRegionId=2, firstDead=-1]($3: Exception, $5: T.class_of()): + e: Exception = $3 $3: NilClass = nil $6: Sorbet::Private::Static::Void = $5: T.class_of().($3: NilClass) + $14: T.class_of(T) = alias + $2: Exception = $14: T.class_of(T).reveal_type(e: Exception) + -> bb6 + +# backedges +# - bb3(rubyRegionId=2) +bb8[rubyRegionId=2, firstDead=-1]($2: T.untyped): + $16: TrueClass = true + -> bb6 + +# backedges +# - bb6(rubyRegionId=3) +bb9[rubyRegionId=0, firstDead=1]($2: T.untyped): + : T.noreturn = return $2: T.untyped + -> bb1 + +} + +method ::TestRescue#parse_rescue_ensure { + +bb0[rubyRegionId=0, firstDead=-1](): + : TestRescue = cast(: NilClass, TestRescue); + $5: T.class_of() = alias > + $3: T.nilable(Exception) = + $3 -> (T.nilable(Exception) ? bb3 : bb4) + +# backedges +# - bb6(rubyRegionId=3) +# - bb9(rubyRegionId=0) +bb1[rubyRegionId=0, firstDead=-1](): + -> bb1 + +# backedges +# - bb0(rubyRegionId=0) +# - bb4(rubyRegionId=1) +bb3[rubyRegionId=2, firstDead=-1](: TestRescue, $2: T.untyped, $3: Exception, $5: T.class_of()): + $11: T.class_of(StandardError) = alias + $12: T::Boolean = $11: T.class_of(StandardError).===($3: Exception) + $12 -> (T::Boolean ? bb7 : bb8) + +# backedges +# - bb0(rubyRegionId=0) +bb4[rubyRegionId=1, firstDead=-1](: TestRescue, $5: T.class_of()): + $2: T.untyped = : TestRescue.meth() + $3: T.nilable(Exception) = + $3 -> (T.nilable(Exception) ? bb3 : bb5) + +# backedges +# - bb4(rubyRegionId=1) +bb5[rubyRegionId=4, firstDead=-1](: TestRescue, $2: T.untyped, $3: NilClass): + -> bb6 + +# backedges +# - bb5(rubyRegionId=4) +# - bb7(rubyRegionId=2) +# - bb8(rubyRegionId=2) +bb6[rubyRegionId=3, firstDead=-1](: TestRescue, $2: T.untyped, $3: T.nilable(Exception), $14: T.nilable(TrueClass)): + $7: T.class_of(T) = alias + $2: T.untyped = $7: T.class_of(T).unsafe($3: T.nilable(Exception)) + $15: T.untyped = : TestRescue.bar() + $14 -> (T.nilable(TrueClass) ? bb1 : bb9) + +# backedges +# - bb3(rubyRegionId=2) +bb7[rubyRegionId=2, firstDead=-1](: TestRescue, $3: StandardError, $5: T.class_of()): + $3: NilClass = nil + $9: Sorbet::Private::Static::Void = $5: T.class_of().($3: NilClass) $2: T.untyped = : TestRescue.baz() -> bb6 # backedges # - bb3(rubyRegionId=2) -bb8[rubyRegionId=2, firstDead=-1](: TestRescue, $2: T.untyped): - $11: TrueClass = true +bb8[rubyRegionId=2, firstDead=-1](: TestRescue, $2: T.untyped, $3: Exception): + $14: TrueClass = true -> bb6 # backedges @@ -674,8 +758,8 @@ method ::TestRescue#parse_bug_rescue_empty_else { bb0[rubyRegionId=0, firstDead=-1](): : TestRescue = cast(: NilClass, TestRescue); $4: T.class_of() = alias > - $3: T.untyped = - $3 -> (T.untyped ? bb3 : bb4) + $3: T.nilable(Exception) = + $3 -> (T.nilable(Exception) ? bb3 : bb4) # backedges # - bb6(rubyRegionId=3) @@ -686,16 +770,16 @@ bb1[rubyRegionId=0, firstDead=-1](): # backedges # - bb0(rubyRegionId=0) # - bb4(rubyRegionId=1) -bb3[rubyRegionId=2, firstDead=-1]($3: T.untyped, $4: T.class_of()): +bb3[rubyRegionId=2, firstDead=-1]($3: Exception, $4: T.class_of()): $7: T.class_of(LoadError) = alias - $8: T::Boolean = $7: T.class_of(LoadError).===($3: T.untyped) + $8: T::Boolean = $7: T.class_of(LoadError).===($3: Exception) $8 -> (T::Boolean ? bb7 : bb8) # backedges # - bb0(rubyRegionId=0) bb4[rubyRegionId=1, firstDead=-1]($4: T.class_of()): - $3: T.untyped = - $3 -> (T.untyped ? bb3 : bb5) + $3: T.nilable(Exception) = + $3 -> (T.nilable(Exception) ? bb3 : bb5) # backedges # - bb4(rubyRegionId=1) @@ -711,7 +795,7 @@ bb6[rubyRegionId=3, firstDead=-1]($9: T.nilable(TrueClass)): # backedges # - bb3(rubyRegionId=2) -bb7[rubyRegionId=2, firstDead=-1]($4: T.class_of()): +bb7[rubyRegionId=2, firstDead=-1]($3: LoadError, $4: T.class_of()): $3: NilClass = nil $5: Sorbet::Private::Static::Void = $4: T.class_of().($3: NilClass) -> bb6 @@ -735,8 +819,8 @@ method ::TestRescue#parse_ruby_bug_12686 { bb0[rubyRegionId=0, firstDead=-1](): : TestRescue = cast(: NilClass, TestRescue); $7: T.class_of() = alias > - $5: T.untyped = - $5 -> (T.untyped ? bb3 : bb4) + $5: T.nilable(Exception) = + $5 -> (T.nilable(Exception) ? bb3 : bb4) # backedges # - bb6(rubyRegionId=3) @@ -747,17 +831,17 @@ bb1[rubyRegionId=0, firstDead=-1](): # backedges # - bb0(rubyRegionId=0) # - bb4(rubyRegionId=1) -bb3[rubyRegionId=2, firstDead=-1](: TestRescue, $4: T.untyped, $5: T.untyped, $7: T.class_of()): +bb3[rubyRegionId=2, firstDead=-1](: TestRescue, $4: T.untyped, $5: Exception, $7: T.class_of()): $10: T.class_of(StandardError) = alias - $11: T::Boolean = $10: T.class_of(StandardError).===($5: T.untyped) + $11: T::Boolean = $10: T.class_of(StandardError).===($5: Exception) $11 -> (T::Boolean ? bb7 : bb8) # backedges # - bb0(rubyRegionId=0) bb4[rubyRegionId=1, firstDead=-1](: TestRescue, $7: T.class_of()): $4: T.untyped = : TestRescue.bar() - $5: T.untyped = - $5 -> (T.untyped ? bb3 : bb5) + $5: T.nilable(Exception) = + $5 -> (T.nilable(Exception) ? bb3 : bb5) # backedges # - bb4(rubyRegionId=1) @@ -773,7 +857,7 @@ bb6[rubyRegionId=3, firstDead=-1](: TestRescue, $4: T.untyped, < # backedges # - bb3(rubyRegionId=2) -bb7[rubyRegionId=2, firstDead=-1](: TestRescue, $7: T.class_of()): +bb7[rubyRegionId=2, firstDead=-1](: TestRescue, $5: StandardError, $7: T.class_of()): $5: NilClass = nil $8: Sorbet::Private::Static::Void = $7: T.class_of().($5: NilClass) $4: NilClass = nil @@ -799,8 +883,8 @@ method ::TestRescue#parse_rescue_mod { bb0[rubyRegionId=0, firstDead=-1](): : TestRescue = cast(: NilClass, TestRescue); $5: T.class_of() = alias > - $3: T.untyped = - $3 -> (T.untyped ? bb3 : bb4) + $3: T.nilable(Exception) = + $3 -> (T.nilable(Exception) ? bb3 : bb4) # backedges # - bb6(rubyRegionId=3) @@ -811,17 +895,17 @@ bb1[rubyRegionId=0, firstDead=-1](): # backedges # - bb0(rubyRegionId=0) # - bb4(rubyRegionId=1) -bb3[rubyRegionId=2, firstDead=-1](: TestRescue, $2: T.untyped, $3: T.untyped, $5: T.class_of()): +bb3[rubyRegionId=2, firstDead=-1](: TestRescue, $2: T.untyped, $3: Exception, $5: T.class_of()): $8: T.class_of(StandardError) = alias - $9: T::Boolean = $8: T.class_of(StandardError).===($3: T.untyped) + $9: T::Boolean = $8: T.class_of(StandardError).===($3: Exception) $9 -> (T::Boolean ? bb7 : bb8) # backedges # - bb0(rubyRegionId=0) bb4[rubyRegionId=1, firstDead=-1](: TestRescue, $5: T.class_of()): $2: T.untyped = : TestRescue.meth() - $3: T.untyped = - $3 -> (T.untyped ? bb3 : bb5) + $3: T.nilable(Exception) = + $3 -> (T.nilable(Exception) ? bb3 : bb5) # backedges # - bb4(rubyRegionId=1) @@ -837,7 +921,7 @@ bb6[rubyRegionId=3, firstDead=-1]($2: T.untyped, : TestRescue, $5: T.class_of()): +bb7[rubyRegionId=2, firstDead=-1](: TestRescue, $3: StandardError, $5: T.class_of()): $3: NilClass = nil $6: Sorbet::Private::Static::Void = $5: T.class_of().($3: NilClass) $2: T.untyped = : TestRescue.bar() @@ -862,8 +946,8 @@ method ::TestRescue#parse_resbody_list_var { bb0[rubyRegionId=0, firstDead=-1](): : TestRescue = cast(: NilClass, TestRescue); $5: T.class_of() = alias > - $3: T.untyped = - $3 -> (T.untyped ? bb3 : bb4) + $3: T.nilable(Exception) = + $3 -> (T.nilable(Exception) ? bb3 : bb4) # backedges # - bb6(rubyRegionId=3) @@ -874,18 +958,17 @@ bb1[rubyRegionId=0, firstDead=-1](): # backedges # - bb0(rubyRegionId=0) # - bb4(rubyRegionId=1) -bb3[rubyRegionId=2, firstDead=-1](: TestRescue, $2: T.untyped, $3: T.untyped, $5: T.class_of()): - ex: T.untyped = $3 +bb3[rubyRegionId=2, firstDead=-1](: TestRescue, $2: T.untyped, $3: Exception, $5: T.class_of()): $7: T.untyped = : TestRescue.foo() - $9: T.untyped = $7: T.untyped.===(ex: T.untyped) + $9: T.untyped = $7: T.untyped.===($3: Exception) $9 -> (T.untyped ? bb7 : bb8) # backedges # - bb0(rubyRegionId=0) bb4[rubyRegionId=1, firstDead=-1](: TestRescue, $5: T.class_of()): $2: T.untyped = : TestRescue.meth() - $3: T.untyped = - $3 -> (T.untyped ? bb3 : bb5) + $3: T.nilable(Exception) = + $3 -> (T.nilable(Exception) ? bb3 : bb5) # backedges # - bb4(rubyRegionId=1) @@ -901,7 +984,7 @@ bb6[rubyRegionId=3, firstDead=-1]($2: T.untyped, : TestRescue, $5: T.class_of()): +bb7[rubyRegionId=2, firstDead=-1](: TestRescue, $3: Exception, $5: T.class_of()): $3: NilClass = nil $6: Sorbet::Private::Static::Void = $5: T.class_of().($3: NilClass) $2: T.untyped = : TestRescue.bar() @@ -926,8 +1009,8 @@ method ::TestRescue#parse_rescue_else_ensure { bb0[rubyRegionId=0, firstDead=-1](): : TestRescue = cast(: NilClass, TestRescue); $6: T.class_of() = alias > - $3: T.untyped = - $3 -> (T.untyped ? bb3 : bb4) + $3: T.nilable(Exception) = + $3 -> (T.nilable(Exception) ? bb3 : bb4) # backedges # - bb6(rubyRegionId=3) @@ -938,21 +1021,21 @@ bb1[rubyRegionId=0, firstDead=-1](): # backedges # - bb0(rubyRegionId=0) # - bb4(rubyRegionId=1) -bb3[rubyRegionId=2, firstDead=-1](: TestRescue, $2: T.untyped, $3: T.untyped, $6: T.class_of()): - $9: T.class_of(StandardError) = alias - $10: T::Boolean = $9: T.class_of(StandardError).===($3: T.untyped) - $10 -> (T::Boolean ? bb7 : bb8) +bb3[rubyRegionId=2, firstDead=-1](: TestRescue, $2: T.untyped, $3: Exception, $6: T.class_of()): + $12: T.class_of(StandardError) = alias + $13: T::Boolean = $12: T.class_of(StandardError).===($3: Exception) + $13 -> (T::Boolean ? bb7 : bb8) # backedges # - bb0(rubyRegionId=0) bb4[rubyRegionId=1, firstDead=-1](: TestRescue, $6: T.class_of()): $2: T.untyped = : TestRescue.meth() - $3: T.untyped = - $3 -> (T.untyped ? bb3 : bb5) + $3: T.nilable(Exception) = + $3 -> (T.nilable(Exception) ? bb3 : bb5) # backedges # - bb4(rubyRegionId=1) -bb5[rubyRegionId=4, firstDead=-1](: TestRescue): +bb5[rubyRegionId=4, firstDead=-1](: TestRescue, $3: NilClass): $2: T.untyped = : TestRescue.foo() -> bb6 @@ -960,22 +1043,24 @@ bb5[rubyRegionId=4, firstDead=-1](: TestRescue): # - bb5(rubyRegionId=4) # - bb7(rubyRegionId=2) # - bb8(rubyRegionId=2) -bb6[rubyRegionId=3, firstDead=-1](: TestRescue, $2: T.untyped, $12: T.nilable(TrueClass)): - $13: T.untyped = : TestRescue.bar() - $12 -> (T.nilable(TrueClass) ? bb1 : bb9) +bb6[rubyRegionId=3, firstDead=-1](: TestRescue, $2: T.untyped, $3: T.nilable(Exception), $15: T.nilable(TrueClass)): + $8: T.class_of(T) = alias + $2: T.untyped = $8: T.class_of(T).unsafe($3: T.nilable(Exception)) + $16: T.untyped = : TestRescue.bar() + $15 -> (T.nilable(TrueClass) ? bb1 : bb9) # backedges # - bb3(rubyRegionId=2) -bb7[rubyRegionId=2, firstDead=-1](: TestRescue, $6: T.class_of()): +bb7[rubyRegionId=2, firstDead=-1](: TestRescue, $3: StandardError, $6: T.class_of()): $3: NilClass = nil - $7: Sorbet::Private::Static::Void = $6: T.class_of().($3: NilClass) + $10: Sorbet::Private::Static::Void = $6: T.class_of().($3: NilClass) $2: T.untyped = : TestRescue.baz() -> bb6 # backedges # - bb3(rubyRegionId=2) -bb8[rubyRegionId=2, firstDead=-1](: TestRescue, $2: T.untyped): - $12: TrueClass = true +bb8[rubyRegionId=2, firstDead=-1](: TestRescue, $2: T.untyped, $3: Exception): + $15: TrueClass = true -> bb6 # backedges @@ -991,8 +1076,8 @@ method ::TestRescue#parse_rescue { bb0[rubyRegionId=0, firstDead=-1](): : TestRescue = cast(: NilClass, TestRescue); $5: T.class_of() = alias > - $3: T.untyped = - $3 -> (T.untyped ? bb3 : bb4) + $3: T.nilable(Exception) = + $3 -> (T.nilable(Exception) ? bb3 : bb4) # backedges # - bb6(rubyRegionId=3) @@ -1003,17 +1088,17 @@ bb1[rubyRegionId=0, firstDead=-1](): # backedges # - bb0(rubyRegionId=0) # - bb4(rubyRegionId=1) -bb3[rubyRegionId=2, firstDead=-1](: TestRescue, $2: T.untyped, $3: T.untyped, $5: T.class_of()): +bb3[rubyRegionId=2, firstDead=-1](: TestRescue, $2: T.untyped, $3: Exception, $5: T.class_of()): $8: T.class_of(StandardError) = alias - $9: T::Boolean = $8: T.class_of(StandardError).===($3: T.untyped) + $9: T::Boolean = $8: T.class_of(StandardError).===($3: Exception) $9 -> (T::Boolean ? bb7 : bb8) # backedges # - bb0(rubyRegionId=0) bb4[rubyRegionId=1, firstDead=-1](: TestRescue, $5: T.class_of()): $2: T.untyped = : TestRescue.meth() - $3: T.untyped = - $3 -> (T.untyped ? bb3 : bb5) + $3: T.nilable(Exception) = + $3 -> (T.nilable(Exception) ? bb3 : bb5) # backedges # - bb4(rubyRegionId=1) @@ -1029,7 +1114,7 @@ bb6[rubyRegionId=3, firstDead=-1]($2: T.untyped, : TestRescue, $5: T.class_of()): +bb7[rubyRegionId=2, firstDead=-1](: TestRescue, $3: StandardError, $5: T.class_of()): $3: NilClass = nil $6: Sorbet::Private::Static::Void = $5: T.class_of().($3: NilClass) $2: T.untyped = : TestRescue.foo() @@ -1054,8 +1139,8 @@ method ::TestRescue#parse_resbody_var { bb0[rubyRegionId=0, firstDead=-1](): : TestRescue = cast(: NilClass, TestRescue); $5: T.class_of() = alias > - $3: T.untyped = - $3 -> (T.untyped ? bb3 : bb4) + $3: T.nilable(Exception) = + $3 -> (T.nilable(Exception) ? bb3 : bb4) # backedges # - bb6(rubyRegionId=3) @@ -1066,18 +1151,17 @@ bb1[rubyRegionId=0, firstDead=-1](): # backedges # - bb0(rubyRegionId=0) # - bb4(rubyRegionId=1) -bb3[rubyRegionId=2, firstDead=-1](: TestRescue, $2: T.untyped, $3: T.untyped, $5: T.class_of()): - ex: T.untyped = $3 +bb3[rubyRegionId=2, firstDead=-1](: TestRescue, $2: T.untyped, $3: Exception, $5: T.class_of()): $8: T.class_of(StandardError) = alias - $9: T::Boolean = $8: T.class_of(StandardError).===(ex: T.untyped) + $9: T::Boolean = $8: T.class_of(StandardError).===($3: Exception) $9 -> (T::Boolean ? bb7 : bb8) # backedges # - bb0(rubyRegionId=0) bb4[rubyRegionId=1, firstDead=-1](: TestRescue, $5: T.class_of()): $2: T.untyped = : TestRescue.meth() - $3: T.untyped = - $3 -> (T.untyped ? bb3 : bb5) + $3: T.nilable(Exception) = + $3 -> (T.nilable(Exception) ? bb3 : bb5) # backedges # - bb4(rubyRegionId=1) @@ -1093,7 +1177,7 @@ bb6[rubyRegionId=3, firstDead=-1]($2: T.untyped, : TestRescue, $5: T.class_of()): +bb7[rubyRegionId=2, firstDead=-1](: TestRescue, $3: StandardError, $5: T.class_of()): $3: NilClass = nil $6: Sorbet::Private::Static::Void = $5: T.class_of().($3: NilClass) $2: T.untyped = : TestRescue.bar() @@ -1119,8 +1203,8 @@ bb0[rubyRegionId=0, firstDead=-1](): @ex$11: T.nilable(StandardError) = alias @ex : TestRescue = cast(: NilClass, TestRescue); $5: T.class_of() = alias > - $3: T.untyped = - $3 -> (T.untyped ? bb3 : bb4) + $3: T.nilable(Exception) = + $3 -> (T.nilable(Exception) ? bb3 : bb4) # backedges # - bb6(rubyRegionId=3) @@ -1131,18 +1215,17 @@ bb1[rubyRegionId=0, firstDead=-1](): # backedges # - bb0(rubyRegionId=0) # - bb4(rubyRegionId=1) -bb3[rubyRegionId=2, firstDead=-1](: TestRescue, $2: T.untyped, $3: T.untyped, $5: T.class_of(), @ex$11: T.nilable(StandardError)): - $2: T.untyped = $3 +bb3[rubyRegionId=2, firstDead=-1](: TestRescue, $2: T.untyped, $3: Exception, $5: T.class_of(), @ex$11: T.nilable(StandardError)): $8: T.class_of(StandardError) = alias - $9: T::Boolean = $8: T.class_of(StandardError).===($3: T.untyped) + $9: T::Boolean = $8: T.class_of(StandardError).===($3: Exception) $9 -> (T::Boolean ? bb7 : bb8) # backedges # - bb0(rubyRegionId=0) bb4[rubyRegionId=1, firstDead=-1](: TestRescue, $5: T.class_of(), @ex$11: T.nilable(StandardError)): $2: T.untyped = : TestRescue.meth() - $3: T.untyped = - $3 -> (T.untyped ? bb3 : bb5) + $3: T.nilable(Exception) = + $3 -> (T.nilable(Exception) ? bb3 : bb5) # backedges # - bb4(rubyRegionId=1) @@ -1158,10 +1241,11 @@ bb6[rubyRegionId=3, firstDead=-1]($2: T.untyped, : TestRescue, $5: T.class_of(), $2: T.untyped, @ex$11: T.nilable(StandardError)): +bb7[rubyRegionId=2, firstDead=-1](: TestRescue, $3: StandardError, $5: T.class_of(), @ex$11: T.nilable(StandardError)): + $2: StandardError = $3 $3: NilClass = nil $6: Sorbet::Private::Static::Void = $5: T.class_of().($3: NilClass) - @ex$11: T.untyped = $2 + @ex$11: StandardError = $2 $2: T.untyped = : TestRescue.bar() -> bb6 @@ -1185,8 +1269,8 @@ bb0[rubyRegionId=0, firstDead=-1](): : TestRescue = cast(: NilClass, TestRescue); $3: NilClass = foo $7: T.class_of() = alias > - $5: T.untyped = - $5 -> (T.untyped ? bb3 : bb4) + $5: T.nilable(Exception) = + $5 -> (T.nilable(Exception) ? bb3 : bb4) # backedges # - bb6(rubyRegionId=3) @@ -1197,17 +1281,17 @@ bb1[rubyRegionId=0, firstDead=-1](): # backedges # - bb0(rubyRegionId=0) # - bb4(rubyRegionId=1) -bb3[rubyRegionId=2, firstDead=-1](: TestRescue, $3: NilClass, $4: T.untyped, $5: T.untyped, $7: T.class_of()): +bb3[rubyRegionId=2, firstDead=-1](: TestRescue, $3: NilClass, $4: T.untyped, $5: Exception, $7: T.class_of()): $10: T.class_of(StandardError) = alias - $11: T::Boolean = $10: T.class_of(StandardError).===($5: T.untyped) + $11: T::Boolean = $10: T.class_of(StandardError).===($5: Exception) $11 -> (T::Boolean ? bb7 : bb8) # backedges # - bb0(rubyRegionId=0) bb4[rubyRegionId=1, firstDead=-1](: TestRescue, $3: NilClass, $7: T.class_of()): $4: T.untyped = : TestRescue.meth() - $5: T.untyped = - $5 -> (T.untyped ? bb3 : bb5) + $5: T.nilable(Exception) = + $5 -> (T.nilable(Exception) ? bb3 : bb5) # backedges # - bb4(rubyRegionId=1) @@ -1223,7 +1307,7 @@ bb6[rubyRegionId=3, firstDead=-1]($3: NilClass, $4: T.untype # backedges # - bb3(rubyRegionId=2) -bb7[rubyRegionId=2, firstDead=-1](: TestRescue, $3: NilClass, $7: T.class_of()): +bb7[rubyRegionId=2, firstDead=-1](: TestRescue, $3: NilClass, $5: StandardError, $7: T.class_of()): $5: NilClass = nil $8: Sorbet::Private::Static::Void = $7: T.class_of().($5: NilClass) $4: T.untyped = : TestRescue.bar() @@ -1250,8 +1334,8 @@ method ::TestRescue#parse_ruby_bug_12402 { bb0[rubyRegionId=0, firstDead=-1](): : TestRescue = cast(: NilClass, TestRescue); $7: T.class_of() = alias > - $3: T.untyped = - $3 -> (T.untyped ? bb3 : bb4) + $3: T.nilable(Exception) = + $3 -> (T.nilable(Exception) ? bb3 : bb4) # backedges # - bb6(rubyRegionId=3) @@ -1262,9 +1346,9 @@ bb1[rubyRegionId=0, firstDead=-1](): # backedges # - bb0(rubyRegionId=0) # - bb4(rubyRegionId=1) -bb3[rubyRegionId=2, firstDead=-1](foo: NilClass, $3: T.untyped, $7: T.class_of()): +bb3[rubyRegionId=2, firstDead=-1](foo: NilClass, $3: Exception, $7: T.class_of()): $10: T.class_of(StandardError) = alias - $11: T::Boolean = $10: T.class_of(StandardError).===($3: T.untyped) + $11: T::Boolean = $10: T.class_of(StandardError).===($3: Exception) $11 -> (T::Boolean ? bb7 : bb8) # backedges @@ -1289,7 +1373,7 @@ bb6[rubyRegionId=3, firstDead=-1](foo: NilClass, $12: T.nilable(Tr # backedges # - bb3(rubyRegionId=2) -bb7[rubyRegionId=2, firstDead=-1]($7: T.class_of()): +bb7[rubyRegionId=2, firstDead=-1]($3: StandardError, $7: T.class_of()): $3: NilClass = nil $8: Sorbet::Private::Static::Void = $7: T.class_of().($3: NilClass) foo: NilClass = nil @@ -1316,8 +1400,8 @@ bb0[rubyRegionId=0, firstDead=-1](): : TestRescue = cast(: NilClass, TestRescue); $3: NilClass = foo $9: T.class_of() = alias > - $5: T.untyped = - $5 -> (T.untyped ? bb3 : bb4) + $5: T.nilable(Exception) = + $5 -> (T.nilable(Exception) ? bb3 : bb4) # backedges # - bb6(rubyRegionId=3) @@ -1328,9 +1412,9 @@ bb1[rubyRegionId=0, firstDead=-1](): # backedges # - bb0(rubyRegionId=0) # - bb4(rubyRegionId=1) -bb3[rubyRegionId=2, firstDead=-1]($3: NilClass, $4: NilClass, $5: T.untyped, $9: T.class_of()): +bb3[rubyRegionId=2, firstDead=-1]($3: NilClass, $4: NilClass, $5: Exception, $9: T.class_of()): $12: T.class_of(StandardError) = alias - $13: T::Boolean = $12: T.class_of(StandardError).===($5: T.untyped) + $13: T::Boolean = $12: T.class_of(StandardError).===($5: Exception) $13 -> (T::Boolean ? bb7 : bb8) # backedges @@ -1355,7 +1439,7 @@ bb6[rubyRegionId=3, firstDead=-1]($3: NilClass, $4: NilClass # backedges # - bb3(rubyRegionId=2) -bb7[rubyRegionId=2, firstDead=-1]($3: NilClass, $9: T.class_of()): +bb7[rubyRegionId=2, firstDead=-1]($3: NilClass, $5: StandardError, $9: T.class_of()): $5: NilClass = nil $10: Sorbet::Private::Static::Void = $9: T.class_of().($5: NilClass) $4: NilClass = nil @@ -1385,8 +1469,8 @@ bb0[rubyRegionId=0, firstDead=-1](): []$4: Integer(0) = 0 $9: T.untyped = []$3: T.untyped.[]([]$4: Integer(0)) $17: T.class_of() = alias > - $13: T.untyped = - $13 -> (T.untyped ? bb3 : bb4) + $13: T.nilable(Exception) = + $13 -> (T.nilable(Exception) ? bb3 : bb4) # backedges # - bb6(rubyRegionId=3) @@ -1397,9 +1481,9 @@ bb1[rubyRegionId=0, firstDead=-1](): # backedges # - bb0(rubyRegionId=0) # - bb4(rubyRegionId=1) -bb3[rubyRegionId=2, firstDead=-1]([]$3: T.untyped, []$4: Integer(0), $9: T.untyped, $12: NilClass, $13: T.untyped, $17: T.class_of()): +bb3[rubyRegionId=2, firstDead=-1]([]$3: T.untyped, []$4: Integer(0), $9: T.untyped, $12: NilClass, $13: Exception, $17: T.class_of()): $20: T.class_of(StandardError) = alias - $21: T::Boolean = $20: T.class_of(StandardError).===($13: T.untyped) + $21: T::Boolean = $20: T.class_of(StandardError).===($13: Exception) $21 -> (T::Boolean ? bb7 : bb8) # backedges @@ -1424,7 +1508,7 @@ bb6[rubyRegionId=3, firstDead=-1]([]$3: T.untyped, []$4: Integer(0), $ # backedges # - bb3(rubyRegionId=2) -bb7[rubyRegionId=2, firstDead=-1]([]$3: T.untyped, []$4: Integer(0), $9: T.untyped, $17: T.class_of()): +bb7[rubyRegionId=2, firstDead=-1]([]$3: T.untyped, []$4: Integer(0), $9: T.untyped, $13: StandardError, $17: T.class_of()): $13: NilClass = nil $18: Sorbet::Private::Static::Void = $17: T.class_of().($13: NilClass) $12: NilClass = nil @@ -1456,7 +1540,7 @@ bb0[rubyRegionId=0, firstDead=-1](): -> bb2 # backedges -# - bb3(rubyRegionId=0) +# - bb7(rubyRegionId=0) bb1[rubyRegionId=0, firstDead=-1](): -> bb1 @@ -1469,14 +1553,13 @@ bb2[rubyRegionId=1, firstDead=-1](: T.class_of(TestRescue), $7: Sorbet::Private::Static::Void, $8: T.class_of(TestRescue)): +bb3[rubyRegionId=0, firstDead=-1]($7: Sorbet::Private::Static::Void, $8: T.class_of(TestRescue)): $3: Sorbet::Private::Static::Void = Solve<$7, sig> : T.class_of(TestRescue) = $8 - $25: T.class_of(T::Sig) = alias - $27: T.class_of(T) = alias - $22: T.class_of(TestRescue) = : T.class_of(TestRescue).extend($25: T.class_of(T::Sig)) - : T.noreturn = return $2: NilClass - -> bb1 + $24: T.class_of(Sorbet::Private::Static) = alias + $26: Sorbet::Private::Static::Void = $24: T.class_of(Sorbet::Private::Static).sig(: T.class_of(TestRescue)) + $27: T.class_of(TestRescue) = + -> bb6 # backedges # - bb2(rubyRegionId=1) @@ -1493,5 +1576,40 @@ bb5[rubyRegionId=1, firstDead=9](: T.class_of(TestRescue), $21: T.noreturn = blockreturn $9: T::Private::Methods::DeclBuilder -> bb2 +# backedges +# - bb3(rubyRegionId=0) +# - bb9(rubyRegionId=2) +bb6[rubyRegionId=2, firstDead=-1](: T.class_of(TestRescue), $26: Sorbet::Private::Static::Void, $27: T.class_of(TestRescue)): + # outerLoops: 1 + -> (NilClass ? bb9 : bb7) + +# backedges +# - bb6(rubyRegionId=2) +bb7[rubyRegionId=0, firstDead=6]($26: Sorbet::Private::Static::Void, $27: T.class_of(TestRescue)): + $22: Sorbet::Private::Static::Void = Solve<$26, sig> + : T.class_of(TestRescue) = $27 + $46: T.class_of(T::Sig) = alias + $48: T.class_of(T) = alias + $43: T.class_of(TestRescue) = : T.class_of(TestRescue).extend($46: T.class_of(T::Sig)) + : T.noreturn = return $2: NilClass + -> bb1 + +# backedges +# - bb6(rubyRegionId=2) +bb9[rubyRegionId=2, firstDead=11](: T.class_of(TestRescue), $26: Sorbet::Private::Static::Void, $27: T.class_of(TestRescue)): + # outerLoops: 1 + : T::Private::Methods::DeclBuilder = loadSelf(sig) + $33: T.class_of(T) = alias + $35: T.class_of(TypeError) = alias + $31: Runtime object representing type: T.class_of(TypeError) = $33: T.class_of(T).class_of($35: T.class_of(TypeError)) + $38: T.class_of(T) = alias + $40: T.class_of(ArgumentError) = alias + $36: Runtime object representing type: T.class_of(ArgumentError) = $38: T.class_of(T).class_of($40: T.class_of(ArgumentError)) + $41: T.class_of() = alias > + $30: [Runtime object representing type: T.class_of(TypeError), Runtime object representing type: T.class_of(ArgumentError)] = $41: T.class_of().($31: Runtime object representing type: T.class_of(TypeError), $36: Runtime object representing type: T.class_of(ArgumentError)) + $28: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.returns($30: [Runtime object representing type: T.class_of(TypeError), Runtime object representing type: T.class_of(ArgumentError)]) + $42: T.noreturn = blockreturn $28: T::Private::Methods::DeclBuilder + -> bb6 + } diff --git a/test/testdata/cfg/rescue_complex.rb.desugar-tree.exp b/test/testdata/cfg/rescue_complex.rb.desugar-tree.exp index bf8a602210..e812ee7ea0 100644 --- a/test/testdata/cfg/rescue_complex.rb.desugar-tree.exp +++ b/test/testdata/cfg/rescue_complex.rb.desugar-tree.exp @@ -34,6 +34,14 @@ class <>> < (::) [::] end + .sig() do || + .returns([::.class_of(::), ::.class_of(::)]) + end + + def tuple_exceptions<>(&) + [::, ::] + end + def initialize<>(&) @ex = ::.let(nil, ::.nilable(::)) end @@ -87,6 +95,12 @@ class <>> < (::) ::.reveal_type(e) end + def rescue_typed_splat<>(&) + .meth() + rescue ::.(.tuple_exceptions()) => e + ::.reveal_type(e) + end + def parse_rescue_ensure<>(&) .meth() rescue => $2 diff --git a/test/testdata/cfg/rescue_else_block.rb.cfg-text.exp b/test/testdata/cfg/rescue_else_block.rb.cfg-text.exp index 65d38f054c..0d25d301ed 100644 --- a/test/testdata/cfg/rescue_else_block.rb.cfg-text.exp +++ b/test/testdata/cfg/rescue_else_block.rb.cfg-text.exp @@ -3,8 +3,8 @@ method ::Object#foo { bb0[rubyRegionId=0, firstDead=-1](): : Object = cast(: NilClass, Object); $5: T.class_of() = alias > - $3: T.untyped = - $3 -> (T.untyped ? bb3 : bb4) + $3: T.nilable(Exception) = + $3 -> (T.nilable(Exception) ? bb3 : bb4) # backedges # - bb9(rubyRegionId=3) @@ -15,17 +15,17 @@ bb1[rubyRegionId=0, firstDead=-1](): # backedges # - bb0(rubyRegionId=0) # - bb4(rubyRegionId=1) -bb3[rubyRegionId=2, firstDead=-1]($2: T.nilable(Integer), $3: T.untyped, $5: T.class_of()): +bb3[rubyRegionId=2, firstDead=-1]($2: T.nilable(Integer), $3: Exception, $5: T.class_of()): $8: T.class_of(StandardError) = alias - $9: T::Boolean = $8: T.class_of(StandardError).===($3: T.untyped) + $9: T::Boolean = $8: T.class_of(StandardError).===($3: Exception) $9 -> (T::Boolean ? bb10 : bb11) # backedges # - bb0(rubyRegionId=0) bb4[rubyRegionId=1, firstDead=-1]($5: T.class_of()): $2: Integer(1) = 1 - $3: T.untyped = - $3 -> (T.untyped ? bb3 : bb5) + $3: T.nilable(Exception) = + $3 -> (T.nilable(Exception) ? bb3 : bb5) # backedges # - bb4(rubyRegionId=1) @@ -49,7 +49,7 @@ bb9[rubyRegionId=3, firstDead=-1]($2: T.nilable(Integer), $2: T.nilable(Integer), $5: T.class_of()): +bb10[rubyRegionId=2, firstDead=-1]($2: T.nilable(Integer), $3: StandardError, $5: T.class_of()): $3: NilClass = nil $6: Sorbet::Private::Static::Void = $5: T.class_of().($3: NilClass) -> bb9 diff --git a/test/testdata/cfg/rescue_expression.rb.cfg-text.exp b/test/testdata/cfg/rescue_expression.rb.cfg-text.exp index 5146009054..d3a59b709d 100644 --- a/test/testdata/cfg/rescue_expression.rb.cfg-text.exp +++ b/test/testdata/cfg/rescue_expression.rb.cfg-text.exp @@ -3,8 +3,8 @@ method ::Object#foo { bb0[rubyRegionId=0, firstDead=-1](): : Object = cast(: NilClass, Object); $8: T.class_of() = alias > - $3: T.untyped = - $3 -> (T.untyped ? bb3 : bb4) + $3: T.nilable(Exception) = + $3 -> (T.nilable(Exception) ? bb3 : bb4) # backedges # - bb6(rubyRegionId=3) @@ -15,12 +15,11 @@ bb1[rubyRegionId=0, firstDead=-1](): # backedges # - bb0(rubyRegionId=0) # - bb4(rubyRegionId=1) -bb3[rubyRegionId=2, firstDead=-1]($2: NilClass, $3: T.untyped, $8: T.class_of()): - e: T.untyped = $3 +bb3[rubyRegionId=2, firstDead=-1]($2: NilClass, $3: Exception, $8: T.class_of()): $13: T.class_of(MyException) = alias $11: MyException = $13: T.class_of(MyException).new() $10: T.class_of(MyException) = $11: MyException.class() - $14: T::Boolean = $10: T.class_of(MyException).===(e: T.untyped) + $14: T::Boolean = $10: T.class_of(MyException).===($3: Exception) $14 -> (T::Boolean ? bb7 : bb8) # backedges @@ -46,7 +45,7 @@ bb6[rubyRegionId=3, firstDead=-1]($2: T.nilable(Integer), $8: T.class_of()): +bb7[rubyRegionId=2, firstDead=-1]($3: MyException, $8: T.class_of()): $3: NilClass = nil $9: Sorbet::Private::Static::Void = $8: T.class_of().($3: NilClass) $2: Integer(3) = 3 @@ -68,16 +67,10 @@ bb9[rubyRegionId=0, firstDead=1]($2: Integer(3)): method ::># { -bb0[rubyRegionId=0, firstDead=10](): +bb0[rubyRegionId=0, firstDead=4](): : T.class_of() = cast(: NilClass, T.class_of()); - $7: T.class_of(Sorbet::Private::Static) = alias - $9: T.class_of(MyException) = alias - $5: Sorbet::Private::Static::Void = $7: T.class_of(Sorbet::Private::Static).keep_for_ide($9: T.class_of(MyException)) - $12: T.class_of(Sorbet::Private::Static) = alias - $14: T.class_of(Exception) = alias - $10: Sorbet::Private::Static::Void = $12: T.class_of(Sorbet::Private::Static).keep_for_ide($14: T.class_of(Exception)) - $18: T.untyped = : T.class_of().foo() - $16: NilClass = : T.class_of().puts($18: T.untyped) + $6: T.untyped = : T.class_of().foo() + $4: NilClass = : T.class_of().puts($6: T.untyped) : T.noreturn = return $2: NilClass -> bb1 diff --git a/test/testdata/cfg/rescue_two_return.rb.cfg-text.exp b/test/testdata/cfg/rescue_two_return.rb.cfg-text.exp index 7817fc7d47..5b156ac7cb 100644 --- a/test/testdata/cfg/rescue_two_return.rb.cfg-text.exp +++ b/test/testdata/cfg/rescue_two_return.rb.cfg-text.exp @@ -3,8 +3,8 @@ method ::Object#foo { bb0[rubyRegionId=0, firstDead=-1](): : Object = cast(: NilClass, Object); $6: T.class_of() = alias > - $4: T.untyped = - $4 -> (T.untyped ? bb3 : bb4) + $4: T.nilable(Exception) = + $4 -> (T.nilable(Exception) ? bb3 : bb4) # backedges # - bb4(rubyRegionId=1) @@ -17,9 +17,9 @@ bb1[rubyRegionId=0, firstDead=-1](): # backedges # - bb0(rubyRegionId=0) -bb3[rubyRegionId=2, firstDead=-1](: Object, $4: T.untyped, $6: T.class_of()): +bb3[rubyRegionId=2, firstDead=-1](: Object, $4: Exception, $6: T.class_of()): $9: T.class_of(StandardError) = alias - $10: T::Boolean = $9: T.class_of(StandardError).===($4: T.untyped) + $10: T::Boolean = $9: T.class_of(StandardError).===($4: Exception) $10 -> (T::Boolean ? bb7 : bb8) # backedges @@ -36,7 +36,7 @@ bb6[rubyRegionId=3, firstDead=-1](: Object, $12: TrueClass): # backedges # - bb3(rubyRegionId=2) -bb7[rubyRegionId=2, firstDead=4]($6: T.class_of()): +bb7[rubyRegionId=2, firstDead=4]($4: StandardError, $6: T.class_of()): $4: NilClass = nil $7: Sorbet::Private::Static::Void = $6: T.class_of().($4: NilClass) $11: Integer(2) = 2 diff --git a/test/testdata/cfg/rescue_var_expression.rb.cfg-text.exp b/test/testdata/cfg/rescue_var_expression.rb.cfg-text.exp index cc85c5d1cf..6ad09f3c91 100644 --- a/test/testdata/cfg/rescue_var_expression.rb.cfg-text.exp +++ b/test/testdata/cfg/rescue_var_expression.rb.cfg-text.exp @@ -3,8 +3,8 @@ method ::Object#foo { bb0[rubyRegionId=0, firstDead=-1](): : Object = cast(: NilClass, Object); $6: T.class_of() = alias > - $3: T.untyped = - $3 -> (T.untyped ? bb3 : bb4) + $3: T.nilable(Exception) = + $3 -> (T.nilable(Exception) ? bb3 : bb4) # backedges # - bb6(rubyRegionId=3) @@ -15,11 +15,10 @@ bb1[rubyRegionId=0, firstDead=-1](): # backedges # - bb0(rubyRegionId=0) # - bb4(rubyRegionId=1) -bb3[rubyRegionId=2, firstDead=-1]($2: NilClass, $3: T.untyped, $6: T.class_of()): - $2: T.untyped = $3 +bb3[rubyRegionId=2, firstDead=-1]($2: NilClass, $3: Exception, $6: T.class_of()): $9: T.class_of(Exception) = alias - $10: T::Boolean = $9: T.class_of(Exception).===($3: T.untyped) - $10 -> (T::Boolean ? bb7 : bb8) + $10: TrueClass = $9: T.class_of(Exception).===($3: Exception) + $10 -> (TrueClass ? bb7 : bb8) # backedges # - bb0(rubyRegionId=0) @@ -38,24 +37,25 @@ bb5[rubyRegionId=4, firstDead=0]($2: NilClass): # - bb5(rubyRegionId=4) # - bb7(rubyRegionId=2) # - bb8(rubyRegionId=2) -bb6[rubyRegionId=3, firstDead=-1]($2: T.nilable(Integer), $16: T.nilable(TrueClass)): - $16 -> (T.nilable(TrueClass) ? bb1 : bb9) +bb6[rubyRegionId=3, firstDead=-1]($2: Integer(3), $16: NilClass): + $16 -> (NilClass ? bb1 : bb9) # backedges # - bb3(rubyRegionId=2) -bb7[rubyRegionId=2, firstDead=-1]($6: T.class_of(), $2: T.untyped): +bb7[rubyRegionId=2, firstDead=-1]($3: Exception, $6: T.class_of()): + $2: Exception = $3 $3: NilClass = nil $7: Sorbet::Private::Static::Void = $6: T.class_of().($3: NilClass) $14: T.class_of(MyClass) = alias $12: MyClass = $14: T.class_of(MyClass).new() - $11: T.untyped = $12: MyClass.foo=($2: T.untyped) + $11: Exception = $12: MyClass.foo=($2: Exception) $2: Integer(3) = 3 -> bb6 # backedges # - bb3(rubyRegionId=2) -bb8[rubyRegionId=2, firstDead=-1]($2: NilClass): - $16: TrueClass = true +bb8[rubyRegionId=2, firstDead=0]($2: NilClass): + $16 = true -> bb6 # backedges @@ -68,13 +68,10 @@ bb9[rubyRegionId=0, firstDead=1]($2: Integer(3)): method ::># { -bb0[rubyRegionId=0, firstDead=7](): +bb0[rubyRegionId=0, firstDead=4](): : T.class_of() = cast(: NilClass, T.class_of()); - $7: T.class_of(Sorbet::Private::Static) = alias - $9: T.class_of(MyClass) = alias - $5: Sorbet::Private::Static::Void = $7: T.class_of(Sorbet::Private::Static).keep_for_ide($9: T.class_of(MyClass)) - $13: T.untyped = : T.class_of().foo() - $11: NilClass = : T.class_of().puts($13: T.untyped) + $6: T.untyped = : T.class_of().foo() + $4: NilClass = : T.class_of().puts($6: T.untyped) : T.noreturn = return $2: NilClass -> bb1 diff --git a/test/testdata/cfg/rescue_with_return.rb.cfg-text.exp b/test/testdata/cfg/rescue_with_return.rb.cfg-text.exp index 7cf6486067..f0f4ef4e17 100644 --- a/test/testdata/cfg/rescue_with_return.rb.cfg-text.exp +++ b/test/testdata/cfg/rescue_with_return.rb.cfg-text.exp @@ -3,8 +3,8 @@ method ::Object#a { bb0[rubyRegionId=0, firstDead=-1](): : Object = cast(: NilClass, Object); $5: T.class_of() = alias > - $3: T.untyped = - $3 -> (T.untyped ? bb3 : bb4) + $3: T.nilable(Exception) = + $3 -> (T.nilable(Exception) ? bb3 : bb4) # backedges # - bb4(rubyRegionId=1) @@ -16,9 +16,9 @@ bb1[rubyRegionId=0, firstDead=-1](): # backedges # - bb0(rubyRegionId=0) -bb3[rubyRegionId=2, firstDead=-1]($3: T.untyped, $5: T.class_of()): +bb3[rubyRegionId=2, firstDead=-1]($3: Exception, $5: T.class_of()): $8: T.class_of(StandardError) = alias - $9: T::Boolean = $8: T.class_of(StandardError).===($3: T.untyped) + $9: T::Boolean = $8: T.class_of(StandardError).===($3: Exception) $9 -> (T::Boolean ? bb7 : bb8) # backedges @@ -36,7 +36,7 @@ bb6[rubyRegionId=3, firstDead=-1]($10: T.nilable(TrueClass)): # backedges # - bb3(rubyRegionId=2) -bb7[rubyRegionId=2, firstDead=-1]($5: T.class_of()): +bb7[rubyRegionId=2, firstDead=-1]($3: StandardError, $5: T.class_of()): $3: NilClass = nil $6: Sorbet::Private::Static::Void = $5: T.class_of().($3: NilClass) -> bb6 diff --git a/test/testdata/cfg/retry.rb.cfg-text.exp b/test/testdata/cfg/retry.rb.cfg-text.exp index 1f5a6ffe97..4249b6408a 100644 --- a/test/testdata/cfg/retry.rb.cfg-text.exp +++ b/test/testdata/cfg/retry.rb.cfg-text.exp @@ -16,15 +16,15 @@ bb1[rubyRegionId=0, firstDead=-1](): # - bb0(rubyRegionId=0) # - bb10(rubyRegionId=2) bb2[rubyRegionId=0, firstDead=-1](: Object, $2: NilClass, try: Integer(0), $13: T.class_of()): - $4: T.untyped = - $4 -> (T.untyped ? bb3 : bb4) + $4: T.nilable(Exception) = + $4 -> (T.nilable(Exception) ? bb3 : bb4) # backedges # - bb2(rubyRegionId=0) # - bb7(rubyRegionId=1) -bb3[rubyRegionId=2, firstDead=-1](: Object, $2: NilClass, try: Integer(0), $4: T.untyped, $13: T.class_of()): +bb3[rubyRegionId=2, firstDead=-1](: Object, $2: NilClass, try: Integer(0), $4: Exception, $13: T.class_of()): $16: T.class_of(StandardError) = alias - $17: T::Boolean = $16: T.class_of(StandardError).===($4: T.untyped) + $17: T::Boolean = $16: T.class_of(StandardError).===($4: Exception) $17 -> (T::Boolean ? bb10 : bb11) # backedges @@ -48,8 +48,8 @@ bb5[rubyRegionId=1, firstDead=5](: Object, try: Integer(0), $13: T. # - bb4(rubyRegionId=1) # - bb5(rubyRegionId=1) bb7[rubyRegionId=1, firstDead=-1](: Object, $2: NilClass, try: Integer(0), $13: T.class_of()): - $4: T.untyped = - $4 -> (T.untyped ? bb3 : bb8) + $4: T.nilable(Exception) = + $4 -> (T.nilable(Exception) ? bb3 : bb8) # backedges # - bb7(rubyRegionId=1) @@ -64,7 +64,7 @@ bb9[rubyRegionId=3, firstDead=-1]($2: NilClass, # backedges # - bb3(rubyRegionId=2) -bb10[rubyRegionId=2, firstDead=-1](: Object, $2: NilClass, try: Integer(0), $13: T.class_of()): +bb10[rubyRegionId=2, firstDead=-1](: Object, $2: NilClass, try: Integer(0), $4: StandardError, $13: T.class_of()): $4: NilClass = nil $14: Sorbet::Private::Static::Void = $13: T.class_of().($4: NilClass) $20: String("rescue") = "rescue" diff --git a/test/testdata/cfg/retry_multiple.rb.cfg-text.exp b/test/testdata/cfg/retry_multiple.rb.cfg-text.exp index 297f8a2321..f0586d7d96 100644 --- a/test/testdata/cfg/retry_multiple.rb.cfg-text.exp +++ b/test/testdata/cfg/retry_multiple.rb.cfg-text.exp @@ -17,15 +17,15 @@ bb1[rubyRegionId=0, firstDead=-1](): # - bb13(rubyRegionId=2) # - bb15(rubyRegionId=2) bb2[rubyRegionId=0, firstDead=-1](: Object, $2: NilClass, try: Integer(0), $25: T.class_of()): - $4: T.untyped = - $4 -> (T.untyped ? bb3 : bb4) + $4: T.nilable(Exception) = + $4 -> (T.nilable(Exception) ? bb3 : bb4) # backedges # - bb2(rubyRegionId=0) # - bb10(rubyRegionId=1) -bb3[rubyRegionId=2, firstDead=-1](: Object, $2: NilClass, try: Integer(0), $4: T.untyped, $25: T.class_of()): +bb3[rubyRegionId=2, firstDead=-1](: Object, $2: NilClass, try: Integer(0), $4: Exception, $25: T.class_of()): $28: T.class_of(A) = alias - $29: T::Boolean = $28: T.class_of(A).===($4: T.untyped) + $29: T::Boolean = $28: T.class_of(A).===($4: Exception) $29 -> (T::Boolean ? bb13 : bb14) # backedges @@ -69,8 +69,8 @@ bb7[rubyRegionId=1, firstDead=6](: Object, try: Integer(0), $25: T. # - bb6(rubyRegionId=1) # - bb7(rubyRegionId=1) bb10[rubyRegionId=1, firstDead=-1](: Object, $2: NilClass, try: Integer(0), $25: T.class_of()): - $4: T.untyped = - $4 -> (T.untyped ? bb3 : bb11) + $4: T.nilable(Exception) = + $4 -> (T.nilable(Exception) ? bb3 : bb11) # backedges # - bb10(rubyRegionId=1) @@ -85,7 +85,7 @@ bb12[rubyRegionId=3, firstDead=-1]($2: NilClass, : Object, $2: NilClass, try: Integer(0), $25: T.class_of()): +bb13[rubyRegionId=2, firstDead=-1](: Object, $2: NilClass, try: Integer(0), $4: A, $25: T.class_of()): $4: NilClass = nil $26: Sorbet::Private::Static::Void = $25: T.class_of().($4: NilClass) $32: String("rescue A ") = "rescue A " @@ -96,14 +96,14 @@ bb13[rubyRegionId=2, firstDead=-1](: Object, $2: NilClas # backedges # - bb3(rubyRegionId=2) -bb14[rubyRegionId=2, firstDead=-1](: Object, $2: NilClass, try: Integer(0), $4: T.untyped, $25: T.class_of()): +bb14[rubyRegionId=2, firstDead=-1](: Object, $2: NilClass, try: Integer(0), $4: Exception, $25: T.class_of()): $38: T.class_of(B) = alias - $39: T::Boolean = $38: T.class_of(B).===($4: T.untyped) + $39: T::Boolean = $38: T.class_of(B).===($4: Exception) $39 -> (T::Boolean ? bb15 : bb16) # backedges # - bb14(rubyRegionId=2) -bb15[rubyRegionId=2, firstDead=-1](: Object, $2: NilClass, try: Integer(0), $25: T.class_of()): +bb15[rubyRegionId=2, firstDead=-1](: Object, $2: NilClass, try: Integer(0), $4: B, $25: T.class_of()): $4: NilClass = nil $36: Sorbet::Private::Static::Void = $25: T.class_of().($4: NilClass) $42: String("rescue B ") = "rescue B " @@ -128,21 +128,9 @@ bb17[rubyRegionId=0, firstDead=1]($2: NilClass): method ::># { -bb0[rubyRegionId=0, firstDead=15](): +bb0[rubyRegionId=0, firstDead=3](): : T.class_of() = cast(: NilClass, T.class_of()); - $7: T.class_of(Sorbet::Private::Static) = alias - $9: T.class_of(A) = alias - $5: Sorbet::Private::Static::Void = $7: T.class_of(Sorbet::Private::Static).keep_for_ide($9: T.class_of(A)) - $12: T.class_of(Sorbet::Private::Static) = alias - $14: T.class_of(Exception) = alias - $10: Sorbet::Private::Static::Void = $12: T.class_of(Sorbet::Private::Static).keep_for_ide($14: T.class_of(Exception)) - $19: T.class_of(Sorbet::Private::Static) = alias - $21: T.class_of(B) = alias - $17: Sorbet::Private::Static::Void = $19: T.class_of(Sorbet::Private::Static).keep_for_ide($21: T.class_of(B)) - $24: T.class_of(Sorbet::Private::Static) = alias - $26: T.class_of(Exception) = alias - $22: Sorbet::Private::Static::Void = $24: T.class_of(Sorbet::Private::Static).keep_for_ide($26: T.class_of(Exception)) - $28: T.untyped = : T.class_of().main() + $4: T.untyped = : T.class_of().main() : T.noreturn = return $2: NilClass -> bb1 diff --git a/test/testdata/cfg/retry_nested.rb.cfg-text.exp b/test/testdata/cfg/retry_nested.rb.cfg-text.exp index 3b41fb2c3d..6daa8eb2e6 100644 --- a/test/testdata/cfg/retry_nested.rb.cfg-text.exp +++ b/test/testdata/cfg/retry_nested.rb.cfg-text.exp @@ -17,15 +17,15 @@ bb1[rubyRegionId=0, firstDead=-1](): # - bb0(rubyRegionId=0) # - bb21(rubyRegionId=2) bb2[rubyRegionId=0, firstDead=-1](: Object, $2: NilClass, try: Integer(0), $40: NilClass, $42: T.class_of()): - $4: T.untyped = - $4 -> (T.untyped ? bb3 : bb4) + $4: T.nilable(Exception) = + $4 -> (T.nilable(Exception) ? bb3 : bb4) # backedges # - bb2(rubyRegionId=0) # - bb18(rubyRegionId=1) -bb3[rubyRegionId=2, firstDead=-1](: Object, $2: NilClass, try: Integer(0), $4: T.untyped, $40: NilClass, $42: T.class_of()): +bb3[rubyRegionId=2, firstDead=-1](: Object, $2: NilClass, try: Integer(0), $4: Exception, $40: NilClass, $42: T.class_of()): $45: T.class_of(B) = alias - $46: T::Boolean = $45: T.class_of(B).===($4: T.untyped) + $46: T::Boolean = $45: T.class_of(B).===($4: Exception) $46 -> (T::Boolean ? bb21 : bb22) # backedges @@ -40,15 +40,15 @@ bb4[rubyRegionId=1, firstDead=-1](: Object, $2: NilClass # - bb4(rubyRegionId=1) # - bb16(rubyRegionId=6) bb5[rubyRegionId=1, firstDead=-1](: Object, $2: NilClass, try: Integer(0), $29: T.class_of(), $40: NilClass, $42: T.class_of()): - $8: T.untyped = - $8 -> (T.untyped ? bb6 : bb7) + $8: T.nilable(Exception) = + $8 -> (T.nilable(Exception) ? bb6 : bb7) # backedges # - bb5(rubyRegionId=1) # - bb13(rubyRegionId=5) -bb6[rubyRegionId=6, firstDead=-1](: Object, $2: NilClass, try: Integer(0), $8: T.untyped, $29: T.class_of(), $40: NilClass, $42: T.class_of()): +bb6[rubyRegionId=6, firstDead=-1](: Object, $2: NilClass, try: Integer(0), $8: Exception, $29: T.class_of(), $40: NilClass, $42: T.class_of()): $32: T.class_of(A) = alias - $33: T::Boolean = $32: T.class_of(A).===($8: T.untyped) + $33: T::Boolean = $32: T.class_of(A).===($8: Exception) $33 -> (T::Boolean ? bb16 : bb17) # backedges @@ -92,8 +92,8 @@ bb10[rubyRegionId=5, firstDead=6](: Object, try: Integer(0), $29: T # - bb9(rubyRegionId=5) # - bb10(rubyRegionId=5) bb13[rubyRegionId=5, firstDead=-1](: Object, $2: NilClass, try: Integer(0), $29: T.class_of(), $40: NilClass, $42: T.class_of()): - $8: T.untyped = - $8 -> (T.untyped ? bb6 : bb14) + $8: T.nilable(Exception) = + $8 -> (T.nilable(Exception) ? bb6 : bb14) # backedges # - bb13(rubyRegionId=5) @@ -108,7 +108,7 @@ bb15[rubyRegionId=7, firstDead=-1](: Object, $2: NilClas # backedges # - bb6(rubyRegionId=6) -bb16[rubyRegionId=6, firstDead=-1](: Object, $2: NilClass, try: Integer(0), $29: T.class_of(), $40: NilClass, $42: T.class_of()): +bb16[rubyRegionId=6, firstDead=-1](: Object, $2: NilClass, try: Integer(0), $8: A, $29: T.class_of(), $40: NilClass, $42: T.class_of()): $8: NilClass = nil $30: Sorbet::Private::Static::Void = $29: T.class_of().($8: NilClass) $36: String("rescue A") = "rescue A" @@ -126,8 +126,8 @@ bb17[rubyRegionId=6, firstDead=-1](: Object, $2: NilClas # backedges # - bb15(rubyRegionId=7) bb18[rubyRegionId=1, firstDead=-1](: Object, $2: NilClass, try: Integer(0), $40: NilClass, $42: T.class_of()): - $4: T.untyped = - $4 -> (T.untyped ? bb3 : bb19) + $4: T.nilable(Exception) = + $4 -> (T.nilable(Exception) ? bb3 : bb19) # backedges # - bb18(rubyRegionId=1) @@ -142,7 +142,7 @@ bb20[rubyRegionId=3, firstDead=-1]($2: NilClass, : Object, $2: NilClass, try: Integer(0), $40: NilClass, $42: T.class_of()): +bb21[rubyRegionId=2, firstDead=-1](: Object, $2: NilClass, try: Integer(0), $4: B, $40: NilClass, $42: T.class_of()): $4: NilClass = nil $43: Sorbet::Private::Static::Void = $42: T.class_of().($4: NilClass) $49: String("rescue B ") = "rescue B " @@ -167,21 +167,9 @@ bb23[rubyRegionId=0, firstDead=1]($2: NilClass): method ::># { -bb0[rubyRegionId=0, firstDead=15](): +bb0[rubyRegionId=0, firstDead=3](): : T.class_of() = cast(: NilClass, T.class_of()); - $7: T.class_of(Sorbet::Private::Static) = alias - $9: T.class_of(A) = alias - $5: Sorbet::Private::Static::Void = $7: T.class_of(Sorbet::Private::Static).keep_for_ide($9: T.class_of(A)) - $12: T.class_of(Sorbet::Private::Static) = alias - $14: T.class_of(Exception) = alias - $10: Sorbet::Private::Static::Void = $12: T.class_of(Sorbet::Private::Static).keep_for_ide($14: T.class_of(Exception)) - $19: T.class_of(Sorbet::Private::Static) = alias - $21: T.class_of(B) = alias - $17: Sorbet::Private::Static::Void = $19: T.class_of(Sorbet::Private::Static).keep_for_ide($21: T.class_of(B)) - $24: T.class_of(Sorbet::Private::Static) = alias - $26: T.class_of(Exception) = alias - $22: Sorbet::Private::Static::Void = $24: T.class_of(Sorbet::Private::Static).keep_for_ide($26: T.class_of(Exception)) - $28: T.untyped = : T.class_of().main() + $4: T.untyped = : T.class_of().main() : T.noreturn = return $2: NilClass -> bb1 diff --git a/test/testdata/cfg/side_effects.rb.cfg-text.exp b/test/testdata/cfg/side_effects.rb.cfg-text.exp index 7c35aec18d..b038a68055 100644 --- a/test/testdata/cfg/side_effects.rb.cfg-text.exp +++ b/test/testdata/cfg/side_effects.rb.cfg-text.exp @@ -1,10 +1,7 @@ method ::># { -bb0[rubyRegionId=0, firstDead=5](): +bb0[rubyRegionId=0, firstDead=2](): : T.class_of() = cast(: NilClass, T.class_of()); - $6: T.class_of(Sorbet::Private::Static) = alias - $8: T.class_of(Side) = alias - $4: Sorbet::Private::Static::Void = $6: T.class_of(Sorbet::Private::Static).keep_for_ide($8: T.class_of(Side)) : T.noreturn = return $2: NilClass -> bb1 diff --git a/test/testdata/cfg/side_effects2.rb.cfg-text.exp b/test/testdata/cfg/side_effects2.rb.cfg-text.exp index d256ad5d41..e1ca48ef75 100644 --- a/test/testdata/cfg/side_effects2.rb.cfg-text.exp +++ b/test/testdata/cfg/side_effects2.rb.cfg-text.exp @@ -1,10 +1,7 @@ method ::># { -bb0[rubyRegionId=0, firstDead=5](): +bb0[rubyRegionId=0, firstDead=2](): : T.class_of() = cast(: NilClass, T.class_of()); - $6: T.class_of(Sorbet::Private::Static) = alias - $8: T.class_of(Side) = alias - $4: Sorbet::Private::Static::Void = $6: T.class_of(Sorbet::Private::Static).keep_for_ide($8: T.class_of(Side)) : T.noreturn = return $2: NilClass -> bb1 diff --git a/test/testdata/cfg/singleton_lazy.rb.symbol-table-raw.exp b/test/testdata/cfg/singleton_lazy.rb.symbol-table-raw.exp index a48afcaaf8..abec883323 100644 --- a/test/testdata/cfg/singleton_lazy.rb.symbol-table-raw.exp +++ b/test/testdata/cfg/singleton_lazy.rb.symbol-table-raw.exp @@ -7,7 +7,7 @@ class >> < > () type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=A) @ Loc {file=test/testdata/cfg/singleton_lazy.rb start=2:1 end=2:8} method > $1>#> () @ Loc {file=test/testdata/cfg/singleton_lazy.rb start=2:1 end=3:4} argument @ Loc {file=test/testdata/cfg/singleton_lazy.rb start=??? end=???} - class > < > (>) @ Loc {file=https://github.com/sorbet/sorbet/tree/master/rbi/core/object.rbi start=removed end=removed} + class > < > (>, >) @ (Loc {file=https://github.com/sorbet/sorbet/tree/master/rbi/stdlib/json.rbi start=removed end=removed}, Loc {file=https://github.com/sorbet/sorbet/tree/master/rbi/core/object.rbi start=removed end=removed}) method ># : private () @ Loc {file=test/testdata/cfg/singleton_lazy.rb start=4:1 end=4:9} argument @ Loc {file=test/testdata/cfg/singleton_lazy.rb start=??? end=???} diff --git a/test/testdata/cfg/textoutput.rb.cfg-text.exp b/test/testdata/cfg/textoutput.rb.cfg-text.exp index d26252caa6..36323d7bad 100644 --- a/test/testdata/cfg/textoutput.rb.cfg-text.exp +++ b/test/testdata/cfg/textoutput.rb.cfg-text.exp @@ -2,12 +2,9 @@ method ::># { bb0[rubyRegionId=0, firstDead=-1](): : T.class_of() = cast(: NilClass, T.class_of()); - $7: T.class_of(Sorbet::Private::Static) = alias - $9: T.class_of(A) = alias - $5: Sorbet::Private::Static::Void = $7: T.class_of(Sorbet::Private::Static).keep_for_ide($9: T.class_of(A)) - $22: T.class_of() = alias > - $11: T.untyped = - $11 -> (T.untyped ? bb3 : bb4) + $14: T.class_of() = alias > + $3: T.nilable(Exception) = + $3 -> (T.nilable(Exception) ? bb3 : bb4) # backedges # - bb6(rubyRegionId=3) @@ -18,61 +15,62 @@ bb1[rubyRegionId=0, firstDead=-1](): # backedges # - bb0(rubyRegionId=0) # - bb4(rubyRegionId=1) -bb3[rubyRegionId=2, firstDead=-1](: T.class_of(), $11: T.untyped, $22: T.class_of()): - e: T.untyped = $11 - $25: T.class_of(StandardError) = alias - $26: T::Boolean = $25: T.class_of(StandardError).===(e: T.untyped) - $26 -> (T::Boolean ? bb7 : bb8) +bb3[rubyRegionId=2, firstDead=-1](: T.class_of(), $2: T.untyped, $3: Exception, $14: T.class_of()): + $20: T.class_of(StandardError) = alias + $21: T::Boolean = $20: T.class_of(StandardError).===($3: Exception) + $21 -> (T::Boolean ? bb7 : bb8) # backedges # - bb0(rubyRegionId=0) -bb4[rubyRegionId=1, firstDead=-1](: T.class_of(), $22: T.class_of()): - $14: T.class_of(A) = alias - $12: A = $14: T.class_of(A).new() - $16: Symbol(:z) = :z - $17: Integer(3) = 3 - $18: Symbol(:w) = :w - $19: String("string") = "string" - $20: T.class_of() = alias > - $15: {z: Integer(3), w: String("string")} = $20: T.class_of().($16: Symbol(:z), $17: Integer(3), $18: Symbol(:w), $19: String("string")) - $21: TrueClass = true - $10: T.untyped = $12: A.f($15: {z: Integer(3), w: String("string")}, $21: TrueClass) - $11: T.untyped = - $11 -> (T.untyped ? bb3 : bb5) +bb4[rubyRegionId=1, firstDead=-1](: T.class_of(), $14: T.class_of()): + $6: T.class_of(A) = alias + $4: A = $6: T.class_of(A).new() + $8: Symbol(:z) = :z + $9: Integer(3) = 3 + $10: Symbol(:w) = :w + $11: String("string") = "string" + $12: T.class_of() = alias > + $7: {z: Integer(3), w: String("string")} = $12: T.class_of().($8: Symbol(:z), $9: Integer(3), $10: Symbol(:w), $11: String("string")) + $13: TrueClass = true + $2: T.untyped = $4: A.f($7: {z: Integer(3), w: String("string")}, $13: TrueClass) + $3: T.nilable(Exception) = + $3 -> (T.nilable(Exception) ? bb3 : bb5) # backedges # - bb4(rubyRegionId=1) -bb5[rubyRegionId=4, firstDead=-1](: T.class_of()): +bb5[rubyRegionId=4, firstDead=-1](: T.class_of(), $2: T.untyped, $3: NilClass): -> bb6 # backedges # - bb5(rubyRegionId=4) # - bb7(rubyRegionId=2) # - bb8(rubyRegionId=2) -bb6[rubyRegionId=3, firstDead=-1](: T.class_of(), $29: T.nilable(TrueClass)): - $32: String("done") = "done" - $30: NilClass = : T.class_of().p($32: String("done")) - $29 -> (T.nilable(TrueClass) ? bb1 : bb9) +bb6[rubyRegionId=3, firstDead=-1](: T.class_of(), $2: T.untyped, $3: T.nilable(Exception), $24: T.nilable(TrueClass)): + $16: T.class_of(T) = alias + e: T.untyped = $16: T.class_of(T).unsafe($3: T.nilable(Exception)) + $27: String("done") = "done" + $25: NilClass = : T.class_of().p($27: String("done")) + $24 -> (T.nilable(TrueClass) ? bb1 : bb9) # backedges # - bb3(rubyRegionId=2) -bb7[rubyRegionId=2, firstDead=-1](: T.class_of(), $22: T.class_of()): - $11: NilClass = nil - $23: Sorbet::Private::Static::Void = $22: T.class_of().($11: NilClass) - $28: String("whoops") = "whoops" - $10: NilClass = : T.class_of().p($28: String("whoops")) +bb7[rubyRegionId=2, firstDead=-1](: T.class_of(), $3: StandardError, $14: T.class_of()): + $3: NilClass = nil + $18: Sorbet::Private::Static::Void = $14: T.class_of().($3: NilClass) + $23: String("whoops") = "whoops" + $2: NilClass = : T.class_of().p($23: String("whoops")) -> bb6 # backedges # - bb3(rubyRegionId=2) -bb8[rubyRegionId=2, firstDead=-1](: T.class_of()): - $29: TrueClass = true +bb8[rubyRegionId=2, firstDead=-1](: T.class_of(), $2: T.untyped, $3: Exception): + $24: TrueClass = true -> bb6 # backedges # - bb6(rubyRegionId=3) -bb9[rubyRegionId=0, firstDead=1](): - : T.noreturn = return $2: NilClass +bb9[rubyRegionId=0, firstDead=1]($2: T.untyped): + : T.noreturn = return $2: T.untyped -> bb1 } diff --git a/test/testdata/cfg/uaf1.rb.cfg-text.exp b/test/testdata/cfg/uaf1.rb.cfg-text.exp index 4608b0e8ff..6b915a3ad8 100644 --- a/test/testdata/cfg/uaf1.rb.cfg-text.exp +++ b/test/testdata/cfg/uaf1.rb.cfg-text.exp @@ -1,10 +1,7 @@ method ::># { -bb0[rubyRegionId=0, firstDead=5](): +bb0[rubyRegionId=0, firstDead=2](): : T.class_of() = cast(: NilClass, T.class_of()); - $6: T.class_of(Sorbet::Private::Static) = alias - $8: T.class_of(A) = alias - $4: Sorbet::Private::Static::Void = $6: T.class_of(Sorbet::Private::Static).keep_for_ide($8: T.class_of(A)) : T.noreturn = return $2: NilClass -> bb1 @@ -50,17 +47,16 @@ bb5[rubyRegionId=1, firstDead=-1](: A, $5: Sorbet::Pr # outerLoops: 1 : A = loadSelf(map) $9: T.class_of() = alias > - $8: T.untyped = - $8 -> (T.untyped ? bb7 : bb8) + $8: T.nilable(Exception) = + $8 -> (T.nilable(Exception) ? bb7 : bb8) # backedges # - bb5(rubyRegionId=1) # - bb8(rubyRegionId=2) -bb7[rubyRegionId=3, firstDead=-1](: A, $5: Sorbet::Private::Static::Void, $6: A, $7: T.nilable(Integer), $8: T.untyped, $9: T.class_of(), $14: NilClass): +bb7[rubyRegionId=3, firstDead=-1](: A, $5: Sorbet::Private::Static::Void, $6: A, $7: T.nilable(Integer), $8: Exception, $9: T.class_of(), $14: NilClass): # outerLoops: 1 - se$1: T.untyped = $8 $12: T.class_of(StandardError) = alias - $13: T::Boolean = $12: T.class_of(StandardError).===(se$1: T.untyped) + $13: T::Boolean = $12: T.class_of(StandardError).===($8: Exception) $13 -> (T::Boolean ? bb11 : bb12) # backedges @@ -68,8 +64,8 @@ bb7[rubyRegionId=3, firstDead=-1](: A, $5: Sorbet::Pr bb8[rubyRegionId=2, firstDead=-1](: A, $5: Sorbet::Private::Static::Void, $6: A, $9: T.class_of(), $14: NilClass): # outerLoops: 1 $7: Integer(1) = 1 - $8: T.untyped = - $8 -> (T.untyped ? bb7 : bb9) + $8: T.nilable(Exception) = + $8 -> (T.nilable(Exception) ? bb7 : bb9) # backedges # - bb8(rubyRegionId=2) @@ -87,7 +83,7 @@ bb10[rubyRegionId=4, firstDead=-1](: A, $5: Sorbet::P # backedges # - bb7(rubyRegionId=3) -bb11[rubyRegionId=3, firstDead=-1](: A, $5: Sorbet::Private::Static::Void, $6: A, $9: T.class_of(), $14: NilClass): +bb11[rubyRegionId=3, firstDead=-1](: A, $5: Sorbet::Private::Static::Void, $6: A, $8: StandardError, $9: T.class_of(), $14: NilClass): # outerLoops: 1 $8: NilClass = nil $10: Sorbet::Private::Static::Void = $9: T.class_of().($8: NilClass) diff --git a/test/testdata/compiler/.rubocop.yml b/test/testdata/compiler/.rubocop.yml deleted file mode 100644 index 9a24e6a2ad..0000000000 --- a/test/testdata/compiler/.rubocop.yml +++ /dev/null @@ -1,2 +0,0 @@ -AllCops: - Exclude: * diff --git a/test/testdata/compiler/abstract_static_methods.rb b/test/testdata/compiler/abstract_static_methods.rb index cd3804b21a..7dd81a8090 100644 --- a/test/testdata/compiler/abstract_static_methods.rb +++ b/test/testdata/compiler/abstract_static_methods.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL class IFoo extend T::Sig @@ -12,9 +11,6 @@ class IFoo def self.foo; end end -# INITIAL{LITERAL}-LABEL: define internal i64 @"func_IFoo.14 -# INITIAL: call void @{{sorbet_defineMethodSingleton.*@func_IFoo.3foo,}} -# INITIAL{LITERAL}: } class Foo < IFoo diff --git a/test/testdata/compiler/all_arguments.opt.ll.exp b/test/testdata/compiler/all_arguments.opt.ll.exp deleted file mode 100644 index 39616d994e..0000000000 --- a/test/testdata/compiler/all_arguments.opt.ll.exp +++ /dev/null @@ -1,1045 +0,0 @@ -; ModuleID = 'payload' -source_filename = "llvm-link" -target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } -%struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.__pthread_cond_s } -%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } -%union.anon = type { i64 } -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } -%struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } -%struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } -%struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } -%struct.anon.0 = type { i16, [2 x i8] } -%struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } -%struct.rb_iseq_location_struct = type { i64, i64, i64, i64, i32, %struct.rb_code_location_struct } -%struct.rb_code_location_struct = type { %struct.rb_code_position_struct, %struct.rb_code_position_struct } -%struct.rb_code_position_struct = type { i32, i32 } -%struct.iseq_insn_info = type { %struct.iseq_insn_info_entry*, i32*, i32, %struct.succ_index_table* } -%struct.iseq_insn_info_entry = type opaque -%struct.succ_index_table = type opaque -%struct.iseq_catch_table = type opaque -%union.iseq_inline_storage_entry = type { %struct.iseq_inline_cache_entry } -%struct.iseq_inline_cache_entry = type { i64, %struct.rb_cref_struct*, i64 } -%struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } -%struct.rb_scope_visi_struct = type { i8, [3 x i8] } -%struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } -%struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } -%union.anon.13 = type { %struct.rb_method_cfunc_struct } -%struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%union.anon.15 = type { i32 } -%struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.16 = type { i64, i64, i64, i64* } -%union.anon.17 = type { %struct.anon.18 } -%struct.anon.18 = type { i64, i32 } -%struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } -%struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } -%struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } -%struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } -%struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } -%struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } -%struct.__sigset_t = type { [16 x i64] } -%struct.rb_calling_info = type { i64, i64, i32, i32 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } -%union.anon.9 = type { %union.pthread_cond_t } -%struct.rb_unblock_callback = type { void (i8*)*, i8* } -%struct.rb_mutex_struct = type opaque -%struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.10 = type { %struct.anon.11 } -%struct.anon.11 = type { i64, i64, i32 } -%struct.rb_fiber_struct = type opaque -%struct.anon.5 = type { [65 x i64] } -%struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } -%struct.rb_event_hook_struct = type opaque -%struct.rb_postponed_job_struct = type opaque -%struct.list_head = type { %struct.list_node } -%union.pthread_mutex_t = type { %struct.__pthread_mutex_s } -%struct.__pthread_mutex_s = type { i32, i32, i32, i32, i32, i16, i16, %struct.__pthread_internal_list } -%struct.__pthread_internal_list = type { %struct.__pthread_internal_list*, %struct.__pthread_internal_list* } -%struct.rb_objspace = type opaque -%struct.rb_at_exit_list = type { void (%struct.rb_vm_struct*)*, %struct.rb_at_exit_list* } -%struct.st_table = type { i8, i8, i8, i32, %struct.st_hash_type*, i64, i64*, i64, i64, %struct.st_table_entry* } -%struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } -%struct.st_table_entry = type opaque -%struct.rb_builtin_function = type opaque -%struct.anon.6 = type { i64, i64, i64, i64 } -%struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } -%struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } -%struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } -%struct.rb_call_info_with_kwarg = type { %struct.rb_call_info, %struct.rb_call_info_kw_arg* } -%struct.rb_call_info_kw_arg = type { i32, [1 x i64] } -%struct.iseq_inline_iv_cache_entry = type { i64, i64 } - -@ruby_current_vm_ptr = external local_unnamed_addr global %struct.rb_vm_struct*, align 8 -@ruby_current_execution_context_ptr = external local_unnamed_addr global %struct.rb_execution_context_struct*, align 8 -@rb_eRuntimeError = external local_unnamed_addr global i64, align 8 -@.str.9 = private unnamed_addr constant [95 x i8] c"sorbet_getBuildSCMRevision: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@.str.10 = private unnamed_addr constant [93 x i8] c"sorbet_getIsReleaseBuild: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@"stackFramePrecomputed_func_Object#14take_arguments" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@iseqEncodedArray = internal global [32 x i64] zeroinitializer -@fileLineNumberInfo = internal global %struct.SorbetLineNumberInfo zeroinitializer -@ic_inspect = internal global %struct.FunctionInlineCache zeroinitializer -@ic_puts = internal global %struct.FunctionInlineCache zeroinitializer -@"stackFramePrecomputed_func_.13" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@ic_take_arguments = internal global %struct.FunctionInlineCache zeroinitializer -@ic_take_arguments.1 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_take_arguments.2 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_take_arguments.3 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_take_arguments.4 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_take_arguments.5 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_take_arguments.6 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_take_arguments.7 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_take_arguments.8 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_take_arguments.9 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_take_arguments.10 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_take_arguments.11 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_take_arguments.12 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_take_arguments.13 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_take_arguments.14 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_take_arguments.15 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_take_arguments.16 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_take_arguments.17 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_take_arguments.18 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_take_arguments.19 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_take_arguments.20 = internal global %struct.FunctionInlineCache zeroinitializer -@sorbet_moduleStringTable = internal unnamed_addr constant [138 x i8] c"take_arguments\00test/testdata/compiler/all_arguments.rb\00g\00d\00e\00\00inspect\00puts\00\00normal\00Object\00a\00b\00c\00f\00baz\00master\00", align 1 -@sorbet_moduleIDTable = internal unnamed_addr global [14 x i64] zeroinitializer, align 8 -@sorbet_moduleIDDescriptors = internal unnamed_addr constant [14 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 14 }, %struct.rb_code_position_struct { i32 55, i32 1 }, %struct.rb_code_position_struct { i32 57, i32 1 }, %struct.rb_code_position_struct { i32 59, i32 1 }, %struct.rb_code_position_struct { i32 61, i32 13 }, %struct.rb_code_position_struct { i32 75, i32 7 }, %struct.rb_code_position_struct { i32 83, i32 4 }, %struct.rb_code_position_struct { i32 88, i32 16 }, %struct.rb_code_position_struct { i32 105, i32 6 }, %struct.rb_code_position_struct { i32 119, i32 1 }, %struct.rb_code_position_struct { i32 121, i32 1 }, %struct.rb_code_position_struct { i32 123, i32 1 }, %struct.rb_code_position_struct { i32 125, i32 1 }, %struct.rb_code_position_struct { i32 127, i32 3 }], align 8 -@sorbet_moduleRubyStringTable = internal unnamed_addr global [3 x i64] zeroinitializer, align 8 -@sorbet_moduleRubyStringDescriptors = internal unnamed_addr constant [3 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 14 }, %struct.rb_code_position_struct { i32 15, i32 39 }, %struct.rb_code_position_struct { i32 88, i32 16 }], align 8 -@rb_cObject = external local_unnamed_addr constant i64 - -; Function Attrs: nounwind readnone willreturn -declare i64 @rb_id2sym(i64) local_unnamed_addr #0 - -; Function Attrs: noreturn -declare void @sorbet_raiseArity(i32, i32, i32) local_unnamed_addr #1 - -; Function Attrs: noreturn -declare void @sorbet_raiseMissingKeywords(i64) local_unnamed_addr #1 - -declare i64 @sorbet_addMissingKWArg(i64, i64) local_unnamed_addr #2 - -declare %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64, i64, i64, i64, %struct.rb_iseq_struct*, i32, i32, %struct.SorbetLineNumberInfo*, i64*, i32, i32) local_unnamed_addr #2 - -declare void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo*, i64*, i32) local_unnamed_addr #2 - -declare i64 @sorbet_readRealpath() local_unnamed_addr #2 - -declare void @sorbet_vm_env_write_slowpath(i64*, i32, i64) local_unnamed_addr #2 - -declare void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) local_unnamed_addr #2 - -declare i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache*, i64) local_unnamed_addr #2 - -declare void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_iseq_struct*) local_unnamed_addr #2 - -declare void @sorbet_vm_define_method(i64, i8*, i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)*, i8*, %struct.rb_iseq_struct*, i1 zeroext) local_unnamed_addr #2 - -declare void @sorbet_vm_intern_ids(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #2 - -declare void @sorbet_vm_init_string_table(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #2 - -declare i32 @rb_block_given_p() local_unnamed_addr #2 - -declare i64 @rb_block_proc() local_unnamed_addr #2 - -declare i64 @rb_ary_new_from_values(i64, i64*) local_unnamed_addr #2 - -declare i64 @rb_hash_new() local_unnamed_addr #2 - -declare i64 @rb_hash_dup(i64) local_unnamed_addr #2 - -; Function Attrs: noreturn -declare void @rb_raise(i64, i8*, ...) local_unnamed_addr #1 - -declare i64 @rb_hash_delete_entry(i64, i64) local_unnamed_addr #2 - -; Function Attrs: inaccessiblememonly nofree nosync nounwind willreturn -declare void @llvm.experimental.noalias.scope.decl(metadata) #3 - -declare i64 @rb_ary_new() local_unnamed_addr #2 - -; Function Attrs: allocsize(0,1) -declare noalias nonnull i8* @ruby_xcalloc(i64, i64) local_unnamed_addr #4 - -; Function Attrs: allocsize(0,1) -declare noalias nonnull i8* @ruby_xmalloc2(i64, i64) local_unnamed_addr #4 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) #5 - -; Function Attrs: nounwind ssp uwtable -define weak i32 @sorbet_getIsReleaseBuild() local_unnamed_addr #6 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([93 x i8], [93 x i8]* @.str.10, i64 0, i64 0)) #10 - unreachable -} - -; Function Attrs: nounwind ssp uwtable -define weak i8* @sorbet_getBuildSCMRevision() local_unnamed_addr #6 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([95 x i8], [95 x i8]* @.str.9, i64 0, i64 0)) #10 - unreachable -} - -define internal fastcc void @sorbet_globalConstructors(i64 %realpath) unnamed_addr { -allocRubyIds: - tail call void @sorbet_vm_intern_ids(i64* noundef getelementptr inbounds ([14 x i64], [14 x i64]* @sorbet_moduleIDTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([14 x %struct.rb_code_position_struct], [14 x %struct.rb_code_position_struct]* @sorbet_moduleIDDescriptors, i32 0, i32 0), i32 noundef 14, i8* noundef getelementptr inbounds ([138 x i8], [138 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_vm_init_string_table(i64* noundef getelementptr inbounds ([3 x i64], [3 x i64]* @sorbet_moduleRubyStringTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([3 x %struct.rb_code_position_struct], [3 x %struct.rb_code_position_struct]* @sorbet_moduleRubyStringDescriptors, i32 0, i32 0), i32 noundef 3, i8* noundef getelementptr inbounds ([138 x i8], [138 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i32 0, i32 0), i32 noundef 32) - tail call fastcc void @"Constr_stackFramePrecomputed_func_Object#14take_arguments"(i64 %realpath) - %rubyId_inspect = load i64, i64* getelementptr inbounds ([14 x i64], [14 x i64]* @sorbet_moduleIDTable, i64 0, i64 5), align 8, !dbg !10, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_inspect, i64 %rubyId_inspect, i32 noundef 16, i32 noundef 0, i32 noundef 0), !dbg !10 - %rubyId_puts = load i64, i64* getelementptr inbounds ([14 x i64], [14 x i64]* @sorbet_moduleIDTable, i64 0, i64 6), align 8, !dbg !15, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts, i64 %rubyId_puts, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !15 - tail call fastcc void @"Constr_stackFramePrecomputed_func_.13"(i64 %realpath) - %rubyId_take_arguments = load i64, i64* getelementptr inbounds ([14 x i64], [14 x i64]* @sorbet_moduleIDTable, i64 0, i64 0), align 8, !dbg !16, !invariant.load !5 - %rubyId_d = load i64, i64* getelementptr inbounds ([14 x i64], [14 x i64]* @sorbet_moduleIDTable, i64 0, i64 2), align 8, !dbg !16, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_take_arguments, i64 %rubyId_take_arguments, i32 noundef 68, i32 noundef 8, i32 noundef 1, i64 %rubyId_d), !dbg !16 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_take_arguments.1, i64 %rubyId_take_arguments, i32 noundef 68, i32 noundef 7, i32 noundef 1, i64 %rubyId_d), !dbg !18 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_take_arguments.2, i64 %rubyId_take_arguments, i32 noundef 68, i32 noundef 6, i32 noundef 1, i64 %rubyId_d), !dbg !19 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_take_arguments.3, i64 %rubyId_take_arguments, i32 noundef 68, i32 noundef 5, i32 noundef 1, i64 %rubyId_d), !dbg !20 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_take_arguments.4, i64 %rubyId_take_arguments, i32 noundef 68, i32 noundef 4, i32 noundef 1, i64 %rubyId_d), !dbg !21 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_take_arguments.5, i64 %rubyId_take_arguments, i32 noundef 68, i32 noundef 3, i32 noundef 1, i64 %rubyId_d), !dbg !22 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_take_arguments.6, i64 %rubyId_take_arguments, i32 noundef 68, i32 noundef 2, i32 noundef 1, i64 %rubyId_d), !dbg !23 - %rubyId_e = load i64, i64* getelementptr inbounds ([14 x i64], [14 x i64]* @sorbet_moduleIDTable, i64 0, i64 3), align 8, !dbg !24, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_take_arguments.7, i64 %rubyId_take_arguments, i32 noundef 68, i32 noundef 9, i32 noundef 2, i64 %rubyId_d, i64 %rubyId_e), !dbg !24 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_take_arguments.8, i64 %rubyId_take_arguments, i32 noundef 68, i32 noundef 8, i32 noundef 2, i64 %rubyId_d, i64 %rubyId_e), !dbg !25 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_take_arguments.9, i64 %rubyId_take_arguments, i32 noundef 68, i32 noundef 7, i32 noundef 2, i64 %rubyId_d, i64 %rubyId_e), !dbg !26 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_take_arguments.10, i64 %rubyId_take_arguments, i32 noundef 68, i32 noundef 6, i32 noundef 2, i64 %rubyId_d, i64 %rubyId_e), !dbg !27 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_take_arguments.11, i64 %rubyId_take_arguments, i32 noundef 68, i32 noundef 5, i32 noundef 2, i64 %rubyId_d, i64 %rubyId_e), !dbg !28 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_take_arguments.12, i64 %rubyId_take_arguments, i32 noundef 68, i32 noundef 4, i32 noundef 2, i64 %rubyId_d, i64 %rubyId_e), !dbg !29 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_take_arguments.13, i64 %rubyId_take_arguments, i32 noundef 68, i32 noundef 3, i32 noundef 2, i64 %rubyId_d, i64 %rubyId_e), !dbg !30 - %rubyId_baz = load i64, i64* getelementptr inbounds ([14 x i64], [14 x i64]* @sorbet_moduleIDTable, i64 0, i64 13), align 8, !dbg !31, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_take_arguments.14, i64 %rubyId_take_arguments, i32 noundef 68, i32 noundef 10, i32 noundef 3, i64 %rubyId_d, i64 %rubyId_e, i64 %rubyId_baz), !dbg !31 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_take_arguments.15, i64 %rubyId_take_arguments, i32 noundef 68, i32 noundef 9, i32 noundef 3, i64 %rubyId_d, i64 %rubyId_e, i64 %rubyId_baz), !dbg !32 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_take_arguments.16, i64 %rubyId_take_arguments, i32 noundef 68, i32 noundef 8, i32 noundef 3, i64 %rubyId_d, i64 %rubyId_e, i64 %rubyId_baz), !dbg !33 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_take_arguments.17, i64 %rubyId_take_arguments, i32 noundef 68, i32 noundef 7, i32 noundef 3, i64 %rubyId_d, i64 %rubyId_e, i64 %rubyId_baz), !dbg !34 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_take_arguments.18, i64 %rubyId_take_arguments, i32 noundef 68, i32 noundef 6, i32 noundef 3, i64 %rubyId_d, i64 %rubyId_e, i64 %rubyId_baz), !dbg !35 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_take_arguments.19, i64 %rubyId_take_arguments, i32 noundef 68, i32 noundef 5, i32 noundef 3, i64 %rubyId_d, i64 %rubyId_e, i64 %rubyId_baz), !dbg !36 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_take_arguments.20, i64 %rubyId_take_arguments, i32 noundef 68, i32 noundef 4, i32 noundef 3, i64 %rubyId_d, i64 %rubyId_e, i64 %rubyId_baz), !dbg !37 - ret void -} - -; Function Attrs: nounwind sspreq uwtable -define internal i64 @"func_Object#14take_arguments"(i32 %argc, i64* %argArray, i64 %selfRaw, %struct.rb_control_frame_struct* nonnull align 8 dereferenceable(8) %cfp, i8* nocapture nofree readnone %calling, i8* nocapture nofree readnone %callData) #7 !dbg !11 { -functionEntryInitializers: - %callArgs = alloca [8 x i64], align 8 - %0 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 0 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 4), i64** %0, align 8, !tbaa !38 - %1 = icmp slt i32 1, %argc, !dbg !40 - br i1 %1, label %2, label %sorbet_determineKwSplatArg.exit, !dbg !40 - -2: ; preds = %functionEntryInitializers - %3 = add nsw i32 %argc, -1, !dbg !40 - %4 = zext i32 %3 to i64 - %5 = getelementptr inbounds i64, i64* %argArray, i64 %4, !dbg !40 - %6 = load i64, i64* %5, align 8, !dbg !40, !tbaa !6 - %7 = and i64 %6, 7, !dbg !40 - %8 = icmp ne i64 %7, 0, !dbg !40 - %9 = and i64 %6, -9, !dbg !40 - %10 = icmp eq i64 %9, 0, !dbg !40 - %11 = or i1 %8, %10, !dbg !40 - br i1 %11, label %sorbet_determineKwSplatArg.exit.thread77, label %sorbet_isa_Hash.exit, !dbg !40 - -sorbet_isa_Hash.exit: ; preds = %2 - %12 = inttoptr i64 %6 to %struct.iseq_inline_iv_cache_entry*, !dbg !40 - %13 = getelementptr inbounds %struct.iseq_inline_iv_cache_entry, %struct.iseq_inline_iv_cache_entry* %12, i64 0, i32 0, !dbg !40 - %14 = load i64, i64* %13, align 8, !dbg !40, !tbaa !41 - %15 = and i64 %14, 31, !dbg !40 - %16 = icmp eq i64 %15, 8, !dbg !40 - br i1 %16, label %fillRequiredArgs, label %sorbet_determineKwSplatArg.exit.thread77, !dbg !40 - -sorbet_determineKwSplatArg.exit.thread77: ; preds = %sorbet_isa_Hash.exit, %2 - br label %fillRequiredArgs, !dbg !40 - -sorbet_determineKwSplatArg.exit: ; preds = %functionEntryInitializers - %tooFewArgs = icmp ult i32 %argc, 1, !dbg !40 - br i1 %tooFewArgs, label %argCountFailBlock, label %fillRequiredArgs, !dbg !40, !prof !43 - -argCountFailBlock: ; preds = %sorbet_determineKwSplatArg.exit - tail call void @sorbet_raiseArity(i32 noundef 0, i32 noundef 1, i32 noundef -1) #11, !dbg !40 - unreachable, !dbg !40 - -fillFromDefaultBlockDone1: ; preds = %sorbet_writeLocal.exit - %17 = getelementptr i64, i64* %argArray, i32 1, !dbg !40 - %rawArg_b = load i64, i64* %17, align 8, !dbg !40 - %18 = icmp sgt i32 %30, 2, !dbg !40 - br i1 %18, label %20, label %fillFromDefaultBlockDone1.thread, !dbg !40 - -fillFromDefaultBlockDone1.thread: ; preds = %sorbet_writeLocal.exit, %fillFromDefaultBlockDone1 - %".sroa.0.084" = phi i64 [ 20, %fillFromDefaultBlockDone1 ], [ 0, %sorbet_writeLocal.exit ] - %b.sroa.0.182 = phi i64 [ %rawArg_b, %fillFromDefaultBlockDone1 ], [ 8, %sorbet_writeLocal.exit ] - %19 = tail call i64 @rb_ary_new() #12, !dbg !40 - br label %sorbet_readRestArgs.exit, !dbg !40 - -20: ; preds = %fillFromDefaultBlockDone1 - %21 = sub nuw nsw i32 %30, 2, !dbg !40 - %22 = zext i32 %21 to i64 - %23 = getelementptr inbounds i64, i64* %argArray, i64 2, !dbg !40 - %24 = tail call i64 @rb_ary_new_from_values(i64 %22, i64* nonnull %23) #12, !dbg !40 - br label %sorbet_readRestArgs.exit, !dbg !40 - -sorbet_readRestArgs.exit: ; preds = %fillFromDefaultBlockDone1.thread, %20 - %".sroa.0.083" = phi i64 [ %".sroa.0.084", %fillFromDefaultBlockDone1.thread ], [ 20, %20 ] - %b.sroa.0.181 = phi i64 [ %b.sroa.0.182, %fillFromDefaultBlockDone1.thread ], [ %rawArg_b, %20 ] - %25 = phi i64 [ %19, %fillFromDefaultBlockDone1.thread ], [ %24, %20 ], !dbg !40 - %rubyId_d = load i64, i64* getelementptr inbounds ([14 x i64], [14 x i64]* @sorbet_moduleIDTable, i64 0, i64 2), align 8, !dbg !40, !invariant.load !5 - %rawSym = tail call i64 @rb_id2sym(i64 %rubyId_d), !dbg !40 - %26 = icmp eq i64 %29, 52, !dbg !40 - br i1 %26, label %kwArgContinue, label %sorbet_removeKWArg.exit74, !dbg !40 - -sorbet_removeKWArg.exit74: ; preds = %sorbet_readRestArgs.exit - %27 = tail call i64 @rb_hash_delete_entry(i64 %29, i64 %rawSym) #12, !dbg !40 - %28 = icmp eq i64 %27, 52, !dbg !40 - br i1 %28, label %kwArgContinue, label %kwArgContinue.thread, !dbg !40 - -kwArgContinue.thread: ; preds = %sorbet_removeKWArg.exit74 - %rubyId_e87 = load i64, i64* getelementptr inbounds ([14 x i64], [14 x i64]* @sorbet_moduleIDTable, i64 0, i64 3), align 8, !dbg !40, !invariant.load !5 - %rawSym2088 = tail call i64 @rb_id2sym(i64 %rubyId_e87), !dbg !40 - br label %sorbet_removeKWArg.exit.thread, !dbg !40 - -fillRequiredArgs: ; preds = %sorbet_isa_Hash.exit, %sorbet_determineKwSplatArg.exit.thread77, %sorbet_determineKwSplatArg.exit - %29 = phi i64 [ 52, %sorbet_determineKwSplatArg.exit ], [ 52, %sorbet_determineKwSplatArg.exit.thread77 ], [ %6, %sorbet_isa_Hash.exit ] - %30 = phi i32 [ %argc, %sorbet_determineKwSplatArg.exit ], [ %argc, %sorbet_determineKwSplatArg.exit.thread77 ], [ %3, %sorbet_isa_Hash.exit ] - %rawArg_a = load i64, i64* %argArray, align 8, !dbg !40 - %31 = tail call i32 @rb_block_given_p() #12, !dbg !40 - %32 = icmp eq i32 %31, 0, !dbg !40 - br i1 %32, label %sorbet_getMethodBlockAsProc.exit, label %33, !dbg !40 - -33: ; preds = %fillRequiredArgs - %34 = tail call i64 @rb_block_proc() #12, !dbg !40 - br label %sorbet_getMethodBlockAsProc.exit, !dbg !40 - -sorbet_getMethodBlockAsProc.exit: ; preds = %fillRequiredArgs, %33 - %35 = phi i64 [ %34, %33 ], [ 8, %fillRequiredArgs ], !dbg !40 - %36 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 4, !dbg !40 - %37 = load i64*, i64** %36, align 8, !dbg !40, !tbaa !44 - tail call void @llvm.experimental.noalias.scope.decl(metadata !46), !dbg !40 - %38 = load i64, i64* %37, align 8, !dbg !40, !tbaa !6 - %39 = and i64 %38, 8, !dbg !40 - %40 = icmp eq i64 %39, 0, !dbg !40 - br i1 %40, label %41, label %43, !dbg !40, !prof !49 - -41: ; preds = %sorbet_getMethodBlockAsProc.exit - %42 = getelementptr inbounds i64, i64* %37, i64 -3, !dbg !40 - store i64 %35, i64* %42, align 8, !dbg !40, !tbaa !6 - br label %sorbet_writeLocal.exit, !dbg !40 - -43: ; preds = %sorbet_getMethodBlockAsProc.exit - tail call void @sorbet_vm_env_write_slowpath(i64* nonnull align 8 dereferenceable(8) %37, i32 noundef -3, i64 %35) #12, !dbg !40 - br label %sorbet_writeLocal.exit, !dbg !40 - -sorbet_writeLocal.exit: ; preds = %41, %43 - %default0 = icmp eq i32 %30, 1, !dbg !40 - br i1 %default0, label %fillFromDefaultBlockDone1.thread, label %fillFromDefaultBlockDone1, !dbg !40, !prof !43 - -kwArgContinue: ; preds = %sorbet_readRestArgs.exit, %sorbet_removeKWArg.exit74 - %44 = tail call i64 @sorbet_addMissingKWArg(i64 noundef 52, i64 %rawSym), !dbg !40 - %rubyId_e = load i64, i64* getelementptr inbounds ([14 x i64], [14 x i64]* @sorbet_moduleIDTable, i64 0, i64 3), align 8, !dbg !40, !invariant.load !5 - %rawSym20 = tail call i64 @rb_id2sym(i64 %rubyId_e), !dbg !40 - br i1 %26, label %sorbet_removeKWArg.exit, label %sorbet_removeKWArg.exit.thread, !dbg !40 - -sorbet_removeKWArg.exit: ; preds = %kwArgContinue - %45 = icmp eq i64 %44, 52, !dbg !40 - br i1 %45, label %sorbet_readKWRestArgs.exit.thread, label %49, !dbg !40, !prof !49 - -sorbet_removeKWArg.exit.thread: ; preds = %kwArgContinue, %kwArgContinue.thread - %rawSym2093 = phi i64 [ %rawSym2088, %kwArgContinue.thread ], [ %rawSym20, %kwArgContinue ] - %missingArgsPhi91 = phi i64 [ 52, %kwArgContinue.thread ], [ %44, %kwArgContinue ] - %d.sroa.0.089 = phi i64 [ %27, %kwArgContinue.thread ], [ 8, %kwArgContinue ] - %46 = tail call i64 @rb_hash_delete_entry(i64 %29, i64 %rawSym2093) #12, !dbg !40 - %47 = icmp eq i64 %46, 52, !dbg !40 - %e.sroa.0.196 = select i1 %47, i64 8, i64 %46, !dbg !40 - %"9.sroa.0.097" = select i1 %47, i64 0, i64 20, !dbg !40 - %48 = icmp eq i64 %missingArgsPhi91, 52, !dbg !40 - br i1 %48, label %sorbet_readKWRestArgs.exit, label %49, !dbg !40, !prof !49 - -49: ; preds = %sorbet_removeKWArg.exit.thread, %sorbet_removeKWArg.exit - %missingArgsPhi9298 = phi i64 [ %missingArgsPhi91, %sorbet_removeKWArg.exit.thread ], [ %44, %sorbet_removeKWArg.exit ] - tail call void @sorbet_raiseMissingKeywords(i64 %missingArgsPhi9298) #10, !dbg !40 - unreachable, !dbg !40 - -sorbet_readKWRestArgs.exit.thread: ; preds = %sorbet_removeKWArg.exit - %50 = tail call i64 @rb_hash_new() #12, !dbg !40 - %51 = and i64 %".sroa.0.083", -9, !dbg !50 - %52 = icmp ne i64 %51, 0, !dbg !50 - %b.sroa.0.0125 = select i1 %52, i64 %b.sroa.0.181, i64 3, !dbg !50 - br label %58, !dbg !51 - -sorbet_readKWRestArgs.exit: ; preds = %sorbet_removeKWArg.exit.thread - %53 = tail call i64 @rb_hash_dup(i64 %29) #12, !dbg !40 - %54 = and i64 %".sroa.0.083", -9, !dbg !50 - %55 = icmp ne i64 %54, 0, !dbg !50 - %b.sroa.0.0 = select i1 %55, i64 %b.sroa.0.181, i64 3, !dbg !50 - %56 = icmp ne i64 %"9.sroa.0.097", 0, !dbg !51 - br i1 %56, label %57, label %58, !dbg !51 - -57: ; preds = %sorbet_readKWRestArgs.exit - br label %58, !dbg !51 - -58: ; preds = %sorbet_readKWRestArgs.exit.thread, %sorbet_readKWRestArgs.exit, %57 - %b.sroa.0.0127 = phi i64 [ %b.sroa.0.0, %57 ], [ %b.sroa.0.0, %sorbet_readKWRestArgs.exit ], [ %b.sroa.0.0125, %sorbet_readKWRestArgs.exit.thread ] - %59 = phi i64 [ %53, %57 ], [ %53, %sorbet_readKWRestArgs.exit ], [ %50, %sorbet_readKWRestArgs.exit.thread ] - %d.sroa.0.09099109126 = phi i64 [ %d.sroa.0.089, %57 ], [ %d.sroa.0.089, %sorbet_readKWRestArgs.exit ], [ 8, %sorbet_readKWRestArgs.exit.thread ] - %60 = phi i64 [ %e.sroa.0.196, %57 ], [ 5, %sorbet_readKWRestArgs.exit ], [ 5, %sorbet_readKWRestArgs.exit.thread ] - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %0, align 8, !dbg !52, !tbaa !38 - %callArgs0Addr = getelementptr [8 x i64], [8 x i64]* %callArgs, i64 0, i64 0, !dbg !10 - store i64 %rawArg_a, i64* %callArgs0Addr, align 8, !dbg !10 - %callArgs1Addr = getelementptr [8 x i64], [8 x i64]* %callArgs, i64 0, i64 1, !dbg !10 - store i64 %b.sroa.0.0127, i64* %callArgs1Addr, align 8, !dbg !10 - %callArgs2Addr = getelementptr [8 x i64], [8 x i64]* %callArgs, i64 0, i64 2, !dbg !10 - store i64 %25, i64* %callArgs2Addr, align 8, !dbg !10 - %callArgs3Addr = getelementptr [8 x i64], [8 x i64]* %callArgs, i64 0, i64 3, !dbg !10 - store i64 %d.sroa.0.09099109126, i64* %callArgs3Addr, align 8, !dbg !10 - %callArgs4Addr = getelementptr [8 x i64], [8 x i64]* %callArgs, i64 0, i64 4, !dbg !10 - store i64 %60, i64* %callArgs4Addr, align 8, !dbg !10 - %callArgs5Addr = getelementptr [8 x i64], [8 x i64]* %callArgs, i64 0, i64 5, !dbg !10 - store i64 %59, i64* %callArgs5Addr, align 8, !dbg !10 - %61 = load i64*, i64** %36, align 8, !dbg !10, !tbaa !44 - tail call void @llvm.experimental.noalias.scope.decl(metadata !53), !dbg !10 - %62 = getelementptr inbounds i64, i64* %61, i64 -3, !dbg !10 - %63 = load i64, i64* %62, align 8, !dbg !10, !tbaa !6 - %callArgs6Addr = getelementptr [8 x i64], [8 x i64]* %callArgs, i64 0, i64 6, !dbg !10 - store i64 %63, i64* %callArgs6Addr, align 8, !dbg !10 - tail call void @llvm.experimental.noalias.scope.decl(metadata !56) #13, !dbg !10 - %64 = call i64 @rb_ary_new_from_values(i64 noundef 7, i64* noundef nonnull %callArgs0Addr) #12, !dbg !10 - %65 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 1, !dbg !10 - %66 = load i64*, i64** %65, align 8, !dbg !10 - store i64 %64, i64* %66, align 8, !dbg !10, !tbaa !6 - %67 = getelementptr inbounds i64, i64* %66, i64 1, !dbg !10 - store i64* %67, i64** %65, align 8, !dbg !10 - %send = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_inspect, i64 0), !dbg !10 - %68 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 1, !dbg !15 - %69 = load i64*, i64** %68, align 8, !dbg !15 - store i64 %selfRaw, i64* %69, align 8, !dbg !15, !tbaa !6 - %70 = getelementptr inbounds i64, i64* %69, i64 1, !dbg !15 - store i64 %send, i64* %70, align 8, !dbg !15, !tbaa !6 - %71 = getelementptr inbounds i64, i64* %70, i64 1, !dbg !15 - store i64* %71, i64** %68, align 8, !dbg !15 - %send121 = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts, i64 0), !dbg !15 - ret i64 %send121 -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_Object#14take_arguments"(i64 %realpath) unnamed_addr #8 { -entryInitializers: - %rubyId_take_arguments = load i64, i64* getelementptr inbounds ([14 x i64], [14 x i64]* @sorbet_moduleIDTable, i64 0, i64 0), align 8, !invariant.load !5 - %rubyStr_take_arguments = load i64, i64* getelementptr inbounds ([3 x i64], [3 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 0), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/all_arguments.rb" = load i64, i64* getelementptr inbounds ([3 x i64], [3 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, align 8 - %rubyId_g = load i64, i64* getelementptr inbounds ([14 x i64], [14 x i64]* @sorbet_moduleIDTable, i64 0, i64 1), align 8, !invariant.load !5 - store i64 %rubyId_g, i64* %locals, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %rubyStr_take_arguments, i64 %rubyId_take_arguments, i64 %"rubyStr_test/testdata/compiler/all_arguments.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 1, i32 noundef 4, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull align 8 %locals, i32 noundef 1, i32 noundef 8) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Object#14take_arguments", align 8 - ret void -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_.13"(i64 %realpath) unnamed_addr #8 { -entryInitializers: - %"rubyId_" = load i64, i64* getelementptr inbounds ([14 x i64], [14 x i64]* @sorbet_moduleIDTable, i64 0, i64 7), align 8, !invariant.load !5 - %"rubyStr_" = load i64, i64* getelementptr inbounds ([3 x i64], [3 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 2), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/all_arguments.rb" = load i64, i64* getelementptr inbounds ([3 x i64], [3 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_", i64 %"rubyId_", i64 %"rubyStr_test/testdata/compiler/all_arguments.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 0, i32 noundef 4, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 11) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - ret void -} - -; Function Attrs: sspreq -define void @Init_all_arguments() local_unnamed_addr #9 { -entry: - %positional_table.i = alloca i64, i32 4, align 8, !dbg !59 - %keyword_table.i = alloca i64, i32 3, align 8, !dbg !59 - %realpath = tail call i64 @sorbet_readRealpath() - tail call fastcc void @sorbet_globalConstructors(i64 %realpath) - %0 = load %struct.rb_vm_struct*, %struct.rb_vm_struct** @ruby_current_vm_ptr, align 8, !tbaa !38 - %1 = getelementptr inbounds %struct.rb_vm_struct, %struct.rb_vm_struct* %0, i64 0, i32 18 - %2 = load i64, i64* %1, align 8, !tbaa !60 - %3 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !38 - %4 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %3, i64 0, i32 2 - %5 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %4, align 8, !tbaa !70 - %6 = bitcast i64* %positional_table.i to i8* - call void @llvm.lifetime.start.p0i8(i64 32, i8* nonnull %6) - %7 = bitcast i64* %keyword_table.i to i8* - call void @llvm.lifetime.start.p0i8(i64 24, i8* nonnull %7) - %stackFrame.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - %8 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame.i, %struct.rb_iseq_struct** %8, align 8, !tbaa !73 - %9 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 4 - %10 = load i64*, i64** %9, align 8, !tbaa !44 - %11 = load i64, i64* %10, align 8, !tbaa !6 - %12 = and i64 %11, -33 - store i64 %12, i64* %10, align 8, !tbaa !6 - tail call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %3, %struct.rb_control_frame_struct* %5, %struct.rb_iseq_struct* %stackFrame.i) #12 - %13 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 0 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 4), i64** %13, align 8, !dbg !74, !tbaa !38 - %14 = load i64, i64* @rb_cObject, align 8, !dbg !59 - %stackFrame386.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Object#14take_arguments", align 8, !dbg !59 - %15 = tail call noalias nonnull i8* @ruby_xcalloc(i64 noundef 1, i64 noundef 64) #14, !dbg !59 - %16 = bitcast i8* %15 to i16*, !dbg !59 - %17 = load i16, i16* %16, align 8, !dbg !59 - %18 = and i16 %17, -384, !dbg !59 - %19 = or i16 %18, 119, !dbg !59 - store i16 %19, i16* %16, align 8, !dbg !59 - %20 = getelementptr inbounds i8, i8* %15, i64 8, !dbg !59 - %21 = bitcast i8* %20 to i32*, !dbg !59 - %22 = getelementptr inbounds i8, i8* %15, i64 12, !dbg !59 - %23 = bitcast i8* %22 to i32*, !dbg !59 - %24 = getelementptr inbounds i8, i8* %15, i64 16, !dbg !59 - %25 = getelementptr inbounds i8, i8* %15, i64 20, !dbg !59 - %26 = bitcast i8* %25 to i32*, !dbg !59 - store i32 0, i32* %26, align 4, !dbg !59, !tbaa !75 - %27 = getelementptr inbounds i8, i8* %15, i64 24, !dbg !59 - %28 = bitcast i8* %27 to i32*, !dbg !59 - store i32 0, i32* %28, align 8, !dbg !59, !tbaa !78 - %29 = getelementptr inbounds i8, i8* %15, i64 28, !dbg !59 - %30 = bitcast i8* %29 to i32*, !dbg !59 - store i32 3, i32* %30, align 4, !dbg !59, !tbaa !79 - %31 = getelementptr inbounds i8, i8* %15, i64 4, !dbg !59 - %32 = bitcast i8* %31 to i32*, !dbg !59 - %33 = bitcast i32* %32 to <4 x i32>*, !dbg !59 - store <4 x i32> , <4 x i32>* %33, align 4, !dbg !59, !tbaa !80 - %34 = load <2 x i64>, <2 x i64>* bitcast (i64* getelementptr inbounds ([14 x i64], [14 x i64]* @sorbet_moduleIDTable, i64 0, i64 9) to <2 x i64>*), align 8, !dbg !59, !invariant.load !5 - %35 = bitcast i64* %positional_table.i to <2 x i64>*, !dbg !59 - store <2 x i64> %34, <2 x i64>* %35, align 8, !dbg !59 - %rubyId_c.i = load i64, i64* getelementptr inbounds ([14 x i64], [14 x i64]* @sorbet_moduleIDTable, i64 0, i64 11), align 8, !dbg !59, !invariant.load !5 - %36 = getelementptr i64, i64* %positional_table.i, i32 2, !dbg !59 - store i64 %rubyId_c.i, i64* %36, align 8, !dbg !59 - %rubyId_g.i = load i64, i64* getelementptr inbounds ([14 x i64], [14 x i64]* @sorbet_moduleIDTable, i64 0, i64 1), align 8, !dbg !59, !invariant.load !5 - %37 = getelementptr i64, i64* %positional_table.i, i32 3, !dbg !59 - store i64 %rubyId_g.i, i64* %37, align 8, !dbg !59 - %38 = tail call noalias nonnull i8* @ruby_xmalloc2(i64 noundef 4, i64 noundef 8) #14, !dbg !59 - call void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture nonnull writeonly align 1 %38, i8* nocapture noundef nonnull readonly align 8 dereferenceable(24) %6, i64 noundef 32, i1 noundef false) #12, !dbg !59 - %39 = getelementptr inbounds i8, i8* %15, i64 32, !dbg !59 - %40 = bitcast i8* %39 to i8**, !dbg !59 - store i8* %38, i8** %40, align 8, !dbg !59, !tbaa !81 - %41 = load <2 x i64>, <2 x i64>* bitcast (i64* getelementptr inbounds ([14 x i64], [14 x i64]* @sorbet_moduleIDTable, i64 0, i64 2) to <2 x i64>*), align 8, !dbg !59, !invariant.load !5 - %42 = bitcast i64* %keyword_table.i to <2 x i64>*, !dbg !59 - store <2 x i64> %41, <2 x i64>* %42, align 8, !dbg !59 - %rubyId_f.i = load i64, i64* getelementptr inbounds ([14 x i64], [14 x i64]* @sorbet_moduleIDTable, i64 0, i64 12), align 8, !dbg !59, !invariant.load !5 - %43 = getelementptr i64, i64* %keyword_table.i, i32 2, !dbg !59 - store i64 %rubyId_f.i, i64* %43, align 8, !dbg !59 - %44 = getelementptr inbounds i8, i8* %15, i64 40, !dbg !59 - %45 = bitcast i8* %44 to i32*, !dbg !59 - store i32 2, i32* %45, align 8, !dbg !59, !tbaa !82 - %46 = getelementptr inbounds i8, i8* %15, i64 44, !dbg !59 - %47 = bitcast i8* %46 to i32*, !dbg !59 - store i32 1, i32* %47, align 4, !dbg !59, !tbaa !83 - %48 = load i32, i32* %21, align 8, !dbg !59, !tbaa !84 - %49 = load i32, i32* %23, align 4, !dbg !59, !tbaa !85 - %50 = add i32 %48, 2, !dbg !59 - %51 = add i32 %50, %49, !dbg !59 - %52 = getelementptr inbounds i8, i8* %15, i64 48, !dbg !59 - %53 = bitcast i8* %52 to i32*, !dbg !59 - store i32 %51, i32* %53, align 8, !dbg !59, !tbaa !86 - %54 = load i16, i16* %16, align 8, !dbg !59 - %55 = and i16 %54, 32, !dbg !59 - %56 = icmp eq i16 %55, 0, !dbg !59 - br i1 %56, label %"func_.13.exit", label %57, !dbg !59 - -57: ; preds = %entry - %58 = add nsw i32 %51, 1, !dbg !59 - %59 = getelementptr inbounds i8, i8* %15, i64 52, !dbg !59 - %60 = bitcast i8* %59 to i32*, !dbg !59 - store i32 %58, i32* %60, align 4, !dbg !59, !tbaa !87 - br label %"func_.13.exit", !dbg !59 - -"func_.13.exit": ; preds = %entry, %57 - %61 = tail call noalias nonnull i8* @ruby_xmalloc2(i64 noundef 3, i64 noundef 8) #14, !dbg !59 - call void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture nonnull writeonly align 1 %61, i8* nocapture noundef nonnull readonly align 8 dereferenceable(24) %7, i64 noundef 24, i1 noundef false) #12, !dbg !59 - %62 = getelementptr inbounds i8, i8* %15, i64 56, !dbg !59 - %63 = bitcast i8* %62 to i8**, !dbg !59 - store i8* %61, i8** %63, align 8, !dbg !59, !tbaa !88 - tail call void @sorbet_vm_define_method(i64 %14, i8* noundef getelementptr inbounds ([138 x i8], [138 x i8]* @sorbet_moduleStringTable, i64 0, i64 0), i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)* noundef @"func_Object#14take_arguments", i8* nonnull %15, %struct.rb_iseq_struct* %stackFrame386.i, i1 noundef zeroext false) #12, !dbg !59 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 8), i64** %13, align 8, !dbg !59, !tbaa !38 - %64 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !16 - %65 = load i64*, i64** %64, align 8, !dbg !16 - store i64 %2, i64* %65, align 8, !dbg !16, !tbaa !6 - %66 = getelementptr inbounds i64, i64* %65, i64 1, !dbg !16 - %67 = getelementptr inbounds i64, i64* %66, i64 1, !dbg !16 - %68 = getelementptr inbounds i64, i64* %67, i64 1, !dbg !16 - %69 = getelementptr inbounds i64, i64* %68, i64 1, !dbg !16 - %70 = bitcast i64* %66 to <4 x i64>*, !dbg !16 - store <4 x i64> , <4 x i64>* %70, align 8, !dbg !16, !tbaa !6 - %71 = getelementptr inbounds i64, i64* %69, i64 1, !dbg !16 - %72 = getelementptr inbounds i64, i64* %71, i64 1, !dbg !16 - %73 = bitcast i64* %71 to <2 x i64>*, !dbg !16 - store <2 x i64> , <2 x i64>* %73, align 8, !dbg !16, !tbaa !6 - %74 = getelementptr inbounds i64, i64* %72, i64 1, !dbg !16 - store i64 -13, i64* %74, align 8, !dbg !16, !tbaa !6 - %75 = getelementptr inbounds i64, i64* %74, i64 1, !dbg !16 - store i64 -15, i64* %75, align 8, !dbg !16, !tbaa !6 - %76 = getelementptr inbounds i64, i64* %75, i64 1, !dbg !16 - store i64* %76, i64** %64, align 8, !dbg !16 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_take_arguments, i64 0), !dbg !16 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 9), i64** %13, align 8, !dbg !16, !tbaa !38 - %77 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !18 - %78 = load i64*, i64** %77, align 8, !dbg !18 - store i64 %2, i64* %78, align 8, !dbg !18, !tbaa !6 - %79 = getelementptr inbounds i64, i64* %78, i64 1, !dbg !18 - %80 = getelementptr inbounds i64, i64* %79, i64 1, !dbg !18 - %81 = getelementptr inbounds i64, i64* %80, i64 1, !dbg !18 - %82 = getelementptr inbounds i64, i64* %81, i64 1, !dbg !18 - %83 = bitcast i64* %79 to <4 x i64>*, !dbg !18 - store <4 x i64> , <4 x i64>* %83, align 8, !dbg !18, !tbaa !6 - %84 = getelementptr inbounds i64, i64* %82, i64 1, !dbg !18 - %85 = getelementptr inbounds i64, i64* %84, i64 1, !dbg !18 - %86 = bitcast i64* %84 to <2 x i64>*, !dbg !18 - store <2 x i64> , <2 x i64>* %86, align 8, !dbg !18, !tbaa !6 - %87 = getelementptr inbounds i64, i64* %85, i64 1, !dbg !18 - store i64 -15, i64* %87, align 8, !dbg !18, !tbaa !6 - %88 = getelementptr inbounds i64, i64* %87, i64 1, !dbg !18 - store i64* %88, i64** %77, align 8, !dbg !18 - %send2 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_take_arguments.1, i64 0), !dbg !18 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 10), i64** %13, align 8, !dbg !18, !tbaa !38 - %89 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !19 - %90 = load i64*, i64** %89, align 8, !dbg !19 - store i64 %2, i64* %90, align 8, !dbg !19, !tbaa !6 - %91 = getelementptr inbounds i64, i64* %90, i64 1, !dbg !19 - %92 = getelementptr inbounds i64, i64* %91, i64 1, !dbg !19 - %93 = getelementptr inbounds i64, i64* %92, i64 1, !dbg !19 - %94 = getelementptr inbounds i64, i64* %93, i64 1, !dbg !19 - %95 = bitcast i64* %91 to <4 x i64>*, !dbg !19 - store <4 x i64> , <4 x i64>* %95, align 8, !dbg !19, !tbaa !6 - %96 = getelementptr inbounds i64, i64* %94, i64 1, !dbg !19 - %97 = getelementptr inbounds i64, i64* %96, i64 1, !dbg !19 - %98 = bitcast i64* %96 to <2 x i64>*, !dbg !19 - store <2 x i64> , <2 x i64>* %98, align 8, !dbg !19, !tbaa !6 - %99 = getelementptr inbounds i64, i64* %97, i64 1, !dbg !19 - store i64* %99, i64** %89, align 8, !dbg !19 - %send4 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_take_arguments.2, i64 0), !dbg !19 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 11), i64** %13, align 8, !dbg !19, !tbaa !38 - %100 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !20 - %101 = load i64*, i64** %100, align 8, !dbg !20 - store i64 %2, i64* %101, align 8, !dbg !20, !tbaa !6 - %102 = getelementptr inbounds i64, i64* %101, i64 1, !dbg !20 - %103 = getelementptr inbounds i64, i64* %102, i64 1, !dbg !20 - %104 = getelementptr inbounds i64, i64* %103, i64 1, !dbg !20 - %105 = getelementptr inbounds i64, i64* %104, i64 1, !dbg !20 - %106 = bitcast i64* %102 to <4 x i64>*, !dbg !20 - store <4 x i64> , <4 x i64>* %106, align 8, !dbg !20, !tbaa !6 - %107 = getelementptr inbounds i64, i64* %105, i64 1, !dbg !20 - store i64 -15, i64* %107, align 8, !dbg !20, !tbaa !6 - %108 = getelementptr inbounds i64, i64* %107, i64 1, !dbg !20 - store i64* %108, i64** %100, align 8, !dbg !20 - %send6 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_take_arguments.3, i64 0), !dbg !20 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 12), i64** %13, align 8, !dbg !20, !tbaa !38 - %109 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !21 - %110 = load i64*, i64** %109, align 8, !dbg !21 - store i64 %2, i64* %110, align 8, !dbg !21, !tbaa !6 - %111 = getelementptr inbounds i64, i64* %110, i64 1, !dbg !21 - %112 = getelementptr inbounds i64, i64* %111, i64 1, !dbg !21 - %113 = getelementptr inbounds i64, i64* %112, i64 1, !dbg !21 - %114 = getelementptr inbounds i64, i64* %113, i64 1, !dbg !21 - %115 = bitcast i64* %111 to <4 x i64>*, !dbg !21 - store <4 x i64> , <4 x i64>* %115, align 8, !dbg !21, !tbaa !6 - %116 = getelementptr inbounds i64, i64* %114, i64 1, !dbg !21 - store i64* %116, i64** %109, align 8, !dbg !21 - %send8 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_take_arguments.4, i64 0), !dbg !21 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 13), i64** %13, align 8, !dbg !21, !tbaa !38 - %117 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !22 - %118 = load i64*, i64** %117, align 8, !dbg !22 - store i64 %2, i64* %118, align 8, !dbg !22, !tbaa !6 - %119 = getelementptr inbounds i64, i64* %118, i64 1, !dbg !22 - %120 = getelementptr inbounds i64, i64* %119, i64 1, !dbg !22 - %121 = bitcast i64* %119 to <2 x i64>*, !dbg !22 - store <2 x i64> , <2 x i64>* %121, align 8, !dbg !22, !tbaa !6 - %122 = getelementptr inbounds i64, i64* %120, i64 1, !dbg !22 - store i64 -15, i64* %122, align 8, !dbg !22, !tbaa !6 - %123 = getelementptr inbounds i64, i64* %122, i64 1, !dbg !22 - store i64* %123, i64** %117, align 8, !dbg !22 - %send10 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_take_arguments.5, i64 0), !dbg !22 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 14), i64** %13, align 8, !dbg !22, !tbaa !38 - %124 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !23 - %125 = load i64*, i64** %124, align 8, !dbg !23 - store i64 %2, i64* %125, align 8, !dbg !23, !tbaa !6 - %126 = getelementptr inbounds i64, i64* %125, i64 1, !dbg !23 - %127 = getelementptr inbounds i64, i64* %126, i64 1, !dbg !23 - %128 = bitcast i64* %126 to <2 x i64>*, !dbg !23 - store <2 x i64> , <2 x i64>* %128, align 8, !dbg !23, !tbaa !6 - %129 = getelementptr inbounds i64, i64* %127, i64 1, !dbg !23 - store i64* %129, i64** %124, align 8, !dbg !23 - %send12 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_take_arguments.6, i64 0), !dbg !23 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 16), i64** %13, align 8, !dbg !23, !tbaa !38 - %130 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !24 - %131 = load i64*, i64** %130, align 8, !dbg !24 - store i64 %2, i64* %131, align 8, !dbg !24, !tbaa !6 - %132 = getelementptr inbounds i64, i64* %131, i64 1, !dbg !24 - %133 = getelementptr inbounds i64, i64* %132, i64 1, !dbg !24 - %134 = getelementptr inbounds i64, i64* %133, i64 1, !dbg !24 - %135 = getelementptr inbounds i64, i64* %134, i64 1, !dbg !24 - %136 = bitcast i64* %132 to <4 x i64>*, !dbg !24 - store <4 x i64> , <4 x i64>* %136, align 8, !dbg !24, !tbaa !6 - %137 = getelementptr inbounds i64, i64* %135, i64 1, !dbg !24 - %138 = getelementptr inbounds i64, i64* %137, i64 1, !dbg !24 - %139 = bitcast i64* %137 to <2 x i64>*, !dbg !24 - store <2 x i64> , <2 x i64>* %139, align 8, !dbg !24, !tbaa !6 - %140 = getelementptr inbounds i64, i64* %138, i64 1, !dbg !24 - store i64 -13, i64* %140, align 8, !dbg !24, !tbaa !6 - %141 = getelementptr inbounds i64, i64* %140, i64 1, !dbg !24 - store i64 -15, i64* %141, align 8, !dbg !24, !tbaa !6 - %142 = getelementptr inbounds i64, i64* %141, i64 1, !dbg !24 - store i64 -17, i64* %142, align 8, !dbg !24, !tbaa !6 - %143 = getelementptr inbounds i64, i64* %142, i64 1, !dbg !24 - store i64* %143, i64** %130, align 8, !dbg !24 - %send14 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_take_arguments.7, i64 0), !dbg !24 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 17), i64** %13, align 8, !dbg !24, !tbaa !38 - %144 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !25 - %145 = load i64*, i64** %144, align 8, !dbg !25 - store i64 %2, i64* %145, align 8, !dbg !25, !tbaa !6 - %146 = getelementptr inbounds i64, i64* %145, i64 1, !dbg !25 - %147 = getelementptr inbounds i64, i64* %146, i64 1, !dbg !25 - %148 = getelementptr inbounds i64, i64* %147, i64 1, !dbg !25 - %149 = getelementptr inbounds i64, i64* %148, i64 1, !dbg !25 - %150 = bitcast i64* %146 to <4 x i64>*, !dbg !25 - store <4 x i64> , <4 x i64>* %150, align 8, !dbg !25, !tbaa !6 - %151 = getelementptr inbounds i64, i64* %149, i64 1, !dbg !25 - %152 = getelementptr inbounds i64, i64* %151, i64 1, !dbg !25 - %153 = bitcast i64* %151 to <2 x i64>*, !dbg !25 - store <2 x i64> , <2 x i64>* %153, align 8, !dbg !25, !tbaa !6 - %154 = getelementptr inbounds i64, i64* %152, i64 1, !dbg !25 - store i64 -15, i64* %154, align 8, !dbg !25, !tbaa !6 - %155 = getelementptr inbounds i64, i64* %154, i64 1, !dbg !25 - store i64 -17, i64* %155, align 8, !dbg !25, !tbaa !6 - %156 = getelementptr inbounds i64, i64* %155, i64 1, !dbg !25 - store i64* %156, i64** %144, align 8, !dbg !25 - %send16 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_take_arguments.8, i64 0), !dbg !25 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 18), i64** %13, align 8, !dbg !25, !tbaa !38 - %157 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !26 - %158 = load i64*, i64** %157, align 8, !dbg !26 - store i64 %2, i64* %158, align 8, !dbg !26, !tbaa !6 - %159 = getelementptr inbounds i64, i64* %158, i64 1, !dbg !26 - %160 = getelementptr inbounds i64, i64* %159, i64 1, !dbg !26 - %161 = getelementptr inbounds i64, i64* %160, i64 1, !dbg !26 - %162 = getelementptr inbounds i64, i64* %161, i64 1, !dbg !26 - %163 = bitcast i64* %159 to <4 x i64>*, !dbg !26 - store <4 x i64> , <4 x i64>* %163, align 8, !dbg !26, !tbaa !6 - %164 = getelementptr inbounds i64, i64* %162, i64 1, !dbg !26 - %165 = getelementptr inbounds i64, i64* %164, i64 1, !dbg !26 - %166 = bitcast i64* %164 to <2 x i64>*, !dbg !26 - store <2 x i64> , <2 x i64>* %166, align 8, !dbg !26, !tbaa !6 - %167 = getelementptr inbounds i64, i64* %165, i64 1, !dbg !26 - store i64 -17, i64* %167, align 8, !dbg !26, !tbaa !6 - %168 = getelementptr inbounds i64, i64* %167, i64 1, !dbg !26 - store i64* %168, i64** %157, align 8, !dbg !26 - %send18 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_take_arguments.9, i64 0), !dbg !26 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 19), i64** %13, align 8, !dbg !26, !tbaa !38 - %169 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !27 - %170 = load i64*, i64** %169, align 8, !dbg !27 - store i64 %2, i64* %170, align 8, !dbg !27, !tbaa !6 - %171 = getelementptr inbounds i64, i64* %170, i64 1, !dbg !27 - %172 = getelementptr inbounds i64, i64* %171, i64 1, !dbg !27 - %173 = getelementptr inbounds i64, i64* %172, i64 1, !dbg !27 - %174 = getelementptr inbounds i64, i64* %173, i64 1, !dbg !27 - %175 = bitcast i64* %171 to <4 x i64>*, !dbg !27 - store <4 x i64> , <4 x i64>* %175, align 8, !dbg !27, !tbaa !6 - %176 = getelementptr inbounds i64, i64* %174, i64 1, !dbg !27 - %177 = getelementptr inbounds i64, i64* %176, i64 1, !dbg !27 - %178 = bitcast i64* %176 to <2 x i64>*, !dbg !27 - store <2 x i64> , <2 x i64>* %178, align 8, !dbg !27, !tbaa !6 - %179 = getelementptr inbounds i64, i64* %177, i64 1, !dbg !27 - store i64* %179, i64** %169, align 8, !dbg !27 - %send20 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_take_arguments.10, i64 0), !dbg !27 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 20), i64** %13, align 8, !dbg !27, !tbaa !38 - %180 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !28 - %181 = load i64*, i64** %180, align 8, !dbg !28 - store i64 %2, i64* %181, align 8, !dbg !28, !tbaa !6 - %182 = getelementptr inbounds i64, i64* %181, i64 1, !dbg !28 - %183 = getelementptr inbounds i64, i64* %182, i64 1, !dbg !28 - %184 = getelementptr inbounds i64, i64* %183, i64 1, !dbg !28 - %185 = getelementptr inbounds i64, i64* %184, i64 1, !dbg !28 - %186 = bitcast i64* %182 to <4 x i64>*, !dbg !28 - store <4 x i64> , <4 x i64>* %186, align 8, !dbg !28, !tbaa !6 - %187 = getelementptr inbounds i64, i64* %185, i64 1, !dbg !28 - store i64 -17, i64* %187, align 8, !dbg !28, !tbaa !6 - %188 = getelementptr inbounds i64, i64* %187, i64 1, !dbg !28 - store i64* %188, i64** %180, align 8, !dbg !28 - %send22 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_take_arguments.11, i64 0), !dbg !28 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 21), i64** %13, align 8, !dbg !28, !tbaa !38 - %189 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !29 - %190 = load i64*, i64** %189, align 8, !dbg !29 - store i64 %2, i64* %190, align 8, !dbg !29, !tbaa !6 - %191 = getelementptr inbounds i64, i64* %190, i64 1, !dbg !29 - %192 = getelementptr inbounds i64, i64* %191, i64 1, !dbg !29 - %193 = getelementptr inbounds i64, i64* %192, i64 1, !dbg !29 - %194 = getelementptr inbounds i64, i64* %193, i64 1, !dbg !29 - %195 = bitcast i64* %191 to <4 x i64>*, !dbg !29 - store <4 x i64> , <4 x i64>* %195, align 8, !dbg !29, !tbaa !6 - %196 = getelementptr inbounds i64, i64* %194, i64 1, !dbg !29 - store i64* %196, i64** %189, align 8, !dbg !29 - %send24 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_take_arguments.12, i64 0), !dbg !29 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 22), i64** %13, align 8, !dbg !29, !tbaa !38 - %197 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !30 - %198 = load i64*, i64** %197, align 8, !dbg !30 - store i64 %2, i64* %198, align 8, !dbg !30, !tbaa !6 - %199 = getelementptr inbounds i64, i64* %198, i64 1, !dbg !30 - %200 = getelementptr inbounds i64, i64* %199, i64 1, !dbg !30 - %201 = bitcast i64* %199 to <2 x i64>*, !dbg !30 - store <2 x i64> , <2 x i64>* %201, align 8, !dbg !30, !tbaa !6 - %202 = getelementptr inbounds i64, i64* %200, i64 1, !dbg !30 - store i64 -17, i64* %202, align 8, !dbg !30, !tbaa !6 - %203 = getelementptr inbounds i64, i64* %202, i64 1, !dbg !30 - store i64* %203, i64** %197, align 8, !dbg !30 - %send26 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_take_arguments.13, i64 0), !dbg !30 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 24), i64** %13, align 8, !dbg !30, !tbaa !38 - %204 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !31 - %205 = load i64*, i64** %204, align 8, !dbg !31 - store i64 %2, i64* %205, align 8, !dbg !31, !tbaa !6 - %206 = getelementptr inbounds i64, i64* %205, i64 1, !dbg !31 - %207 = getelementptr inbounds i64, i64* %206, i64 1, !dbg !31 - %208 = getelementptr inbounds i64, i64* %207, i64 1, !dbg !31 - %209 = getelementptr inbounds i64, i64* %208, i64 1, !dbg !31 - %210 = bitcast i64* %206 to <4 x i64>*, !dbg !31 - store <4 x i64> , <4 x i64>* %210, align 8, !dbg !31, !tbaa !6 - %211 = getelementptr inbounds i64, i64* %209, i64 1, !dbg !31 - %212 = getelementptr inbounds i64, i64* %211, i64 1, !dbg !31 - %213 = bitcast i64* %211 to <2 x i64>*, !dbg !31 - store <2 x i64> , <2 x i64>* %213, align 8, !dbg !31, !tbaa !6 - %214 = getelementptr inbounds i64, i64* %212, i64 1, !dbg !31 - store i64 -13, i64* %214, align 8, !dbg !31, !tbaa !6 - %215 = getelementptr inbounds i64, i64* %214, i64 1, !dbg !31 - store i64 -15, i64* %215, align 8, !dbg !31, !tbaa !6 - %216 = getelementptr inbounds i64, i64* %215, i64 1, !dbg !31 - store i64 -17, i64* %216, align 8, !dbg !31, !tbaa !6 - %217 = getelementptr inbounds i64, i64* %216, i64 1, !dbg !31 - store i64 -19, i64* %217, align 8, !dbg !31, !tbaa !6 - %218 = getelementptr inbounds i64, i64* %217, i64 1, !dbg !31 - store i64* %218, i64** %204, align 8, !dbg !31 - %send28 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_take_arguments.14, i64 0), !dbg !31 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 25), i64** %13, align 8, !dbg !31, !tbaa !38 - %219 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !32 - %220 = load i64*, i64** %219, align 8, !dbg !32 - store i64 %2, i64* %220, align 8, !dbg !32, !tbaa !6 - %221 = getelementptr inbounds i64, i64* %220, i64 1, !dbg !32 - %222 = getelementptr inbounds i64, i64* %221, i64 1, !dbg !32 - %223 = getelementptr inbounds i64, i64* %222, i64 1, !dbg !32 - %224 = getelementptr inbounds i64, i64* %223, i64 1, !dbg !32 - %225 = bitcast i64* %221 to <4 x i64>*, !dbg !32 - store <4 x i64> , <4 x i64>* %225, align 8, !dbg !32, !tbaa !6 - %226 = getelementptr inbounds i64, i64* %224, i64 1, !dbg !32 - %227 = getelementptr inbounds i64, i64* %226, i64 1, !dbg !32 - %228 = bitcast i64* %226 to <2 x i64>*, !dbg !32 - store <2 x i64> , <2 x i64>* %228, align 8, !dbg !32, !tbaa !6 - %229 = getelementptr inbounds i64, i64* %227, i64 1, !dbg !32 - store i64 -15, i64* %229, align 8, !dbg !32, !tbaa !6 - %230 = getelementptr inbounds i64, i64* %229, i64 1, !dbg !32 - store i64 -17, i64* %230, align 8, !dbg !32, !tbaa !6 - %231 = getelementptr inbounds i64, i64* %230, i64 1, !dbg !32 - store i64 -19, i64* %231, align 8, !dbg !32, !tbaa !6 - %232 = getelementptr inbounds i64, i64* %231, i64 1, !dbg !32 - store i64* %232, i64** %219, align 8, !dbg !32 - %send30 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_take_arguments.15, i64 0), !dbg !32 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 26), i64** %13, align 8, !dbg !32, !tbaa !38 - %233 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !33 - %234 = load i64*, i64** %233, align 8, !dbg !33 - store i64 %2, i64* %234, align 8, !dbg !33, !tbaa !6 - %235 = getelementptr inbounds i64, i64* %234, i64 1, !dbg !33 - %236 = getelementptr inbounds i64, i64* %235, i64 1, !dbg !33 - %237 = getelementptr inbounds i64, i64* %236, i64 1, !dbg !33 - %238 = getelementptr inbounds i64, i64* %237, i64 1, !dbg !33 - %239 = bitcast i64* %235 to <4 x i64>*, !dbg !33 - store <4 x i64> , <4 x i64>* %239, align 8, !dbg !33, !tbaa !6 - %240 = getelementptr inbounds i64, i64* %238, i64 1, !dbg !33 - %241 = getelementptr inbounds i64, i64* %240, i64 1, !dbg !33 - %242 = bitcast i64* %240 to <2 x i64>*, !dbg !33 - store <2 x i64> , <2 x i64>* %242, align 8, !dbg !33, !tbaa !6 - %243 = getelementptr inbounds i64, i64* %241, i64 1, !dbg !33 - store i64 -17, i64* %243, align 8, !dbg !33, !tbaa !6 - %244 = getelementptr inbounds i64, i64* %243, i64 1, !dbg !33 - store i64 -19, i64* %244, align 8, !dbg !33, !tbaa !6 - %245 = getelementptr inbounds i64, i64* %244, i64 1, !dbg !33 - store i64* %245, i64** %233, align 8, !dbg !33 - %send32 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_take_arguments.16, i64 0), !dbg !33 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 27), i64** %13, align 8, !dbg !33, !tbaa !38 - %246 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !34 - %247 = load i64*, i64** %246, align 8, !dbg !34 - store i64 %2, i64* %247, align 8, !dbg !34, !tbaa !6 - %248 = getelementptr inbounds i64, i64* %247, i64 1, !dbg !34 - %249 = getelementptr inbounds i64, i64* %248, i64 1, !dbg !34 - %250 = getelementptr inbounds i64, i64* %249, i64 1, !dbg !34 - %251 = getelementptr inbounds i64, i64* %250, i64 1, !dbg !34 - %252 = bitcast i64* %248 to <4 x i64>*, !dbg !34 - store <4 x i64> , <4 x i64>* %252, align 8, !dbg !34, !tbaa !6 - %253 = getelementptr inbounds i64, i64* %251, i64 1, !dbg !34 - %254 = getelementptr inbounds i64, i64* %253, i64 1, !dbg !34 - %255 = bitcast i64* %253 to <2 x i64>*, !dbg !34 - store <2 x i64> , <2 x i64>* %255, align 8, !dbg !34, !tbaa !6 - %256 = getelementptr inbounds i64, i64* %254, i64 1, !dbg !34 - store i64 -19, i64* %256, align 8, !dbg !34, !tbaa !6 - %257 = getelementptr inbounds i64, i64* %256, i64 1, !dbg !34 - store i64* %257, i64** %246, align 8, !dbg !34 - %send34 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_take_arguments.17, i64 0), !dbg !34 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 28), i64** %13, align 8, !dbg !34, !tbaa !38 - %258 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !35 - %259 = load i64*, i64** %258, align 8, !dbg !35 - store i64 %2, i64* %259, align 8, !dbg !35, !tbaa !6 - %260 = getelementptr inbounds i64, i64* %259, i64 1, !dbg !35 - %261 = getelementptr inbounds i64, i64* %260, i64 1, !dbg !35 - %262 = getelementptr inbounds i64, i64* %261, i64 1, !dbg !35 - %263 = getelementptr inbounds i64, i64* %262, i64 1, !dbg !35 - %264 = bitcast i64* %260 to <4 x i64>*, !dbg !35 - store <4 x i64> , <4 x i64>* %264, align 8, !dbg !35, !tbaa !6 - %265 = getelementptr inbounds i64, i64* %263, i64 1, !dbg !35 - %266 = getelementptr inbounds i64, i64* %265, i64 1, !dbg !35 - %267 = bitcast i64* %265 to <2 x i64>*, !dbg !35 - store <2 x i64> , <2 x i64>* %267, align 8, !dbg !35, !tbaa !6 - %268 = getelementptr inbounds i64, i64* %266, i64 1, !dbg !35 - store i64* %268, i64** %258, align 8, !dbg !35 - %send36 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_take_arguments.18, i64 0), !dbg !35 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 29), i64** %13, align 8, !dbg !35, !tbaa !38 - %269 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !36 - %270 = load i64*, i64** %269, align 8, !dbg !36 - store i64 %2, i64* %270, align 8, !dbg !36, !tbaa !6 - %271 = getelementptr inbounds i64, i64* %270, i64 1, !dbg !36 - %272 = getelementptr inbounds i64, i64* %271, i64 1, !dbg !36 - %273 = getelementptr inbounds i64, i64* %272, i64 1, !dbg !36 - %274 = getelementptr inbounds i64, i64* %273, i64 1, !dbg !36 - %275 = bitcast i64* %271 to <4 x i64>*, !dbg !36 - store <4 x i64> , <4 x i64>* %275, align 8, !dbg !36, !tbaa !6 - %276 = getelementptr inbounds i64, i64* %274, i64 1, !dbg !36 - store i64 -19, i64* %276, align 8, !dbg !36, !tbaa !6 - %277 = getelementptr inbounds i64, i64* %276, i64 1, !dbg !36 - store i64* %277, i64** %269, align 8, !dbg !36 - %send38 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_take_arguments.19, i64 0), !dbg !36 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 30), i64** %13, align 8, !dbg !36, !tbaa !38 - %278 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !37 - %279 = load i64*, i64** %278, align 8, !dbg !37 - store i64 %2, i64* %279, align 8, !dbg !37, !tbaa !6 - %280 = getelementptr inbounds i64, i64* %279, i64 1, !dbg !37 - %281 = getelementptr inbounds i64, i64* %280, i64 1, !dbg !37 - %282 = getelementptr inbounds i64, i64* %281, i64 1, !dbg !37 - %283 = getelementptr inbounds i64, i64* %282, i64 1, !dbg !37 - %284 = bitcast i64* %280 to <4 x i64>*, !dbg !37 - store <4 x i64> , <4 x i64>* %284, align 8, !dbg !37, !tbaa !6 - %285 = getelementptr inbounds i64, i64* %283, i64 1, !dbg !37 - store i64* %285, i64** %278, align 8, !dbg !37 - %send40 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_take_arguments.20, i64 0), !dbg !37 - call void @llvm.lifetime.end.p0i8(i64 32, i8* nonnull %6) - call void @llvm.lifetime.end.p0i8(i64 24, i8* nonnull %7) - ret void -} - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #5 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #5 - -attributes #0 = { nounwind readnone willreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #1 = { noreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #2 = { "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #3 = { inaccessiblememonly nofree nosync nounwind willreturn } -attributes #4 = { allocsize(0,1) "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #5 = { argmemonly nofree nosync nounwind willreturn } -attributes #6 = { nounwind ssp uwtable "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #7 = { nounwind sspreq uwtable } -attributes #8 = { ssp } -attributes #9 = { sspreq } -attributes #10 = { noreturn nounwind } -attributes #11 = { noreturn } -attributes #12 = { nounwind } -attributes #13 = { willreturn } -attributes #14 = { nounwind allocsize(0,1) } - -!llvm.module.flags = !{!0, !1, !2} -!llvm.dbg.cu = !{!3} - -!0 = !{i32 2, !"Debug Info Version", i32 3} -!1 = !{i32 4, !"cf-protection-return", i32 1} -!2 = !{i32 4, !"cf-protection-branch", i32 1} -!3 = distinct !DICompileUnit(language: DW_LANG_C, file: !4, producer: "Sorbet LLVM", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !5) -!4 = !DIFile(filename: "test/testdata/compiler/all_arguments.rb", directory: ".") -!5 = !{} -!6 = !{!7, !7, i64 0} -!7 = !{!"long", !8, i64 0} -!8 = !{!"omnipotent char", !9, i64 0} -!9 = !{!"Simple C/C++ TBAA"} -!10 = !DILocation(line: 5, column: 10, scope: !11) -!11 = distinct !DISubprogram(name: "Object#take_arguments", linkageName: "func_Object#14take_arguments", scope: null, file: !4, line: 4, type: !12, scopeLine: 4, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!12 = !DISubroutineType(types: !13) -!13 = !{!14} -!14 = !DIBasicType(name: "VALUE", size: 64, encoding: DW_ATE_signed) -!15 = !DILocation(line: 5, column: 5, scope: !11) -!16 = !DILocation(line: 8, column: 1, scope: !17) -!17 = distinct !DISubprogram(name: ".", linkageName: "func_.13", scope: null, file: !4, line: 4, type: !12, scopeLine: 4, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!18 = !DILocation(line: 9, column: 1, scope: !17) -!19 = !DILocation(line: 10, column: 1, scope: !17) -!20 = !DILocation(line: 11, column: 1, scope: !17) -!21 = !DILocation(line: 12, column: 1, scope: !17) -!22 = !DILocation(line: 13, column: 1, scope: !17) -!23 = !DILocation(line: 14, column: 1, scope: !17) -!24 = !DILocation(line: 16, column: 1, scope: !17) -!25 = !DILocation(line: 17, column: 1, scope: !17) -!26 = !DILocation(line: 18, column: 1, scope: !17) -!27 = !DILocation(line: 19, column: 1, scope: !17) -!28 = !DILocation(line: 20, column: 1, scope: !17) -!29 = !DILocation(line: 21, column: 1, scope: !17) -!30 = !DILocation(line: 22, column: 1, scope: !17) -!31 = !DILocation(line: 24, column: 1, scope: !17) -!32 = !DILocation(line: 25, column: 1, scope: !17) -!33 = !DILocation(line: 26, column: 1, scope: !17) -!34 = !DILocation(line: 27, column: 1, scope: !17) -!35 = !DILocation(line: 28, column: 1, scope: !17) -!36 = !DILocation(line: 29, column: 1, scope: !17) -!37 = !DILocation(line: 30, column: 1, scope: !17) -!38 = !{!39, !39, i64 0} -!39 = !{!"any pointer", !8, i64 0} -!40 = !DILocation(line: 4, column: 1, scope: !11) -!41 = !{!42, !7, i64 0} -!42 = !{!"RBasic", !7, i64 0, !7, i64 8} -!43 = !{!"branch_weights", i32 1, i32 2000} -!44 = !{!45, !39, i64 32} -!45 = !{!"rb_control_frame_struct", !39, i64 0, !39, i64 8, !39, i64 16, !7, i64 24, !39, i64 32, !39, i64 40, !39, i64 48} -!46 = !{!47} -!47 = distinct !{!47, !48, !"vm_get_ep: argument 0"} -!48 = distinct !{!48, !"vm_get_ep"} -!49 = !{!"branch_weights", i32 2000, i32 1} -!50 = !DILocation(line: 4, column: 23, scope: !11) -!51 = !DILocation(line: 4, column: 36, scope: !11) -!52 = !DILocation(line: 0, scope: !11) -!53 = !{!54} -!54 = distinct !{!54, !55, !"vm_get_ep: argument 0"} -!55 = distinct !{!55, !"vm_get_ep"} -!56 = !{!57} -!57 = distinct !{!57, !58, !"sorbet_buildArrayIntrinsic: argument 0"} -!58 = distinct !{!58, !"sorbet_buildArrayIntrinsic"} -!59 = !DILocation(line: 4, column: 1, scope: !17) -!60 = !{!61, !7, i64 400} -!61 = !{!"rb_vm_struct", !7, i64 0, !62, i64 8, !39, i64 192, !39, i64 200, !39, i64 208, !66, i64 216, !8, i64 224, !63, i64 264, !63, i64 280, !63, i64 296, !63, i64 312, !7, i64 328, !65, i64 336, !65, i64 340, !65, i64 344, !65, i64 344, !65, i64 344, !65, i64 344, !65, i64 348, !7, i64 352, !8, i64 360, !7, i64 400, !7, i64 408, !7, i64 416, !7, i64 424, !7, i64 432, !7, i64 440, !7, i64 448, !39, i64 456, !39, i64 464, !67, i64 472, !68, i64 992, !39, i64 1016, !39, i64 1024, !65, i64 1032, !65, i64 1036, !63, i64 1040, !8, i64 1056, !7, i64 1096, !7, i64 1104, !7, i64 1112, !7, i64 1120, !7, i64 1128, !65, i64 1136, !39, i64 1144, !39, i64 1152, !39, i64 1160, !39, i64 1168, !39, i64 1176, !39, i64 1184, !65, i64 1192, !69, i64 1200, !8, i64 1232} -!62 = !{!"rb_global_vm_lock_struct", !39, i64 0, !8, i64 8, !63, i64 48, !39, i64 64, !65, i64 72, !8, i64 80, !8, i64 128, !65, i64 176, !65, i64 180} -!63 = !{!"list_head", !64, i64 0} -!64 = !{!"list_node", !39, i64 0, !39, i64 8} -!65 = !{!"int", !8, i64 0} -!66 = !{!"long long", !8, i64 0} -!67 = !{!"", !8, i64 0} -!68 = !{!"rb_hook_list_struct", !39, i64 0, !65, i64 8, !65, i64 12, !65, i64 16} -!69 = !{!"", !7, i64 0, !7, i64 8, !7, i64 16, !7, i64 24} -!70 = !{!71, !39, i64 16} -!71 = !{!"rb_execution_context_struct", !39, i64 0, !7, i64 8, !39, i64 16, !39, i64 24, !39, i64 32, !65, i64 40, !65, i64 44, !39, i64 48, !39, i64 56, !39, i64 64, !7, i64 72, !7, i64 80, !39, i64 88, !7, i64 96, !39, i64 104, !39, i64 112, !7, i64 120, !7, i64 128, !8, i64 136, !8, i64 137, !7, i64 144, !72, i64 152} -!72 = !{!"", !39, i64 0, !39, i64 8, !7, i64 16, !8, i64 24} -!73 = !{!45, !39, i64 16} -!74 = !DILocation(line: 0, scope: !17) -!75 = !{!76, !65, i64 20} -!76 = !{!"rb_sorbet_param_struct", !77, i64 0, !65, i64 4, !65, i64 8, !65, i64 12, !65, i64 16, !65, i64 20, !65, i64 24, !65, i64 28, !39, i64 32, !65, i64 40, !65, i64 44, !65, i64 48, !65, i64 52, !39, i64 56} -!77 = !{!"", !65, i64 0, !65, i64 0, !65, i64 0, !65, i64 0, !65, i64 0, !65, i64 0, !65, i64 0, !65, i64 0, !65, i64 1, !65, i64 1} -!78 = !{!76, !65, i64 24} -!79 = !{!76, !65, i64 28} -!80 = !{!65, !65, i64 0} -!81 = !{!76, !39, i64 32} -!82 = !{!76, !65, i64 40} -!83 = !{!76, !65, i64 44} -!84 = !{!76, !65, i64 8} -!85 = !{!76, !65, i64 12} -!86 = !{!76, !65, i64 48} -!87 = !{!76, !65, i64 52} -!88 = !{!76, !39, i64 56} diff --git a/test/testdata/compiler/autoload__2.rb b/test/testdata/compiler/autoload__2.rb index fe8a05580c..7b48a08e05 100644 --- a/test/testdata/compiler/autoload__2.rb +++ b/test/testdata/compiler/autoload__2.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true +# typed: false # compiled: false class A diff --git a/test/testdata/compiler/block-optimizations/block_param_call.rb b/test/testdata/compiler/block-optimizations/block_param_call.rb index f59edfd8e4..17bd69faa2 100644 --- a/test/testdata/compiler/block-optimizations/block_param_call.rb +++ b/test/testdata/compiler/block-optimizations/block_param_call.rb @@ -1,27 +1,12 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL OPT def baz(&blk) blk.call("baz") end -# INITIAL-LABEL: define internal i64 @"func_Object#3baz" -# INITIAL-NOT: call i64 @sorbet_getMethodBlockAsProc -# INITIAL-NOT: call i64 @rb_block_proc -# INITIAL: call i64 @sorbet_vm_callBlock -# INITIAL-NOT: call i64 @sorbet_getMethodBlockAsProc -# INITIAL-NOT: call i64 @rb_block_proc -# INITIAL{LITERAL}: } -# OPT-LABEL: define internal i64 "func_Object#3baz" -# OPT-NOT: call i64 @sorbet_getMethodBlockAsProc -# OPT-NOT: call i64 @rb_block_proc -# OPT: call i64 @sorbet_vm_callBlock -# OPT-NOT: call i64 @sorbet_getMethodBlockAsProc -# OPT-NOT: call i64 @rb_block_proc -# OPT{LITERAL}: } baz do |s| p s diff --git a/test/testdata/compiler/block-optimizations/pass_block_param_explicit.rb b/test/testdata/compiler/block-optimizations/pass_block_param_explicit.rb index 6a49b3a2ee..95d3408e8f 100644 --- a/test/testdata/compiler/block-optimizations/pass_block_param_explicit.rb +++ b/test/testdata/compiler/block-optimizations/pass_block_param_explicit.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL -# run_filecheck: OPT def foo yield @@ -12,23 +10,7 @@ def bar(&blk) foo(&blk) end -# INITIAL-LABEL: "func_Object#3bar" -# INITIAL-NOT: call i64 @sorbet_getMethodBlockAsProc -# INITIAL-NOT: call i64 @rb_block_proc -# INITIAL: @sorbet_getPassedBlockHandler -# INITIAL: call i64 @sorbet_callFuncWithCache -# INITIAL-NOT: call i64 @sorbet_getMethodBlockAsProc -# INITIAL-NOT: call i64 @rb_block_proc -# INITIAL{LITERAL}: } -# OPT-LABEL: "func_Object#3bar" -# OPT-NOT: call i64 @sorbet_getMethodBlockAsProc -# OPT-NOT: call i64 @rb_block_proc -# OPT: @sorbet_getPassedBlockHandler -# OPT: call i64 @sorbet_callFuncWithCache -# OPT-NOT: call i64 @sorbet_getMethodBlockAsProc -# OPT-NOT: call i64 @rb_block_proc -# OPT{LITERAL}: } bar do puts "bar" diff --git a/test/testdata/compiler/block-optimizations/yield_block_param.rb b/test/testdata/compiler/block-optimizations/yield_block_param.rb index fa223d4b8b..f6da67c6ff 100644 --- a/test/testdata/compiler/block-optimizations/yield_block_param.rb +++ b/test/testdata/compiler/block-optimizations/yield_block_param.rb @@ -1,28 +1,12 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL -# run_filecheck: OPT def boo(&blk) yield end -# INITIAL-LABEL: define internal i64 @"func_Object#3boo" -# INITIAL-NOT: call i64 @sorbet_getMethodBlockAsProc -# INITIAL-NOT: call i64 @rb_block_proc -# INITIAL: call i64 @sorbet_vm_callBlock -# INITIAL-NOT: call i64 @sorbet_getMethodBlockAsProc -# INITIAL-NOT: call i64 @rb_block_proc -# INITIAL{LITERAL}: } -# OPT-LABEL: define internal i64 @"func_Object#3boo" -# OPT-NOT: call i64 @sorbet_getMethodBlockAsProc -# OPT-NOT: call i64 @rb_block_proc -# OPT: call i64 @sorbet_vm_callBlock -# OPT-NOT: call i64 @sorbet_getMethodBlockAsProc -# OPT-NOT: call i64 @rb_block_proc -# OPT{LITERAL}: } boo do puts "boohey" diff --git a/test/testdata/compiler/block-optimizations/yield_in_begin.rb b/test/testdata/compiler/block-optimizations/yield_in_begin.rb index dfb7e0c230..3f3e606a77 100644 --- a/test/testdata/compiler/block-optimizations/yield_in_begin.rb +++ b/test/testdata/compiler/block-optimizations/yield_in_begin.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL -# run_filecheck: OPT def boo(&blk) begin @@ -13,27 +11,8 @@ def boo(&blk) end end -# INITIAL-LABEL: define internal i64 @"func_Object#3boo" -# INITIAL-NOT: call i64 @sorbet_getMethodBlockAsProc -# INITIAL: call i64 @sorbet_run_exception_handling{{.*}} -# INITIAL-NOT: call i64 @sorbet_getMethodBlockAsProc -# INITIAL{LITERAL}: } -# INITIAL-LABEL: define internal i64 @"func_Object#3boo$block_1" -# INITIAL-NOT: call i64 @sorbet_getMethodBlockAsProc -# INITIAL-NOT: call i64 @rb_block_proc -# INITIAL: call i64 @sorbet_vm_callBlock -# INITIAL-NOT: call i64 @sorbet_getMethodBlockAsProc -# INITIAL-NOT: call i64 @rb_block_proc -# INITIAL{LITERAL}: } -# OPT-LABEL: define internal noundef i64 @"func_Object#3boo$block_1" -# OPT-NOT: call i64 @sorbet_getMethodBlockAsProc -# OPT-NOT: call i64 @rb_block_proc -# OPT: call i64 @sorbet_vm_callBlock -# OPT-NOT: call i64 @sorbet_getMethodBlockAsProc -# OPT-NOT: call i64 @rb_block_proc -# OPT{LITERAL}: } boo do puts "boohey" diff --git a/test/testdata/compiler/block-optimizations/yield_in_block.rb b/test/testdata/compiler/block-optimizations/yield_in_block.rb index e3a70fed08..da58569584 100644 --- a/test/testdata/compiler/block-optimizations/yield_in_block.rb +++ b/test/testdata/compiler/block-optimizations/yield_in_block.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL -# run_filecheck: OPT # Verify that we do reify the block when it's invoked in a block. @@ -13,21 +11,8 @@ def boo(array, &blk) end end -# INITIAL-LABEL: define internal i64 @"func_Object#3boo" -# INITIAL: call i64 @sorbet_getMethodBlockAsProc -# INITIAL{LITERAL}: } -# OPT-LABEL: define internal i64 @"func_Object#3boo" -# OPT: call i64 @rb_block_proc -# OPT{LITERAL}: } -# INITIAL-LABEL: define internal i64 @"func_Object#3boo$block_1" -# INITIAL-NOT: call i64 @sorbet_getMethodBlockAsProc -# INITIAL-NOT: call i64 @rb_block_proc -# INITIAL: @sorbet_i_send(%struct.FunctionInlineCache* @ic_call -# INITIAL-NOT: call i64 @sorbet_getMethodBlockAsProc -# INITIAL-NOT: call i64 @rb_block_proc -# INITIAL{LITERAL}: } boo([1, 2]) do puts "boohey" diff --git a/test/testdata/compiler/block-optimizations/yield_in_block_begin.rb b/test/testdata/compiler/block-optimizations/yield_in_block_begin.rb index 8b19f05639..b8e7935d30 100644 --- a/test/testdata/compiler/block-optimizations/yield_in_block_begin.rb +++ b/test/testdata/compiler/block-optimizations/yield_in_block_begin.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL -# run_filecheck: OPT # Verify that we do reify the block when it's invoked in a block, even inside begin. @@ -17,21 +15,8 @@ def boo(array, &blk) end end -# INITIAL-LABEL: define internal i64 @"func_Object#3boo" -# INITIAL: call i64 @sorbet_getMethodBlockAsProc -# INITIAL{LITERAL}: } -# OPT-LABEL: define internal i64 @"func_Object#3boo" -# OPT: call i64 @rb_block_proc -# OPT{LITERAL}: } -# INITIAL-LABEL: define internal i64 @"func_Object#3boo$block_2" -# INITIAL-NOT: call i64 @sorbet_getMethodBlockAsProc -# INITIAL-NOT: call i64 @rb_block_proc -# INITIAL: @sorbet_i_send(%struct.FunctionInlineCache* @ic_call -# INITIAL-NOT: call i64 @sorbet_getMethodBlockAsProc -# INITIAL-NOT: call i64 @rb_block_proc -# INITIAL{LITERAL}: } boo([1, 2]) do puts "boohey" diff --git a/test/testdata/compiler/block-optimizations/yield_in_ensure.rb b/test/testdata/compiler/block-optimizations/yield_in_ensure.rb index 0df5cfe2d5..84db8323c0 100644 --- a/test/testdata/compiler/block-optimizations/yield_in_ensure.rb +++ b/test/testdata/compiler/block-optimizations/yield_in_ensure.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL -# run_filecheck: OPT # Verify that we do reify the block when it's invoked in an ensure block. @@ -15,23 +13,8 @@ def boo(&blk) end end -# INITIAL-LABEL: define internal i64 @"func_Object#3boo" -# INITIAL: call i64 @sorbet_getMethodBlockAsProc -# INITIAL: call i64 @sorbet_run_exception_handling{{.*}} -# INITIAL{LITERAL}: } -# OPT-LABEL: define internal i64 @"func_Object#3boo" -# OPT: call i64 @rb_block_proc -# OPT: call i64 @sorbet_run_exception_handling{{.*}} -# OPT{LITERAL}: } -# INITIAL-LABEL: define internal i64 @"func_Object#3boo$block_3" -# INITIAL-NOT: call i64 @sorbet_getMethodBlockAsProc -# INITIAL-NOT: call i64 @rb_block_proc -# INITIAL: @sorbet_i_send(%struct.FunctionInlineCache* @ic_call -# INITIAL-NOT: call i64 @sorbet_getMethodBlockAsProc -# INITIAL-NOT: call i64 @rb_block_proc -# INITIAL{LITERAL}: } boo do puts "boohey" diff --git a/test/testdata/compiler/block-optimizations/yield_in_rescue.rb b/test/testdata/compiler/block-optimizations/yield_in_rescue.rb index d55bb51c4c..16ca7eedaa 100644 --- a/test/testdata/compiler/block-optimizations/yield_in_rescue.rb +++ b/test/testdata/compiler/block-optimizations/yield_in_rescue.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL -# run_filecheck: OPT # Verify that we do reify the block when it's invoked in a rescue block. @@ -16,23 +14,8 @@ def boo(&blk) end end -# INITIAL-LABEL: define internal i64 @"func_Object#3boo" -# INITIAL: call i64 @sorbet_getMethodBlockAsProc -# INITIAL: call i64 @sorbet_run_exception_handling{{.*}} -# INITIAL{LITERAL}: } -# OPT-LABEL: define internal i64 @"func_Object#3boo" -# OPT: call i64 @rb_block_proc -# OPT: call i64 @sorbet_run_exception_handling{{.*}} -# OPT{LITERAL}: } -# INITIAL-LABEL: define internal i64 @"func_Object#3boo$block_2" -# INITIAL-NOT: call i64 @sorbet_getMethodBlockAsProc -# INITIAL-NOT: call i64 @rb_block_proc -# INITIAL: @sorbet_i_send(%struct.FunctionInlineCache* @ic_call -# INITIAL-NOT: call i64 @sorbet_getMethodBlockAsProc -# INITIAL-NOT: call i64 @rb_block_proc -# INITIAL{LITERAL}: } boo do puts "boohey" diff --git a/test/testdata/compiler/block-optimizations/yield_noparams.rb b/test/testdata/compiler/block-optimizations/yield_noparams.rb index e5c9fe0867..7fa7bd0ead 100644 --- a/test/testdata/compiler/block-optimizations/yield_noparams.rb +++ b/test/testdata/compiler/block-optimizations/yield_noparams.rb @@ -1,28 +1,12 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL -# run_filecheck: OPT def foo yield end -# INITIAL-LABEL: define internal i64 @"func_Object#3foo" -# INITIAL-NOT: call i64 @sorbet_getMethodBlockAsProc -# INITIAL-NOT: call i64 @rb_block_proc -# INITIAL: call i64 @sorbet_vm_callBlock -# INITIAL-NOT: call i64 @sorbet_getMethodBlockAsProc -# INITIAL-NOT: call i64 @rb_block_proc -# INITIAL{LITERAL}: } -# OPT-LABEL: define internal i64 @"func_Object#3foo" -# OPT-NOT: call i64 @sorbet_getMethodBlockAsProc -# OPT-NOT: call i64 @rb_block_proc -# OPT: call i64 @sorbet_vm_callBlock -# OPT-NOT: call i64 @sorbet_getMethodBlockAsProc -# OPT-NOT: call i64 @rb_block_proc -# OPT{LITERAL}: } foo do puts "heey" diff --git a/test/testdata/compiler/block_arg_expand.opt.ll.exp b/test/testdata/compiler/block_arg_expand.opt.ll.exp deleted file mode 100644 index 44ec48703d..0000000000 --- a/test/testdata/compiler/block_arg_expand.opt.ll.exp +++ /dev/null @@ -1,1620 +0,0 @@ -; ModuleID = 'payload' -source_filename = "llvm-link" -target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } -%struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } -%struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } -%struct.anon.0 = type { i16, [2 x i8] } -%struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } -%struct.rb_iseq_location_struct = type { i64, i64, i64, i64, i32, %struct.rb_code_location_struct } -%struct.rb_code_location_struct = type { %struct.rb_code_position_struct, %struct.rb_code_position_struct } -%struct.rb_code_position_struct = type { i32, i32 } -%struct.iseq_insn_info = type { %struct.iseq_insn_info_entry*, i32*, i32, %struct.succ_index_table* } -%struct.iseq_insn_info_entry = type opaque -%struct.succ_index_table = type opaque -%struct.iseq_catch_table = type opaque -%union.iseq_inline_storage_entry = type { %struct.iseq_inline_cache_entry } -%struct.iseq_inline_cache_entry = type { i64, %struct.rb_cref_struct*, i64 } -%struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } -%struct.rb_scope_visi_struct = type { i8, [3 x i8] } -%struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } -%struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } -%union.anon.13 = type { %struct.rb_method_cfunc_struct } -%struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%struct.rb_calling_info = type { i64, i64, i32, i32 } -%union.anon.15 = type { i32 } -%struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.16 = type { i64, i64, i64, i64* } -%union.anon.17 = type { %struct.anon.18 } -%struct.anon.18 = type { i64, i32 } -%struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } -%struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } -%struct.rb_fiber_struct = type opaque -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } -%struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } -%struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.__pthread_cond_s } -%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } -%union.anon = type { i64 } -%struct.anon.5 = type { [65 x i64] } -%struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } -%struct.rb_event_hook_struct = type opaque -%struct.rb_postponed_job_struct = type opaque -%struct.list_head = type { %struct.list_node } -%struct.rb_objspace = type opaque -%struct.rb_at_exit_list = type { void (%struct.rb_vm_struct*)*, %struct.rb_at_exit_list* } -%struct.rb_builtin_function = type opaque -%struct.anon.6 = type { i64, i64, i64, i64 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } -%union.anon.9 = type { %union.pthread_cond_t } -%union.pthread_mutex_t = type { %struct.__pthread_mutex_s } -%struct.__pthread_mutex_s = type { i32, i32, i32, i32, i32, i16, i16, %struct.__pthread_internal_list } -%struct.__pthread_internal_list = type { %struct.__pthread_internal_list*, %struct.__pthread_internal_list* } -%struct.rb_unblock_callback = type { void (i8*)*, i8* } -%struct.rb_mutex_struct = type opaque -%struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.10 = type { %struct.anon.11 } -%struct.anon.11 = type { i64, i64, i32 } -%struct.st_table = type { i8, i8, i8, i32, %struct.st_hash_type*, i64, i64*, i64, i64, %struct.st_table_entry* } -%struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } -%struct.st_table_entry = type opaque -%struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } -%struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } -%struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } -%struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } -%struct.__sigset_t = type { [16 x i64] } -%struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } -%struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } -%struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } -%struct.rb_call_info_with_kwarg = type { %struct.rb_call_info, %struct.rb_call_info_kw_arg* } -%struct.rb_call_info_kw_arg = type { i32, [1 x i64] } -%struct.rb_captured_block = type { i64, i64*, %union.anon.20 } -%union.anon.20 = type { %struct.rb_iseq_struct* } -%struct.vm_ifunc = type { i64, i64, i64 (i64, i64, i32, i64*, i64)*, i8*, %struct.rb_code_position_struct } -%struct.iseq_inline_iv_cache_entry = type { i64, i64 } -%struct.RArray = type { %struct.iseq_inline_iv_cache_entry, %union.anon.28 } -%union.anon.28 = type { %struct.anon.29 } -%struct.anon.29 = type { i64, %union.anon, i64* } -%struct.sorbet_inlineIntrinsicEnv = type { i64, i64, i32, i64*, i64 } - -@ruby_current_execution_context_ptr = external local_unnamed_addr global %struct.rb_execution_context_struct*, align 8 -@rb_eRuntimeError = external local_unnamed_addr global i64, align 8 -@.str.8 = private unnamed_addr constant [95 x i8] c"sorbet_getBuildSCMRevision: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@.str.9 = private unnamed_addr constant [93 x i8] c"sorbet_getIsReleaseBuild: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@"stackFramePrecomputed_func_.13" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@iseqEncodedArray = internal global [32 x i64] zeroinitializer -@fileLineNumberInfo = internal global %struct.SorbetLineNumberInfo zeroinitializer -@"stackFramePrecomputed_func_.13$block_1" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"stackFramePrecomputed_func_.13$block_2" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"stackFramePrecomputed_func_.13$block_3" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"stackFramePrecomputed_func_.13$block_4" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"stackFramePrecomputed_func_.13$block_5" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"func_.13$block_1_ifunc" = internal unnamed_addr global i64 0 -@"ic_+" = internal global %struct.FunctionInlineCache zeroinitializer -@"func_.13$block_2_ifunc" = internal unnamed_addr global i64 0 -@ic_p = internal global %struct.FunctionInlineCache zeroinitializer -@"func_.13$block_3_ifunc" = internal unnamed_addr global i64 0 -@ic_p.5 = internal global %struct.FunctionInlineCache zeroinitializer -@"func_.13$block_4_ifunc" = internal unnamed_addr global i64 0 -@ic_p.8 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_p.9 = internal global %struct.FunctionInlineCache zeroinitializer -@"func_.13$block_5_ifunc" = internal unnamed_addr global i64 0 -@ic_p.12 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_p.13 = internal global %struct.FunctionInlineCache zeroinitializer -@sorbet_moduleStringTable = internal unnamed_addr constant [163 x i8] c"\00test/testdata/compiler/block_arg_expand.rb\00\00block in \00\00each\00T.let\00Integer\00+\00p\00x\00default\00something\00master\00", align 1 -@sorbet_moduleIDTable = internal unnamed_addr global [10 x i64] zeroinitializer, align 8 -@sorbet_moduleIDDescriptors = internal unnamed_addr constant [10 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 16 }, %struct.rb_code_position_struct { i32 60, i32 12 }, %struct.rb_code_position_struct { i32 73, i32 25 }, %struct.rb_code_position_struct { i32 99, i32 13 }, %struct.rb_code_position_struct { i32 113, i32 4 }, %struct.rb_code_position_struct { i32 132, i32 1 }, %struct.rb_code_position_struct { i32 134, i32 1 }, %struct.rb_code_position_struct { i32 136, i32 1 }, %struct.rb_code_position_struct { i32 138, i32 7 }, %struct.rb_code_position_struct { i32 146, i32 9 }], align 8 -@sorbet_moduleRubyStringTable = internal unnamed_addr global [3 x i64] zeroinitializer, align 8 -@sorbet_moduleRubyStringDescriptors = internal unnamed_addr constant [3 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 16 }, %struct.rb_code_position_struct { i32 17, i32 42 }, %struct.rb_code_position_struct { i32 73, i32 25 }], align 8 - -; Function Attrs: nounwind readnone willreturn -declare i64 @rb_id2sym(i64) local_unnamed_addr #0 - -; Function Attrs: cold noreturn -declare void @sorbet_cast_failure(i64, i8*, i8*) local_unnamed_addr #1 - -declare %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64, i64, i64, i64, %struct.rb_iseq_struct*, i32, i32, %struct.SorbetLineNumberInfo*, i64*, i32, i32) local_unnamed_addr #2 - -declare void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo*, i64*, i32) local_unnamed_addr #2 - -declare i64 @sorbet_readRealpath() local_unnamed_addr #2 - -declare void @sorbet_pushBlockFrame(%struct.rb_captured_block*) local_unnamed_addr #2 - -declare void @sorbet_popFrame() local_unnamed_addr #2 - -declare void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) local_unnamed_addr #2 - -declare i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache*, i64) local_unnamed_addr #2 - -declare void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_iseq_struct*) local_unnamed_addr #2 - -declare i64 @sorbet_rb_int_plus_slowpath(i64, i64) local_unnamed_addr #2 - -declare i64 @sorbet_globalConstRegister(i64) local_unnamed_addr #2 - -declare %struct.vm_ifunc* @sorbet_globalConstFetchIfunc(i64) local_unnamed_addr #2 - -declare void @sorbet_vm_intern_ids(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #2 - -declare void @sorbet_vm_init_string_table(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #2 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #3 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #3 - -declare void @rb_ary_detransient(i64) local_unnamed_addr #2 - -declare i64 @rb_ary_new_from_values(i64, i64*) local_unnamed_addr #2 - -; Function Attrs: noreturn -declare void @rb_raise(i64, i8*, ...) local_unnamed_addr #4 - -declare i64 @rb_int2big(i64) local_unnamed_addr #2 - -; Function Attrs: nofree nosync nounwind readnone speculatable willreturn -declare { i64, i1 } @llvm.sadd.with.overflow.i64(i64, i64) #5 - -; Function Attrs: inaccessiblememonly nofree nosync nounwind willreturn -declare void @llvm.experimental.noalias.scope.decl(metadata) #6 - -declare i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct*, i32) local_unnamed_addr #2 - -declare %struct.vm_ifunc* @rb_vm_ifunc_new(i64 (i64, i64, i32, i64*, i64)*, i8*, i32, i32) local_unnamed_addr #2 - -; Function Attrs: nounwind ssp uwtable -define weak i32 @sorbet_getIsReleaseBuild() local_unnamed_addr #7 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([93 x i8], [93 x i8]* @.str.9, i64 0, i64 0)) #12 - unreachable -} - -; Function Attrs: nounwind ssp uwtable -define weak i8* @sorbet_getBuildSCMRevision() local_unnamed_addr #7 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([95 x i8], [95 x i8]* @.str.8, i64 0, i64 0)) #12 - unreachable -} - -; Function Attrs: ssp -define internal i64 @"func_.13$block_1"(i64 %firstYieldArgRaw, i64 %localsOffset, i32 %argc, i64* nocapture readonly %argArray, i64 %blockArg) #8 !dbg !10 { -functionEntryInitializers: - %0 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !15 - %1 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %0, i64 0, i32 2 - %2 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %1, align 8, !tbaa !17 - %stackFrame = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13$block_1", align 8 - %3 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame, %struct.rb_iseq_struct** %3, align 8, !tbaa !21 - %4 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 4 - %5 = load i64*, i64** %4, align 8, !tbaa !23 - %6 = load i64, i64* %5, align 8, !tbaa !6 - %7 = and i64 %6, -129 - store i64 %7, i64* %5, align 8, !tbaa !6 - %8 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 0 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %8, align 8, !tbaa !15 - %arrayExpansionSizeGuard = icmp eq i32 %argc, 1, !dbg !24 - br i1 %arrayExpansionSizeGuard, label %argArrayExpandArrayTest, label %fillRequiredArgs, !dbg !24 - -argArrayExpandArrayTest: ; preds = %functionEntryInitializers - %arg1_maybeExpandToFullArgs = load i64, i64* %argArray, align 8, !dbg !24 - %9 = and i64 %arg1_maybeExpandToFullArgs, 7, !dbg !24 - %10 = icmp ne i64 %9, 0, !dbg !24 - %11 = and i64 %arg1_maybeExpandToFullArgs, -9, !dbg !24 - %12 = icmp eq i64 %11, 0, !dbg !24 - %13 = or i1 %10, %12, !dbg !24 - br i1 %13, label %fillFromDefaultBlockDone2, label %sorbet_isa_Array.exit, !dbg !24 - -sorbet_isa_Array.exit: ; preds = %argArrayExpandArrayTest - %14 = inttoptr i64 %arg1_maybeExpandToFullArgs to %struct.iseq_inline_iv_cache_entry*, !dbg !24 - %15 = getelementptr inbounds %struct.iseq_inline_iv_cache_entry, %struct.iseq_inline_iv_cache_entry* %14, i64 0, i32 0, !dbg !24 - %16 = load i64, i64* %15, align 8, !dbg !24, !tbaa !25 - %17 = and i64 %16, 31, !dbg !24 - %18 = icmp eq i64 %17, 7, !dbg !24 - br i1 %18, label %argArrayExpand, label %fillFromDefaultBlockDone2, !dbg !24 - -argArrayExpand: ; preds = %sorbet_isa_Array.exit - %19 = inttoptr i64 %arg1_maybeExpandToFullArgs to %struct.iseq_inline_iv_cache_entry*, !dbg !24 - %20 = getelementptr inbounds %struct.iseq_inline_iv_cache_entry, %struct.iseq_inline_iv_cache_entry* %19, i64 0, i32 0, !dbg !24 - %21 = load i64, i64* %20, align 8, !dbg !24, !tbaa !25 - %22 = and i64 %21, 33554432, !dbg !24 - %23 = icmp eq i64 %22, 0, !dbg !24 - br i1 %23, label %25, label %24, !dbg !24 - -24: ; preds = %argArrayExpand - tail call void @rb_ary_detransient(i64 %arg1_maybeExpandToFullArgs) #13, !dbg !24 - br label %25, !dbg !24 - -25: ; preds = %24, %argArrayExpand - %26 = load i64, i64* %20, align 8, !dbg !24, !tbaa !25 - %27 = and i64 %26, 8192, !dbg !24 - %28 = icmp eq i64 %27, 0, !dbg !24 - %29 = inttoptr i64 %arg1_maybeExpandToFullArgs to %struct.RArray*, !dbg !24 - br i1 %28, label %34, label %30, !dbg !24 - -30: ; preds = %25 - %31 = getelementptr inbounds %struct.RArray, %struct.RArray* %29, i64 0, i32 1, i32 0, i32 0, !dbg !24 - %32 = lshr i64 %26, 15, !dbg !24 - %33 = and i64 %32, 3, !dbg !24 - br label %rb_array_len.exit, !dbg !24 - -34: ; preds = %25 - %35 = getelementptr inbounds %struct.RArray, %struct.RArray* %29, i64 0, i32 1, i32 0, i32 2, !dbg !24 - %36 = load i64*, i64** %35, align 8, !dbg !24, !tbaa !27 - %37 = getelementptr inbounds %struct.RArray, %struct.RArray* %29, i64 0, i32 1, i32 0, i32 0, !dbg !24 - %38 = load i64, i64* %37, align 8, !dbg !24, !tbaa !27 - br label %rb_array_len.exit, !dbg !24 - -rb_array_len.exit: ; preds = %30, %34 - %39 = phi i64* [ %31, %30 ], [ %36, %34 ] - %40 = phi i64 [ %33, %30 ], [ %38, %34 ], !dbg !24 - %41 = trunc i64 %40 to i32, !dbg !24 - br label %fillRequiredArgs, !dbg !24 - -fillFromArgBlock0: ; preds = %fillRequiredArgs - %rawArg_el1 = load i64, i64* %argArrayPhi, align 8, !dbg !24 - %default1 = icmp eq i32 %argcPhi, 1, !dbg !24 - br i1 %default1, label %fillFromDefaultBlockDone2, label %fillFromArgBlock1, !dbg !24, !prof !28 - -fillFromArgBlock1: ; preds = %fillFromArgBlock0 - %42 = getelementptr i64, i64* %argArrayPhi, i32 1, !dbg !24 - %rawArg_el2 = load i64, i64* %42, align 8, !dbg !24 - br label %fillFromDefaultBlockDone2, !dbg !24 - -fillFromDefaultBlockDone2: ; preds = %argArrayExpandArrayTest, %sorbet_isa_Array.exit, %fillFromArgBlock0, %fillFromArgBlock1 - %el2.sroa.0.0 = phi i64 [ %rawArg_el2, %fillFromArgBlock1 ], [ 8, %fillFromArgBlock0 ], [ 8, %sorbet_isa_Array.exit ], [ 8, %argArrayExpandArrayTest ], !dbg !24 - %el1.sroa.0.1 = phi i64 [ %rawArg_el1, %fillFromArgBlock1 ], [ %rawArg_el1, %fillFromArgBlock0 ], [ %arg1_maybeExpandToFullArgs, %sorbet_isa_Array.exit ], [ %arg1_maybeExpandToFullArgs, %argArrayExpandArrayTest ], !dbg !24 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 9), i64** %8, align 8, !dbg !29, !tbaa !15 - %43 = and i64 %el1.sroa.0.1, 1, !dbg !30 - %44 = icmp eq i64 %43, 0, !dbg !30 - br i1 %44, label %45, label %typeTestSuccess, !dbg !30, !prof !28 - -45: ; preds = %fillFromDefaultBlockDone2.thread, %fillFromDefaultBlockDone2 - %el1.sroa.0.148 = phi i64 [ 8, %fillFromDefaultBlockDone2.thread ], [ %el1.sroa.0.1, %fillFromDefaultBlockDone2 ] - %el2.sroa.0.045 = phi i64 [ 8, %fillFromDefaultBlockDone2.thread ], [ %el2.sroa.0.0, %fillFromDefaultBlockDone2 ] - %46 = and i64 %el1.sroa.0.148, 7, !dbg !30 - %47 = icmp ne i64 %46, 0, !dbg !30 - %48 = and i64 %el1.sroa.0.148, -9, !dbg !30 - %49 = icmp eq i64 %48, 0, !dbg !30 - %50 = or i1 %47, %49, !dbg !30 - br i1 %50, label %codeRepl40, label %sorbet_isa_Integer.exit, !dbg !30 - -sorbet_isa_Integer.exit: ; preds = %45 - %51 = inttoptr i64 %el1.sroa.0.148 to %struct.iseq_inline_iv_cache_entry*, !dbg !30 - %52 = getelementptr inbounds %struct.iseq_inline_iv_cache_entry, %struct.iseq_inline_iv_cache_entry* %51, i64 0, i32 0, !dbg !30 - %53 = load i64, i64* %52, align 8, !dbg !30, !tbaa !25 - %54 = and i64 %53, 31, !dbg !30 - %55 = icmp eq i64 %54, 10, !dbg !30 - br i1 %55, label %typeTestSuccess, label %codeRepl40, !dbg !30, !prof !31 - -fillRequiredArgs: ; preds = %functionEntryInitializers, %rb_array_len.exit - %argcPhi = phi i32 [ %argc, %functionEntryInitializers ], [ %41, %rb_array_len.exit ], !dbg !24 - %argArrayPhi = phi i64* [ %argArray, %functionEntryInitializers ], [ %39, %rb_array_len.exit ], !dbg !24 - %default0 = icmp eq i32 %argcPhi, 0, !dbg !24 - br i1 %default0, label %fillFromDefaultBlockDone2.thread, label %fillFromArgBlock0, !dbg !24, !prof !28 - -fillFromDefaultBlockDone2.thread: ; preds = %fillRequiredArgs - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 9), i64** %8, align 8, !dbg !29, !tbaa !15 - br label %45, !dbg !30 - -typeTestSuccess: ; preds = %fillFromDefaultBlockDone2, %sorbet_isa_Integer.exit - %el1.sroa.0.147 = phi i64 [ %el1.sroa.0.1, %fillFromDefaultBlockDone2 ], [ %el1.sroa.0.148, %sorbet_isa_Integer.exit ] - %el2.sroa.0.044 = phi i64 [ %el2.sroa.0.0, %fillFromDefaultBlockDone2 ], [ %el2.sroa.0.045, %sorbet_isa_Integer.exit ] - %56 = and i64 %el2.sroa.0.044, 1, !dbg !32 - %57 = icmp eq i64 %56, 0, !dbg !32 - br i1 %57, label %58, label %"fastSymCallIntrinsic_Integer_+", !dbg !32, !prof !28 - -58: ; preds = %typeTestSuccess - %59 = and i64 %el2.sroa.0.044, 7, !dbg !32 - %60 = icmp ne i64 %59, 0, !dbg !32 - %61 = and i64 %el2.sroa.0.044, -9, !dbg !32 - %62 = icmp eq i64 %61, 0, !dbg !32 - %63 = or i1 %60, %62, !dbg !32 - br i1 %63, label %codeRepl, label %sorbet_isa_Integer.exit41, !dbg !32 - -sorbet_isa_Integer.exit41: ; preds = %58 - %64 = inttoptr i64 %el2.sroa.0.044 to %struct.iseq_inline_iv_cache_entry*, !dbg !32 - %65 = getelementptr inbounds %struct.iseq_inline_iv_cache_entry, %struct.iseq_inline_iv_cache_entry* %64, i64 0, i32 0, !dbg !32 - %66 = load i64, i64* %65, align 8, !dbg !32, !tbaa !25 - %67 = and i64 %66, 31, !dbg !32 - %68 = icmp eq i64 %67, 10, !dbg !32 - br i1 %68, label %"fastSymCallIntrinsic_Integer_+", label %codeRepl, !dbg !32, !prof !31 - -codeRepl40: ; preds = %45, %sorbet_isa_Integer.exit - %el1.sroa.0.149 = phi i64 [ %el1.sroa.0.148, %45 ], [ %el1.sroa.0.148, %sorbet_isa_Integer.exit ] - tail call fastcc void @"func_.13$block_1.cold.1"(i64 %el1.sroa.0.149) #14, !dbg !30 - unreachable - -codeRepl: ; preds = %58, %sorbet_isa_Integer.exit41 - %el2.sroa.0.046 = phi i64 [ %el2.sroa.0.044, %58 ], [ %el2.sroa.0.044, %sorbet_isa_Integer.exit41 ] - tail call fastcc void @"func_.13$block_1.cold.1"(i64 %el2.sroa.0.046) #14, !dbg !32 - unreachable - -"fastSymCallIntrinsic_Integer_+": ; preds = %typeTestSuccess, %sorbet_isa_Integer.exit41 - tail call void @llvm.experimental.noalias.scope.decl(metadata !33), !dbg !30 - %69 = and i64 %el2.sroa.0.044, 1, !dbg !30 - %70 = and i64 %69, %el1.sroa.0.147, !dbg !30 - %71 = icmp eq i64 %70, 0, !dbg !30 - br i1 %71, label %81, label %72, !dbg !30, !prof !36 - -72: ; preds = %"fastSymCallIntrinsic_Integer_+" - %73 = add nsw i64 %el2.sroa.0.044, -1, !dbg !30 - %74 = tail call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %el1.sroa.0.147, i64 %73) #15, !dbg !30 - %75 = extractvalue { i64, i1 } %74, 1, !dbg !30 - %76 = extractvalue { i64, i1 } %74, 0, !dbg !30 - br i1 %75, label %77, label %sorbet_rb_int_plus.exit, !dbg !30 - -77: ; preds = %72 - %78 = ashr i64 %76, 1, !dbg !30 - %79 = xor i64 %78, -9223372036854775808, !dbg !30 - %80 = tail call i64 @rb_int2big(i64 %79) #13, !dbg !30, !noalias !33 - br label %sorbet_rb_int_plus.exit, !dbg !30 - -81: ; preds = %"fastSymCallIntrinsic_Integer_+" - %82 = tail call i64 @sorbet_rb_int_plus_slowpath(i64 %el1.sroa.0.147, i64 %el2.sroa.0.044) #13, !dbg !30, !noalias !33 - br label %sorbet_rb_int_plus.exit, !dbg !30 - -sorbet_rb_int_plus.exit: ; preds = %77, %72, %81 - %83 = phi i64 [ %82, %81 ], [ %80, %77 ], [ %76, %72 ], !dbg !30 - %84 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !30, !tbaa !15 - %85 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %84, i64 0, i32 5, !dbg !30 - %86 = load i32, i32* %85, align 8, !dbg !30, !tbaa !37 - %87 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %84, i64 0, i32 6, !dbg !30 - %88 = load i32, i32* %87, align 4, !dbg !30, !tbaa !38 - %89 = xor i32 %88, -1, !dbg !30 - %90 = and i32 %89, %86, !dbg !30 - %91 = icmp eq i32 %90, 0, !dbg !30 - br i1 %91, label %rb_vm_check_ints.exit, label %92, !dbg !30, !prof !31 - -92: ; preds = %sorbet_rb_int_plus.exit - %93 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %84, i64 0, i32 8, !dbg !30 - %94 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %93, align 8, !dbg !30, !tbaa !39 - %95 = tail call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %94, i32 noundef 0) #13, !dbg !30 - br label %rb_vm_check_ints.exit, !dbg !30 - -rb_vm_check_ints.exit: ; preds = %sorbet_rb_int_plus.exit, %92 - ret i64 %83, !dbg !30 -} - -; Function Attrs: ssp -define internal i64 @"func_.13$block_2"(i64 %firstYieldArgRaw, i64 %localsOffset, i32 %argc, i64* nocapture readonly %argArray, i64 %blockArg) #8 !dbg !40 { -functionEntryInitializers: - %0 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !15 - %1 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %0, i64 0, i32 2 - %2 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %1, align 8, !tbaa !17 - %3 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 3 - %4 = load i64, i64* %3, align 8, !tbaa !41 - %stackFrame = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13$block_2", align 8 - %5 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame, %struct.rb_iseq_struct** %5, align 8, !tbaa !21 - %6 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 4 - %7 = load i64*, i64** %6, align 8, !tbaa !23 - %8 = load i64, i64* %7, align 8, !tbaa !6 - %9 = and i64 %8, -129 - store i64 %9, i64* %7, align 8, !tbaa !6 - %10 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 0 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %10, align 8, !tbaa !15 - %default0 = icmp eq i32 %argc, 0, !dbg !42 - br i1 %default0, label %fillFromDefaultBlockDone1, label %fillFromArgBlock0, !dbg !42, !prof !28 - -fillFromArgBlock0: ; preds = %functionEntryInitializers - %rawArg_array = load i64, i64* %argArray, align 8, !dbg !42 - br label %fillFromDefaultBlockDone1, !dbg !42 - -fillFromDefaultBlockDone1: ; preds = %functionEntryInitializers, %fillFromArgBlock0 - %array.sroa.0.0 = phi i64 [ %rawArg_array, %fillFromArgBlock0 ], [ 8, %functionEntryInitializers ], !dbg !42 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 14), i64** %10, align 8, !dbg !43, !tbaa !15 - %11 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 1, !dbg !44 - %12 = load i64*, i64** %11, align 8, !dbg !44 - store i64 %4, i64* %12, align 8, !dbg !44, !tbaa !6 - %13 = getelementptr inbounds i64, i64* %12, i64 1, !dbg !44 - store i64 %array.sroa.0.0, i64* %13, align 8, !dbg !44, !tbaa !6 - %14 = getelementptr inbounds i64, i64* %13, i64 1, !dbg !44 - store i64* %14, i64** %11, align 8, !dbg !44 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_p, i64 0), !dbg !44 - ret i64 %send, !dbg !44 -} - -; Function Attrs: ssp -define internal i64 @"func_.13$block_3"(i64 %firstYieldArgRaw, i64 %localsOffset, i32 %argc, i64* nocapture readonly %argArray, i64 %blockArg) #8 !dbg !45 { -functionEntryInitializers: - %0 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !15 - %1 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %0, i64 0, i32 2 - %2 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %1, align 8, !tbaa !17 - %3 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 3 - %4 = load i64, i64* %3, align 8, !tbaa !41 - %stackFrame = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13$block_3", align 8 - %5 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame, %struct.rb_iseq_struct** %5, align 8, !tbaa !21 - %6 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 4 - %7 = load i64*, i64** %6, align 8, !tbaa !23 - %8 = load i64, i64* %7, align 8, !tbaa !6 - %9 = and i64 %8, -129 - store i64 %9, i64* %7, align 8, !tbaa !6 - %10 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 0 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %10, align 8, !tbaa !15 - %default0 = icmp eq i32 %argc, 0, !dbg !46 - br i1 %default0, label %BB15, label %BB14, !dbg !46, !prof !28 - -BB14: ; preds = %functionEntryInitializers - %rawArg_array = load i64, i64* %argArray, align 8, !dbg !46 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 18), i64** %10, align 8, !tbaa !15 - br label %BB16, !dbg !47 - -BB15: ; preds = %functionEntryInitializers - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 18), i64** %10, align 8, !tbaa !15 - %rubyId_x = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 7), align 8, !dbg !48, !invariant.load !5 - %rawSym = tail call i64 @rb_id2sym(i64 %rubyId_x) #16, !dbg !48 - br label %BB16, !dbg !48 - -BB16: ; preds = %BB15, %BB14 - %array.sroa.0.0 = phi i64 [ %rawArg_array, %BB14 ], [ %rawSym, %BB15 ], !dbg !49 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 19), i64** %10, align 8, !tbaa !15 - %11 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 1, !dbg !50 - %12 = load i64*, i64** %11, align 8, !dbg !50 - store i64 %4, i64* %12, align 8, !dbg !50, !tbaa !6 - %13 = getelementptr inbounds i64, i64* %12, i64 1, !dbg !50 - store i64 %array.sroa.0.0, i64* %13, align 8, !dbg !50, !tbaa !6 - %14 = getelementptr inbounds i64, i64* %13, i64 1, !dbg !50 - store i64* %14, i64** %11, align 8, !dbg !50 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_p.5, i64 0), !dbg !50 - ret i64 %send, !dbg !50 -} - -; Function Attrs: ssp -define internal i64 @"func_.13$block_4"(i64 %firstYieldArgRaw, i64 %localsOffset, i32 %argc, i64* nocapture readonly %argArray, i64 %blockArg) #8 !dbg !51 { -functionEntryInitializers: - %0 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !15 - %1 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %0, i64 0, i32 2 - %2 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %1, align 8, !tbaa !17 - %3 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 3 - %4 = load i64, i64* %3, align 8, !tbaa !41 - %stackFrame = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13$block_4", align 8 - %5 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame, %struct.rb_iseq_struct** %5, align 8, !tbaa !21 - %6 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 4 - %7 = load i64*, i64** %6, align 8, !tbaa !23 - %8 = load i64, i64* %7, align 8, !tbaa !6 - %9 = and i64 %8, -129 - store i64 %9, i64* %7, align 8, !tbaa !6 - %10 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 0 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %10, align 8, !tbaa !15 - %arrayExpansionSizeGuard = icmp eq i32 %argc, 1, !dbg !52 - br i1 %arrayExpansionSizeGuard, label %argArrayExpandArrayTest, label %fillRequiredArgs, !dbg !52 - -BB23.thread: ; preds = %fillRequiredArgs - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 23), i64** %10, align 8, !tbaa !15 - %rubyId_default = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 8), align 8, !dbg !53, !invariant.load !5 - %rawSym = tail call i64 @rb_id2sym(i64 %rubyId_default) #16, !dbg !53 - br label %BB25, !dbg !54 - -BB23.thread61: ; preds = %argArrayExpandArrayTest, %sorbet_isa_Array.exit, %fillFromArgBlock0 - %x.sroa.0.2.ph.ph = phi i64 [ %rawArg_x, %fillFromArgBlock0 ], [ %arg1_maybeExpandToFullArgs, %sorbet_isa_Array.exit ], [ %arg1_maybeExpandToFullArgs, %argArrayExpandArrayTest ] - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 23), i64** %10, align 8, !tbaa !15 - br label %BB25, !dbg !54 - -BB24: ; preds = %fillFromArgBlock0 - %11 = getelementptr i64, i64* %argArrayPhi, i32 1, !dbg !52 - %rawArg_y = load i64, i64* %11, align 8, !dbg !52 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 23), i64** %10, align 8, !tbaa !15 - br label %BB26, !dbg !54 - -BB25: ; preds = %BB23.thread61, %BB23.thread - %x.sroa.0.060 = phi i64 [ %rawSym, %BB23.thread ], [ %x.sroa.0.2.ph.ph, %BB23.thread61 ] - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 23), i64** %10, align 8, !tbaa !15 - %rubyId_something = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 9), align 8, !dbg !55, !invariant.load !5 - %rawSym16 = tail call i64 @rb_id2sym(i64 %rubyId_something) #16, !dbg !55 - br label %BB26, !dbg !55 - -BB26: ; preds = %BB25, %BB24 - %x.sroa.0.059 = phi i64 [ %rawArg_x, %BB24 ], [ %x.sroa.0.060, %BB25 ] - %y.sroa.0.0 = phi i64 [ %rawArg_y, %BB24 ], [ %rawSym16, %BB25 ], !dbg !56 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 24), i64** %10, align 8, !tbaa !15 - %12 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 1, !dbg !57 - %13 = load i64*, i64** %12, align 8, !dbg !57 - store i64 %4, i64* %13, align 8, !dbg !57, !tbaa !6 - %14 = getelementptr inbounds i64, i64* %13, i64 1, !dbg !57 - store i64 %x.sroa.0.059, i64* %14, align 8, !dbg !57, !tbaa !6 - %15 = getelementptr inbounds i64, i64* %14, i64 1, !dbg !57 - store i64* %15, i64** %12, align 8, !dbg !57 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_p.8, i64 0), !dbg !57 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 25), i64** %10, align 8, !dbg !57, !tbaa !15 - %16 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 1, !dbg !58 - %17 = load i64*, i64** %16, align 8, !dbg !58 - store i64 %4, i64* %17, align 8, !dbg !58, !tbaa !6 - %18 = getelementptr inbounds i64, i64* %17, i64 1, !dbg !58 - store i64 %y.sroa.0.0, i64* %18, align 8, !dbg !58, !tbaa !6 - %19 = getelementptr inbounds i64, i64* %18, i64 1, !dbg !58 - store i64* %19, i64** %16, align 8, !dbg !58 - %send66 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_p.9, i64 0), !dbg !58 - ret i64 %send66, !dbg !58 - -argArrayExpandArrayTest: ; preds = %functionEntryInitializers - %arg1_maybeExpandToFullArgs = load i64, i64* %argArray, align 8, !dbg !52 - %20 = and i64 %arg1_maybeExpandToFullArgs, 7, !dbg !52 - %21 = icmp ne i64 %20, 0, !dbg !52 - %22 = and i64 %arg1_maybeExpandToFullArgs, -9, !dbg !52 - %23 = icmp eq i64 %22, 0, !dbg !52 - %24 = or i1 %21, %23, !dbg !52 - br i1 %24, label %BB23.thread61, label %sorbet_isa_Array.exit, !dbg !52 - -sorbet_isa_Array.exit: ; preds = %argArrayExpandArrayTest - %25 = inttoptr i64 %arg1_maybeExpandToFullArgs to %struct.iseq_inline_iv_cache_entry*, !dbg !52 - %26 = getelementptr inbounds %struct.iseq_inline_iv_cache_entry, %struct.iseq_inline_iv_cache_entry* %25, i64 0, i32 0, !dbg !52 - %27 = load i64, i64* %26, align 8, !dbg !52, !tbaa !25 - %28 = and i64 %27, 31, !dbg !52 - %29 = icmp eq i64 %28, 7, !dbg !52 - br i1 %29, label %argArrayExpand, label %BB23.thread61, !dbg !52 - -argArrayExpand: ; preds = %sorbet_isa_Array.exit - %30 = inttoptr i64 %arg1_maybeExpandToFullArgs to %struct.iseq_inline_iv_cache_entry*, !dbg !52 - %31 = getelementptr inbounds %struct.iseq_inline_iv_cache_entry, %struct.iseq_inline_iv_cache_entry* %30, i64 0, i32 0, !dbg !52 - %32 = load i64, i64* %31, align 8, !dbg !52, !tbaa !25 - %33 = and i64 %32, 33554432, !dbg !52 - %34 = icmp eq i64 %33, 0, !dbg !52 - br i1 %34, label %36, label %35, !dbg !52 - -35: ; preds = %argArrayExpand - tail call void @rb_ary_detransient(i64 %arg1_maybeExpandToFullArgs) #13, !dbg !52 - br label %36, !dbg !52 - -36: ; preds = %35, %argArrayExpand - %37 = load i64, i64* %31, align 8, !dbg !52, !tbaa !25 - %38 = and i64 %37, 8192, !dbg !52 - %39 = icmp eq i64 %38, 0, !dbg !52 - %40 = inttoptr i64 %arg1_maybeExpandToFullArgs to %struct.RArray*, !dbg !52 - br i1 %39, label %45, label %41, !dbg !52 - -41: ; preds = %36 - %42 = getelementptr inbounds %struct.RArray, %struct.RArray* %40, i64 0, i32 1, i32 0, i32 0, !dbg !52 - %43 = lshr i64 %37, 15, !dbg !52 - %44 = and i64 %43, 3, !dbg !52 - br label %rb_array_len.exit, !dbg !52 - -45: ; preds = %36 - %46 = getelementptr inbounds %struct.RArray, %struct.RArray* %40, i64 0, i32 1, i32 0, i32 2, !dbg !52 - %47 = load i64*, i64** %46, align 8, !dbg !52, !tbaa !27 - %48 = getelementptr inbounds %struct.RArray, %struct.RArray* %40, i64 0, i32 1, i32 0, i32 0, !dbg !52 - %49 = load i64, i64* %48, align 8, !dbg !52, !tbaa !27 - br label %rb_array_len.exit, !dbg !52 - -rb_array_len.exit: ; preds = %41, %45 - %50 = phi i64* [ %42, %41 ], [ %47, %45 ] - %51 = phi i64 [ %44, %41 ], [ %49, %45 ], !dbg !52 - %52 = trunc i64 %51 to i32, !dbg !52 - br label %fillRequiredArgs, !dbg !52 - -fillFromArgBlock0: ; preds = %fillRequiredArgs - %rawArg_x = load i64, i64* %argArrayPhi, align 8, !dbg !52 - %default1 = icmp eq i32 %argcPhi, 1, !dbg !52 - br i1 %default1, label %BB23.thread61, label %BB24, !dbg !52, !prof !28 - -fillRequiredArgs: ; preds = %functionEntryInitializers, %rb_array_len.exit - %argcPhi = phi i32 [ %argc, %functionEntryInitializers ], [ %52, %rb_array_len.exit ], !dbg !52 - %argArrayPhi = phi i64* [ %argArray, %functionEntryInitializers ], [ %50, %rb_array_len.exit ], !dbg !52 - %default0 = icmp eq i32 %argcPhi, 0, !dbg !52 - br i1 %default0, label %BB23.thread, label %fillFromArgBlock0, !dbg !52, !prof !28 -} - -; Function Attrs: ssp -define internal i64 @"func_.13$block_5"(i64 %firstYieldArgRaw, i64 %localsOffset, i32 %argc, i64* nocapture readonly %argArray, i64 %blockArg) #8 !dbg !59 { -functionEntryInitializers: - %0 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !15 - %1 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %0, i64 0, i32 2 - %2 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %1, align 8, !tbaa !17 - %3 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 3 - %4 = load i64, i64* %3, align 8, !tbaa !41 - %stackFrame = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13$block_5", align 8 - %5 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame, %struct.rb_iseq_struct** %5, align 8, !tbaa !21 - %6 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 4 - %7 = load i64*, i64** %6, align 8, !tbaa !23 - %8 = load i64, i64* %7, align 8, !tbaa !6 - %9 = and i64 %8, -129 - store i64 %9, i64* %7, align 8, !tbaa !6 - %10 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 0 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %10, align 8, !tbaa !15 - %arrayExpansionSizeGuard = icmp eq i32 %argc, 1, !dbg !60 - br i1 %arrayExpansionSizeGuard, label %argArrayExpandArrayTest, label %fillRequiredArgs, !dbg !60 - -BB31: ; preds = %fillFromArgBlock0 - %11 = getelementptr i64, i64* %argArrayPhi, i32 1, !dbg !60 - %rawArg_y = load i64, i64* %11, align 8, !dbg !60 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 28), i64** %10, align 8, !tbaa !15 - br label %BB33, !dbg !61 - -BB32: ; preds = %argArrayExpandArrayTest, %sorbet_isa_Array.exit, %fillRequiredArgs, %fillFromArgBlock0 - %x.sroa.0.1.ph = phi i64 [ 8, %fillRequiredArgs ], [ %rawArg_x, %fillFromArgBlock0 ], [ %arg1_maybeExpandToFullArgs, %sorbet_isa_Array.exit ], [ %arg1_maybeExpandToFullArgs, %argArrayExpandArrayTest ] - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 28), i64** %10, align 8, !tbaa !15 - %rubyId_something = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 9), align 8, !dbg !62, !invariant.load !5 - %rawSym = tail call i64 @rb_id2sym(i64 %rubyId_something) #16, !dbg !62 - br label %BB33, !dbg !62 - -BB33: ; preds = %BB32, %BB31 - %x.sroa.0.140 = phi i64 [ %rawArg_x, %BB31 ], [ %x.sroa.0.1.ph, %BB32 ] - %y.sroa.0.0 = phi i64 [ %rawArg_y, %BB31 ], [ %rawSym, %BB32 ], !dbg !63 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 29), i64** %10, align 8, !tbaa !15 - %12 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 1, !dbg !64 - %13 = load i64*, i64** %12, align 8, !dbg !64 - store i64 %4, i64* %13, align 8, !dbg !64, !tbaa !6 - %14 = getelementptr inbounds i64, i64* %13, i64 1, !dbg !64 - store i64 %x.sroa.0.140, i64* %14, align 8, !dbg !64, !tbaa !6 - %15 = getelementptr inbounds i64, i64* %14, i64 1, !dbg !64 - store i64* %15, i64** %12, align 8, !dbg !64 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_p.12, i64 0), !dbg !64 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 30), i64** %10, align 8, !dbg !64, !tbaa !15 - %16 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 1, !dbg !65 - %17 = load i64*, i64** %16, align 8, !dbg !65 - store i64 %4, i64* %17, align 8, !dbg !65, !tbaa !6 - %18 = getelementptr inbounds i64, i64* %17, i64 1, !dbg !65 - store i64 %y.sroa.0.0, i64* %18, align 8, !dbg !65, !tbaa !6 - %19 = getelementptr inbounds i64, i64* %18, i64 1, !dbg !65 - store i64* %19, i64** %16, align 8, !dbg !65 - %send43 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_p.13, i64 0), !dbg !65 - ret i64 %send43, !dbg !65 - -argArrayExpandArrayTest: ; preds = %functionEntryInitializers - %arg1_maybeExpandToFullArgs = load i64, i64* %argArray, align 8, !dbg !60 - %20 = and i64 %arg1_maybeExpandToFullArgs, 7, !dbg !60 - %21 = icmp ne i64 %20, 0, !dbg !60 - %22 = and i64 %arg1_maybeExpandToFullArgs, -9, !dbg !60 - %23 = icmp eq i64 %22, 0, !dbg !60 - %24 = or i1 %21, %23, !dbg !60 - br i1 %24, label %BB32, label %sorbet_isa_Array.exit, !dbg !60 - -sorbet_isa_Array.exit: ; preds = %argArrayExpandArrayTest - %25 = inttoptr i64 %arg1_maybeExpandToFullArgs to %struct.iseq_inline_iv_cache_entry*, !dbg !60 - %26 = getelementptr inbounds %struct.iseq_inline_iv_cache_entry, %struct.iseq_inline_iv_cache_entry* %25, i64 0, i32 0, !dbg !60 - %27 = load i64, i64* %26, align 8, !dbg !60, !tbaa !25 - %28 = and i64 %27, 31, !dbg !60 - %29 = icmp eq i64 %28, 7, !dbg !60 - br i1 %29, label %argArrayExpand, label %BB32, !dbg !60 - -argArrayExpand: ; preds = %sorbet_isa_Array.exit - %30 = inttoptr i64 %arg1_maybeExpandToFullArgs to %struct.iseq_inline_iv_cache_entry*, !dbg !60 - %31 = getelementptr inbounds %struct.iseq_inline_iv_cache_entry, %struct.iseq_inline_iv_cache_entry* %30, i64 0, i32 0, !dbg !60 - %32 = load i64, i64* %31, align 8, !dbg !60, !tbaa !25 - %33 = and i64 %32, 33554432, !dbg !60 - %34 = icmp eq i64 %33, 0, !dbg !60 - br i1 %34, label %36, label %35, !dbg !60 - -35: ; preds = %argArrayExpand - tail call void @rb_ary_detransient(i64 %arg1_maybeExpandToFullArgs) #13, !dbg !60 - br label %36, !dbg !60 - -36: ; preds = %35, %argArrayExpand - %37 = load i64, i64* %31, align 8, !dbg !60, !tbaa !25 - %38 = and i64 %37, 8192, !dbg !60 - %39 = icmp eq i64 %38, 0, !dbg !60 - %40 = inttoptr i64 %arg1_maybeExpandToFullArgs to %struct.RArray*, !dbg !60 - br i1 %39, label %45, label %41, !dbg !60 - -41: ; preds = %36 - %42 = getelementptr inbounds %struct.RArray, %struct.RArray* %40, i64 0, i32 1, i32 0, i32 0, !dbg !60 - %43 = lshr i64 %37, 15, !dbg !60 - %44 = and i64 %43, 3, !dbg !60 - br label %rb_array_len.exit, !dbg !60 - -45: ; preds = %36 - %46 = getelementptr inbounds %struct.RArray, %struct.RArray* %40, i64 0, i32 1, i32 0, i32 2, !dbg !60 - %47 = load i64*, i64** %46, align 8, !dbg !60, !tbaa !27 - %48 = getelementptr inbounds %struct.RArray, %struct.RArray* %40, i64 0, i32 1, i32 0, i32 0, !dbg !60 - %49 = load i64, i64* %48, align 8, !dbg !60, !tbaa !27 - br label %rb_array_len.exit, !dbg !60 - -rb_array_len.exit: ; preds = %41, %45 - %50 = phi i64* [ %42, %41 ], [ %47, %45 ] - %51 = phi i64 [ %44, %41 ], [ %49, %45 ], !dbg !60 - %52 = trunc i64 %51 to i32, !dbg !60 - br label %fillRequiredArgs, !dbg !60 - -fillFromArgBlock0: ; preds = %fillRequiredArgs - %rawArg_x = load i64, i64* %argArrayPhi, align 8, !dbg !60 - %default1 = icmp eq i32 %argcPhi, 1, !dbg !60 - br i1 %default1, label %BB32, label %BB31, !dbg !60, !prof !28 - -fillRequiredArgs: ; preds = %functionEntryInitializers, %rb_array_len.exit - %argcPhi = phi i32 [ %argc, %functionEntryInitializers ], [ %52, %rb_array_len.exit ], !dbg !60 - %argArrayPhi = phi i64* [ %argArray, %functionEntryInitializers ], [ %50, %rb_array_len.exit ], !dbg !60 - %default0 = icmp eq i32 %argcPhi, 0, !dbg !60 - br i1 %default0, label %BB32, label %fillFromArgBlock0, !dbg !60, !prof !28 -} - -; Function Attrs: sspreq -define void @Init_block_arg_expand() local_unnamed_addr #9 { -entry: - %0 = alloca i64, align 8 - %1 = alloca i64, align 8 - %2 = alloca i64, align 8 - %3 = alloca %struct.sorbet_inlineIntrinsicEnv, align 8 - %4 = alloca %struct.sorbet_inlineIntrinsicEnv, align 8 - %5 = alloca %struct.sorbet_inlineIntrinsicEnv, align 8 - %6 = alloca %struct.sorbet_inlineIntrinsicEnv, align 8 - %7 = alloca %struct.sorbet_inlineIntrinsicEnv, align 8 - %callArgs.i = alloca [3 x i64], align 8 - %locals.i.i = alloca i64, align 8 - %realpath = tail call i64 @sorbet_readRealpath() - tail call void @sorbet_vm_intern_ids(i64* noundef getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([10 x %struct.rb_code_position_struct], [10 x %struct.rb_code_position_struct]* @sorbet_moduleIDDescriptors, i32 0, i32 0), i32 noundef 10, i8* noundef getelementptr inbounds ([163 x i8], [163 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_vm_init_string_table(i64* noundef getelementptr inbounds ([3 x i64], [3 x i64]* @sorbet_moduleRubyStringTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([3 x %struct.rb_code_position_struct], [3 x %struct.rb_code_position_struct]* @sorbet_moduleRubyStringDescriptors, i32 0, i32 0), i32 noundef 3, i8* noundef getelementptr inbounds ([163 x i8], [163 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i32 0, i32 0), i32 noundef 32) - %8 = bitcast i64* %locals.i.i to i8* - call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %8) - %"rubyId_.i.i" = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 0), align 8, !invariant.load !5 - %"rubyStr_.i.i" = load i64, i64* getelementptr inbounds ([3 x i64], [3 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 0), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/block_arg_expand.rb.i.i" = load i64, i64* getelementptr inbounds ([3 x i64], [3 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %"rubyId_.i.i" = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 1), align 8, !invariant.load !5 - store i64 %"rubyId_.i.i", i64* %locals.i.i, align 8 - %9 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_.i.i", i64 %"rubyId_.i.i", i64 %"rubyStr_test/testdata/compiler/block_arg_expand.rb.i.i", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 0, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull align 8 %locals.i.i, i32 noundef 1, i32 noundef 3) - store %struct.rb_iseq_struct* %9, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %8) - %"rubyId_block in .i.i" = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 2), align 8, !invariant.load !5 - %"rubyStr_block in .i.i" = load i64, i64* getelementptr inbounds ([3 x i64], [3 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 2), align 8, !invariant.load !5 - %10 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_block in .i.i", i64 %"rubyId_block in .i.i", i64 %"rubyStr_test/testdata/compiler/block_arg_expand.rb.i.i", i64 %realpath, %struct.rb_iseq_struct* %9, i32 noundef 2, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef null, i32 noundef 0, i32 noundef 3) - store %struct.rb_iseq_struct* %10, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13$block_1", align 8 - %stackFrame.i11.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - %11 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_block in .i.i", i64 %"rubyId_block in .i.i", i64 %"rubyStr_test/testdata/compiler/block_arg_expand.rb.i.i", i64 %realpath, %struct.rb_iseq_struct* %stackFrame.i11.i, i32 noundef 2, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef null, i32 noundef 0, i32 noundef 3) - store %struct.rb_iseq_struct* %11, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13$block_2", align 8 - %stackFrame.i15.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - %12 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_block in .i.i", i64 %"rubyId_block in .i.i", i64 %"rubyStr_test/testdata/compiler/block_arg_expand.rb.i.i", i64 %realpath, %struct.rb_iseq_struct* %stackFrame.i15.i, i32 noundef 2, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef null, i32 noundef 0, i32 noundef 3) - store %struct.rb_iseq_struct* %12, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13$block_3", align 8 - %stackFrame.i19.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - %13 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_block in .i.i", i64 %"rubyId_block in .i.i", i64 %"rubyStr_test/testdata/compiler/block_arg_expand.rb.i.i", i64 %realpath, %struct.rb_iseq_struct* %stackFrame.i19.i, i32 noundef 2, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef null, i32 noundef 0, i32 noundef 3) - store %struct.rb_iseq_struct* %13, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13$block_4", align 8 - %stackFrame.i23.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - %14 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_block in .i.i", i64 %"rubyId_block in .i.i", i64 %"rubyStr_test/testdata/compiler/block_arg_expand.rb.i.i", i64 %realpath, %struct.rb_iseq_struct* %stackFrame.i23.i, i32 noundef 2, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef null, i32 noundef 0, i32 noundef 3) - store %struct.rb_iseq_struct* %14, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13$block_5", align 8 - %15 = call %struct.vm_ifunc* @rb_vm_ifunc_new(i64 (i64, i64, i32, i64*, i64)* noundef @"func_.13$block_1", i8* noundef null, i32 noundef 2, i32 noundef 2) #13 - %16 = ptrtoint %struct.vm_ifunc* %15 to i64 - %17 = call i64 @sorbet_globalConstRegister(i64 %16) - store i64 %17, i64* @"func_.13$block_1_ifunc", align 8 - %"rubyId_+.i" = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 5), align 8, !dbg !30, !invariant.load !5 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @"ic_+", i64 %"rubyId_+.i", i32 noundef 16, i32 noundef 1, i32 noundef 0), !dbg !30 - %18 = call %struct.vm_ifunc* @rb_vm_ifunc_new(i64 (i64, i64, i32, i64*, i64)* noundef @"func_.13$block_2", i8* noundef null, i32 noundef 1, i32 noundef 1) #13 - %19 = ptrtoint %struct.vm_ifunc* %18 to i64 - %20 = call i64 @sorbet_globalConstRegister(i64 %19) - store i64 %20, i64* @"func_.13$block_2_ifunc", align 8 - %rubyId_p.i = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 6), align 8, !dbg !44, !invariant.load !5 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_p, i64 %rubyId_p.i, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !44 - %21 = call %struct.vm_ifunc* @rb_vm_ifunc_new(i64 (i64, i64, i32, i64*, i64)* noundef @"func_.13$block_3", i8* noundef null, i32 noundef 0, i32 noundef 1) #13 - %22 = ptrtoint %struct.vm_ifunc* %21 to i64 - %23 = call i64 @sorbet_globalConstRegister(i64 %22) - store i64 %23, i64* @"func_.13$block_3_ifunc", align 8 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_p.5, i64 %rubyId_p.i, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !50 - %24 = call %struct.vm_ifunc* @rb_vm_ifunc_new(i64 (i64, i64, i32, i64*, i64)* noundef @"func_.13$block_4", i8* noundef null, i32 noundef 0, i32 noundef 2) #13 - %25 = ptrtoint %struct.vm_ifunc* %24 to i64 - %26 = call i64 @sorbet_globalConstRegister(i64 %25) - store i64 %26, i64* @"func_.13$block_4_ifunc", align 8 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_p.8, i64 %rubyId_p.i, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !57 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_p.9, i64 %rubyId_p.i, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !58 - %27 = call %struct.vm_ifunc* @rb_vm_ifunc_new(i64 (i64, i64, i32, i64*, i64)* noundef @"func_.13$block_5", i8* noundef null, i32 noundef 1, i32 noundef 2) #13 - %28 = ptrtoint %struct.vm_ifunc* %27 to i64 - %29 = call i64 @sorbet_globalConstRegister(i64 %28) - store i64 %29, i64* @"func_.13$block_5_ifunc", align 8 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_p.12, i64 %rubyId_p.i, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !64 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_p.13, i64 %rubyId_p.i, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !65 - %30 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !15 - %31 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %30, i64 0, i32 2 - %32 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %31, align 8, !tbaa !17 - %33 = bitcast [3 x i64]* %callArgs.i to i8* - call void @llvm.lifetime.start.p0i8(i64 24, i8* nonnull %33) - %stackFrame.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - %34 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %31, align 8, !tbaa !17 - %35 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %34, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame.i, %struct.rb_iseq_struct** %35, align 8, !tbaa !21 - %36 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %34, i64 0, i32 4 - %37 = load i64*, i64** %36, align 8, !tbaa !23 - %38 = load i64, i64* %37, align 8, !tbaa !6 - %39 = and i64 %38, -33 - store i64 %39, i64* %37, align 8, !tbaa !6 - call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %30, %struct.rb_control_frame_struct* %34, %struct.rb_iseq_struct* %stackFrame.i) #13 - %40 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %32, i64 0, i32 0 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %40, align 8, !dbg !66, !tbaa !15 - %callArgs0Addr.i = getelementptr [3 x i64], [3 x i64]* %callArgs.i, i64 0, i64 0, !dbg !67 - %41 = bitcast i64* %callArgs0Addr.i to <2 x i64>*, !dbg !67 - store <2 x i64> , <2 x i64>* %41, align 8, !dbg !67 - call void @llvm.experimental.noalias.scope.decl(metadata !68) #13, !dbg !67 - %42 = call i64 @rb_ary_new_from_values(i64 noundef 2, i64* noundef nonnull align 8 %callArgs0Addr.i) #13, !dbg !67 - store i64 %42, i64* %callArgs0Addr.i, align 8, !dbg !71 - call void @llvm.experimental.noalias.scope.decl(metadata !72) #13, !dbg !71 - %43 = call i64 @rb_ary_new_from_values(i64 noundef 1, i64* noundef nonnull %callArgs0Addr.i) #13, !dbg !71 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 8), i64** %40, align 8, !dbg !71, !tbaa !15 - %rubyId_each.i = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 4), align 8, !dbg !75, !invariant.load !5 - %44 = load i64, i64* @"func_.13$block_1_ifunc", align 8, !dbg !75 - %45 = call %struct.vm_ifunc* @sorbet_globalConstFetchIfunc(i64 %44) #13, !dbg !75 - %46 = bitcast %struct.sorbet_inlineIntrinsicEnv* %6 to i8*, !dbg !75 - call void @llvm.lifetime.start.p0i8(i64 noundef 40, i8* noundef nonnull %46) #13, !dbg !75 - %47 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %6, i64 0, i32 0, !dbg !75 - store i64 %43, i64* %47, align 8, !dbg !75, !tbaa !76 - %48 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %6, i64 0, i32 1, !dbg !75 - store i64 %rubyId_each.i, i64* %48, align 8, !dbg !75, !tbaa !78 - %49 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %6, i64 0, i32 2, !dbg !75 - store i32 0, i32* %49, align 8, !dbg !75, !tbaa !79 - %50 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %6, i64 0, i32 3, !dbg !75 - %51 = bitcast i64** %50 to i8*, !dbg !75 - call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %51, i8 0, i64 16, i1 false) #13, !dbg !75 - %52 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !75, !tbaa !15 - %53 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %52, i64 0, i32 2, !dbg !75 - %54 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %53, align 8, !dbg !75, !tbaa !17 - %55 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %54, i64 0, i32 3, !dbg !75 - %56 = bitcast i64* %55 to %struct.rb_captured_block*, !dbg !75 - %57 = getelementptr inbounds i64, i64* %55, i64 2, !dbg !75 - %58 = bitcast i64* %57 to %struct.vm_ifunc**, !dbg !75 - store %struct.vm_ifunc* %45, %struct.vm_ifunc** %58, align 8, !dbg !75, !tbaa !27 - call void @llvm.experimental.noalias.scope.decl(metadata !80) #13, !dbg !75 - %59 = ptrtoint %struct.rb_captured_block* %56 to i64, !dbg !75 - %60 = or i64 %59, 3, !dbg !75 - %61 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %52, i64 0, i32 17, !dbg !75 - %62 = and i64 %60, -4, !dbg !83 - %63 = inttoptr i64 %62 to %struct.rb_captured_block*, !dbg !83 - store i64 0, i64* %61, align 8, !dbg !83, !tbaa !85 - call void @sorbet_pushBlockFrame(%struct.rb_captured_block* %63) #13, !dbg !83 - %64 = inttoptr i64 %43 to %struct.iseq_inline_iv_cache_entry*, !dbg !83 - %65 = getelementptr inbounds %struct.iseq_inline_iv_cache_entry, %struct.iseq_inline_iv_cache_entry* %64, i64 0, i32 0, !dbg !83 - %66 = load i64, i64* %65, align 8, !dbg !83, !tbaa !25 - %67 = and i64 %66, 8192, !dbg !83 - %68 = icmp eq i64 %67, 0, !dbg !83 - br i1 %68, label %72, label %69, !dbg !83 - -69: ; preds = %entry - %70 = lshr i64 %66, 15, !dbg !83 - %71 = and i64 %70, 3, !dbg !83 - br label %rb_array_len.exit1.i2.i, !dbg !83 - -72: ; preds = %entry - %73 = inttoptr i64 %43 to %struct.RArray*, !dbg !83 - %74 = getelementptr inbounds %struct.RArray, %struct.RArray* %73, i64 0, i32 1, i32 0, i32 0, !dbg !83 - %75 = load i64, i64* %74, align 8, !dbg !83, !tbaa !27 - br label %rb_array_len.exit1.i2.i, !dbg !83 - -rb_array_len.exit1.i2.i: ; preds = %72, %69 - %76 = phi i64 [ %71, %69 ], [ %75, %72 ], !dbg !83 - %77 = icmp sgt i64 %76, 0, !dbg !83 - br i1 %77, label %78, label %forward_sorbet_rb_array_each_withBlock.exit.i, !dbg !83 - -78: ; preds = %rb_array_len.exit1.i2.i - %79 = bitcast i64* %1 to i8*, !dbg !83 - %80 = inttoptr i64 %43 to %struct.RArray*, !dbg !75 - %81 = getelementptr inbounds %struct.RArray, %struct.RArray* %80, i64 0, i32 1, i32 0, i32 0, !dbg !75 - %82 = getelementptr inbounds %struct.RArray, %struct.RArray* %80, i64 0, i32 1, i32 0, i32 2, !dbg !75 - br label %83, !dbg !83 - -83: ; preds = %rb_array_len.exit.i4.i, %78 - %84 = phi i64 [ 0, %78 ], [ %94, %rb_array_len.exit.i4.i ], !dbg !83 - call void @llvm.lifetime.start.p0i8(i64 noundef 8, i8* noundef nonnull align 8 dereferenceable(8) %79) #13, !dbg !83 - %85 = load i64, i64* %65, align 8, !dbg !83, !tbaa !25 - %86 = and i64 %85, 8192, !dbg !83 - %87 = icmp eq i64 %86, 0, !dbg !83 - br i1 %87, label %88, label %rb_array_const_ptr_transient.exit.i3.i, !dbg !83 - -88: ; preds = %83 - %89 = load i64*, i64** %82, align 8, !dbg !83, !tbaa !27 - br label %rb_array_const_ptr_transient.exit.i3.i, !dbg !83 - -rb_array_const_ptr_transient.exit.i3.i: ; preds = %88, %83 - %90 = phi i64* [ %89, %88 ], [ %81, %83 ], !dbg !83 - %91 = getelementptr inbounds i64, i64* %90, i64 %84, !dbg !83 - %92 = load i64, i64* %91, align 8, !dbg !83, !tbaa !6 - store i64 %92, i64* %1, align 8, !dbg !83, !tbaa !6 - %93 = call i64 @"func_.13$block_1"(i64 undef, i64 undef, i32 noundef 1, i64* noalias nocapture noundef nonnull readonly align 8 dereferenceable(8) %1, i64 undef) #13, !dbg !83 - call void @llvm.lifetime.end.p0i8(i64 noundef 8, i8* noundef nonnull %79) #13, !dbg !83 - %94 = add nuw nsw i64 %84, 1, !dbg !83 - %95 = load i64, i64* %65, align 8, !dbg !83, !tbaa !25 - %96 = and i64 %95, 8192, !dbg !83 - %97 = icmp eq i64 %96, 0, !dbg !83 - br i1 %97, label %101, label %98, !dbg !83 - -98: ; preds = %rb_array_const_ptr_transient.exit.i3.i - %99 = lshr i64 %95, 15, !dbg !83 - %100 = and i64 %99, 3, !dbg !83 - br label %rb_array_len.exit.i4.i, !dbg !83 - -101: ; preds = %rb_array_const_ptr_transient.exit.i3.i - %102 = load i64, i64* %81, align 8, !dbg !83, !tbaa !27 - br label %rb_array_len.exit.i4.i, !dbg !83 - -rb_array_len.exit.i4.i: ; preds = %101, %98 - %103 = phi i64 [ %100, %98 ], [ %102, %101 ], !dbg !83 - %104 = icmp sgt i64 %103, %94, !dbg !83 - br i1 %104, label %83, label %forward_sorbet_rb_array_each_withBlock.exit.i, !dbg !83, !llvm.loop !86 - -forward_sorbet_rb_array_each_withBlock.exit.i: ; preds = %rb_array_len.exit.i4.i, %rb_array_len.exit1.i2.i - call void @sorbet_popFrame() #13, !dbg !83 - call void @llvm.lifetime.end.p0i8(i64 noundef 40, i8* noundef nonnull %46) #13, !dbg !75 - %105 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !75, !tbaa !15 - %106 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %105, i64 0, i32 5, !dbg !75 - %107 = load i32, i32* %106, align 8, !dbg !75, !tbaa !37 - %108 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %105, i64 0, i32 6, !dbg !75 - %109 = load i32, i32* %108, align 4, !dbg !75, !tbaa !38 - %110 = xor i32 %109, -1, !dbg !75 - %111 = and i32 %110, %107, !dbg !75 - %112 = icmp eq i32 %111, 0, !dbg !75 - br i1 %112, label %rb_check_arity.1.exit.i6.i, label %113, !dbg !75, !prof !31 - -113: ; preds = %forward_sorbet_rb_array_each_withBlock.exit.i - %114 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %105, i64 0, i32 8, !dbg !75 - %115 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %114, align 8, !dbg !75, !tbaa !39 - %116 = call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %115, i32 noundef 0) #13, !dbg !75 - br label %rb_check_arity.1.exit.i6.i, !dbg !75 - -rb_check_arity.1.exit.i6.i: ; preds = %113, %forward_sorbet_rb_array_each_withBlock.exit.i - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 13), i64** %40, align 8, !dbg !75, !tbaa !15 - %117 = load i64, i64* @"func_.13$block_2_ifunc", align 8, !dbg !88 - %118 = call %struct.vm_ifunc* @sorbet_globalConstFetchIfunc(i64 %117) #13, !dbg !88 - %119 = bitcast %struct.sorbet_inlineIntrinsicEnv* %5 to i8*, !dbg !88 - call void @llvm.lifetime.start.p0i8(i64 noundef 40, i8* noundef nonnull %119) #13, !dbg !88 - %120 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %5, i64 0, i32 0, !dbg !88 - store i64 %43, i64* %120, align 8, !dbg !88, !tbaa !76 - %121 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %5, i64 0, i32 1, !dbg !88 - store i64 %rubyId_each.i, i64* %121, align 8, !dbg !88, !tbaa !78 - %122 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %5, i64 0, i32 2, !dbg !88 - store i32 0, i32* %122, align 8, !dbg !88, !tbaa !79 - %123 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %5, i64 0, i32 3, !dbg !88 - %124 = bitcast i64** %123 to i8*, !dbg !88 - call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %124, i8 0, i64 16, i1 false) #13, !dbg !88 - %125 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !88, !tbaa !15 - %126 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %125, i64 0, i32 2, !dbg !88 - %127 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %126, align 8, !dbg !88, !tbaa !17 - %128 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %127, i64 0, i32 3, !dbg !88 - %129 = bitcast i64* %128 to %struct.rb_captured_block*, !dbg !88 - %130 = getelementptr inbounds i64, i64* %128, i64 2, !dbg !88 - %131 = bitcast i64* %130 to %struct.vm_ifunc**, !dbg !88 - store %struct.vm_ifunc* %118, %struct.vm_ifunc** %131, align 8, !dbg !88, !tbaa !27 - call void @llvm.experimental.noalias.scope.decl(metadata !89) #13, !dbg !88 - %132 = ptrtoint %struct.rb_captured_block* %129 to i64, !dbg !88 - %133 = or i64 %132, 3, !dbg !88 - %134 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %125, i64 0, i32 17, !dbg !88 - %135 = and i64 %133, -4, !dbg !92 - %136 = inttoptr i64 %135 to %struct.rb_captured_block*, !dbg !92 - store i64 0, i64* %134, align 8, !dbg !92, !tbaa !85 - call void @sorbet_pushBlockFrame(%struct.rb_captured_block* %136) #13, !dbg !92 - %137 = load i64, i64* %65, align 8, !dbg !92, !tbaa !25 - %138 = and i64 %137, 8192, !dbg !92 - %139 = icmp eq i64 %138, 0, !dbg !92 - br i1 %139, label %143, label %140, !dbg !92 - -140: ; preds = %rb_check_arity.1.exit.i6.i - %141 = lshr i64 %137, 15, !dbg !92 - %142 = and i64 %141, 3, !dbg !92 - br label %rb_array_len.exit1.i7.i, !dbg !92 - -143: ; preds = %rb_check_arity.1.exit.i6.i - %144 = inttoptr i64 %43 to %struct.RArray*, !dbg !92 - %145 = getelementptr inbounds %struct.RArray, %struct.RArray* %144, i64 0, i32 1, i32 0, i32 0, !dbg !92 - %146 = load i64, i64* %145, align 8, !dbg !92, !tbaa !27 - br label %rb_array_len.exit1.i7.i, !dbg !92 - -rb_array_len.exit1.i7.i: ; preds = %143, %140 - %147 = phi i64 [ %142, %140 ], [ %146, %143 ], !dbg !92 - %148 = icmp sgt i64 %147, 0, !dbg !92 - br i1 %148, label %149, label %forward_sorbet_rb_array_each_withBlock.1.exit.i, !dbg !92 - -149: ; preds = %rb_array_len.exit1.i7.i - %150 = inttoptr i64 %43 to %struct.RArray*, !dbg !88 - %151 = getelementptr inbounds %struct.RArray, %struct.RArray* %150, i64 0, i32 1, i32 0, i32 0, !dbg !88 - %152 = getelementptr inbounds %struct.RArray, %struct.RArray* %150, i64 0, i32 1, i32 0, i32 2, !dbg !88 - br label %153, !dbg !92 - -153: ; preds = %rb_array_len.exit.i9.i, %149 - %154 = phi i64 [ 0, %149 ], [ %178, %rb_array_len.exit.i9.i ], !dbg !92 - %155 = load i64, i64* %65, align 8, !dbg !92, !tbaa !25 - %156 = and i64 %155, 8192, !dbg !92 - %157 = icmp eq i64 %156, 0, !dbg !92 - br i1 %157, label %158, label %rb_array_const_ptr_transient.exit.i8.i, !dbg !92 - -158: ; preds = %153 - %159 = load i64*, i64** %152, align 8, !dbg !92, !tbaa !27 - br label %rb_array_const_ptr_transient.exit.i8.i, !dbg !92 - -rb_array_const_ptr_transient.exit.i8.i: ; preds = %158, %153 - %160 = phi i64* [ %159, %158 ], [ %151, %153 ], !dbg !92 - %161 = getelementptr inbounds i64, i64* %160, i64 %154, !dbg !92 - %162 = load i64, i64* %161, align 8, !dbg !92, !tbaa !6 - call void @llvm.experimental.noalias.scope.decl(metadata !94) #13, !dbg !92 - %163 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !88, !tbaa !15, !noalias !94 - %164 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %163, i64 0, i32 2, !dbg !88 - %165 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %164, align 8, !dbg !88, !tbaa !17, !noalias !94 - %166 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %165, i64 0, i32 3, !dbg !88 - %167 = load i64, i64* %166, align 8, !dbg !88, !tbaa !41, !noalias !94 - %stackFrame.i.i.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13$block_2", align 8, !dbg !88, !noalias !94 - %168 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %165, i64 0, i32 2, !dbg !88 - store %struct.rb_iseq_struct* %stackFrame.i.i.i, %struct.rb_iseq_struct** %168, align 8, !dbg !88, !tbaa !21, !noalias !94 - %169 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %165, i64 0, i32 4, !dbg !88 - %170 = load i64*, i64** %169, align 8, !dbg !88, !tbaa !23, !noalias !94 - %171 = load i64, i64* %170, align 8, !dbg !88, !tbaa !6, !noalias !94 - %172 = and i64 %171, -129, !dbg !88 - store i64 %172, i64* %170, align 8, !dbg !88, !tbaa !6, !noalias !94 - %173 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %165, i64 0, i32 0, !dbg !88 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 14), i64** %173, align 8, !dbg !97, !tbaa !15, !noalias !94 - %174 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %165, i64 0, i32 1, !dbg !99 - %175 = load i64*, i64** %174, align 8, !dbg !99 - store i64 %167, i64* %175, align 8, !dbg !99, !tbaa !6 - %176 = getelementptr inbounds i64, i64* %175, i64 1, !dbg !99 - store i64 %162, i64* %176, align 8, !dbg !99, !tbaa !6 - %177 = getelementptr inbounds i64, i64* %176, i64 1, !dbg !99 - store i64* %177, i64** %174, align 8, !dbg !99 - %send = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_p, i64 0), !dbg !99 - %178 = add nuw nsw i64 %154, 1, !dbg !92 - %179 = load i64, i64* %65, align 8, !dbg !92, !tbaa !25 - %180 = and i64 %179, 8192, !dbg !92 - %181 = icmp eq i64 %180, 0, !dbg !92 - br i1 %181, label %185, label %182, !dbg !92 - -182: ; preds = %rb_array_const_ptr_transient.exit.i8.i - %183 = lshr i64 %179, 15, !dbg !92 - %184 = and i64 %183, 3, !dbg !92 - br label %rb_array_len.exit.i9.i, !dbg !92 - -185: ; preds = %rb_array_const_ptr_transient.exit.i8.i - %186 = load i64, i64* %151, align 8, !dbg !92, !tbaa !27 - br label %rb_array_len.exit.i9.i, !dbg !92 - -rb_array_len.exit.i9.i: ; preds = %185, %182 - %187 = phi i64 [ %184, %182 ], [ %186, %185 ], !dbg !92 - %188 = icmp sgt i64 %187, %178, !dbg !92 - br i1 %188, label %153, label %forward_sorbet_rb_array_each_withBlock.1.exit.i, !dbg !92, !llvm.loop !100 - -forward_sorbet_rb_array_each_withBlock.1.exit.i: ; preds = %rb_array_len.exit.i9.i, %rb_array_len.exit1.i7.i - call void @sorbet_popFrame() #13, !dbg !92 - call void @llvm.lifetime.end.p0i8(i64 noundef 40, i8* noundef nonnull %119) #13, !dbg !88 - %189 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !88, !tbaa !15 - %190 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %189, i64 0, i32 5, !dbg !88 - %191 = load i32, i32* %190, align 8, !dbg !88, !tbaa !37 - %192 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %189, i64 0, i32 6, !dbg !88 - %193 = load i32, i32* %192, align 4, !dbg !88, !tbaa !38 - %194 = xor i32 %193, -1, !dbg !88 - %195 = and i32 %194, %191, !dbg !88 - %196 = icmp eq i32 %195, 0, !dbg !88 - br i1 %196, label %rb_check_arity.1.exit.i11.i, label %197, !dbg !88, !prof !31 - -197: ; preds = %forward_sorbet_rb_array_each_withBlock.1.exit.i - %198 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %189, i64 0, i32 8, !dbg !88 - %199 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %198, align 8, !dbg !88, !tbaa !39 - %200 = call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %199, i32 noundef 0) #13, !dbg !88 - br label %rb_check_arity.1.exit.i11.i, !dbg !88 - -rb_check_arity.1.exit.i11.i: ; preds = %197, %forward_sorbet_rb_array_each_withBlock.1.exit.i - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 18), i64** %40, align 8, !dbg !88, !tbaa !15 - %201 = load i64, i64* @"func_.13$block_3_ifunc", align 8, !dbg !101 - %202 = call %struct.vm_ifunc* @sorbet_globalConstFetchIfunc(i64 %201) #13, !dbg !101 - %203 = bitcast %struct.sorbet_inlineIntrinsicEnv* %4 to i8*, !dbg !101 - call void @llvm.lifetime.start.p0i8(i64 noundef 40, i8* noundef nonnull %203) #13, !dbg !101 - %204 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %4, i64 0, i32 0, !dbg !101 - store i64 %43, i64* %204, align 8, !dbg !101, !tbaa !76 - %205 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %4, i64 0, i32 1, !dbg !101 - store i64 %rubyId_each.i, i64* %205, align 8, !dbg !101, !tbaa !78 - %206 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %4, i64 0, i32 2, !dbg !101 - store i32 0, i32* %206, align 8, !dbg !101, !tbaa !79 - %207 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %4, i64 0, i32 3, !dbg !101 - %208 = bitcast i64** %207 to i8*, !dbg !101 - call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %208, i8 0, i64 16, i1 false) #13, !dbg !101 - %209 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !101, !tbaa !15 - %210 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %209, i64 0, i32 2, !dbg !101 - %211 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %210, align 8, !dbg !101, !tbaa !17 - %212 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %211, i64 0, i32 3, !dbg !101 - %213 = bitcast i64* %212 to %struct.rb_captured_block*, !dbg !101 - %214 = getelementptr inbounds i64, i64* %212, i64 2, !dbg !101 - %215 = bitcast i64* %214 to %struct.vm_ifunc**, !dbg !101 - store %struct.vm_ifunc* %202, %struct.vm_ifunc** %215, align 8, !dbg !101, !tbaa !27 - call void @llvm.experimental.noalias.scope.decl(metadata !102) #13, !dbg !101 - %216 = ptrtoint %struct.rb_captured_block* %213 to i64, !dbg !101 - %217 = or i64 %216, 3, !dbg !101 - %218 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %209, i64 0, i32 17, !dbg !101 - %219 = and i64 %217, -4, !dbg !105 - %220 = inttoptr i64 %219 to %struct.rb_captured_block*, !dbg !105 - store i64 0, i64* %218, align 8, !dbg !105, !tbaa !85 - call void @sorbet_pushBlockFrame(%struct.rb_captured_block* %220) #13, !dbg !105 - %221 = load i64, i64* %65, align 8, !dbg !105, !tbaa !25 - %222 = and i64 %221, 8192, !dbg !105 - %223 = icmp eq i64 %222, 0, !dbg !105 - br i1 %223, label %227, label %224, !dbg !105 - -224: ; preds = %rb_check_arity.1.exit.i11.i - %225 = lshr i64 %221, 15, !dbg !105 - %226 = and i64 %225, 3, !dbg !105 - br label %rb_array_len.exit1.i12.i, !dbg !105 - -227: ; preds = %rb_check_arity.1.exit.i11.i - %228 = inttoptr i64 %43 to %struct.RArray*, !dbg !105 - %229 = getelementptr inbounds %struct.RArray, %struct.RArray* %228, i64 0, i32 1, i32 0, i32 0, !dbg !105 - %230 = load i64, i64* %229, align 8, !dbg !105, !tbaa !27 - br label %rb_array_len.exit1.i12.i, !dbg !105 - -rb_array_len.exit1.i12.i: ; preds = %227, %224 - %231 = phi i64 [ %226, %224 ], [ %230, %227 ], !dbg !105 - %232 = icmp sgt i64 %231, 0, !dbg !105 - br i1 %232, label %233, label %forward_sorbet_rb_array_each_withBlock.3.exit.i, !dbg !105 - -233: ; preds = %rb_array_len.exit1.i12.i - %234 = inttoptr i64 %43 to %struct.RArray*, !dbg !101 - %235 = getelementptr inbounds %struct.RArray, %struct.RArray* %234, i64 0, i32 1, i32 0, i32 0, !dbg !101 - %236 = getelementptr inbounds %struct.RArray, %struct.RArray* %234, i64 0, i32 1, i32 0, i32 2, !dbg !101 - br label %237, !dbg !105 - -237: ; preds = %rb_array_len.exit.i15.i, %233 - %238 = phi i64 [ 0, %233 ], [ %262, %rb_array_len.exit.i15.i ], !dbg !105 - %239 = load i64, i64* %65, align 8, !dbg !105, !tbaa !25 - %240 = and i64 %239, 8192, !dbg !105 - %241 = icmp eq i64 %240, 0, !dbg !105 - br i1 %241, label %242, label %rb_array_const_ptr_transient.exit.i14.i, !dbg !105 - -242: ; preds = %237 - %243 = load i64*, i64** %236, align 8, !dbg !105, !tbaa !27 - br label %rb_array_const_ptr_transient.exit.i14.i, !dbg !105 - -rb_array_const_ptr_transient.exit.i14.i: ; preds = %242, %237 - %244 = phi i64* [ %243, %242 ], [ %235, %237 ], !dbg !105 - %245 = getelementptr inbounds i64, i64* %244, i64 %238, !dbg !105 - %246 = load i64, i64* %245, align 8, !dbg !105, !tbaa !6 - call void @llvm.experimental.noalias.scope.decl(metadata !107) #13, !dbg !105 - %247 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !101, !tbaa !15, !noalias !107 - %248 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %247, i64 0, i32 2, !dbg !101 - %249 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %248, align 8, !dbg !101, !tbaa !17, !noalias !107 - %250 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %249, i64 0, i32 3, !dbg !101 - %251 = load i64, i64* %250, align 8, !dbg !101, !tbaa !41, !noalias !107 - %stackFrame.i.i13.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13$block_3", align 8, !dbg !101, !noalias !107 - %252 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %249, i64 0, i32 2, !dbg !101 - store %struct.rb_iseq_struct* %stackFrame.i.i13.i, %struct.rb_iseq_struct** %252, align 8, !dbg !101, !tbaa !21, !noalias !107 - %253 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %249, i64 0, i32 4, !dbg !101 - %254 = load i64*, i64** %253, align 8, !dbg !101, !tbaa !23, !noalias !107 - %255 = load i64, i64* %254, align 8, !dbg !101, !tbaa !6, !noalias !107 - %256 = and i64 %255, -129, !dbg !101 - store i64 %256, i64* %254, align 8, !dbg !101, !tbaa !6, !noalias !107 - %257 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %249, i64 0, i32 0, !dbg !101 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 19), i64** %257, align 8, !dbg !101, !tbaa !15, !noalias !107 - %258 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %249, i64 0, i32 1, !dbg !110 - %259 = load i64*, i64** %258, align 8, !dbg !110 - store i64 %251, i64* %259, align 8, !dbg !110, !tbaa !6 - %260 = getelementptr inbounds i64, i64* %259, i64 1, !dbg !110 - store i64 %246, i64* %260, align 8, !dbg !110, !tbaa !6 - %261 = getelementptr inbounds i64, i64* %260, i64 1, !dbg !110 - store i64* %261, i64** %258, align 8, !dbg !110 - %send14 = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_p.5, i64 0), !dbg !110 - %262 = add nuw nsw i64 %238, 1, !dbg !105 - %263 = load i64, i64* %65, align 8, !dbg !105, !tbaa !25 - %264 = and i64 %263, 8192, !dbg !105 - %265 = icmp eq i64 %264, 0, !dbg !105 - br i1 %265, label %269, label %266, !dbg !105 - -266: ; preds = %rb_array_const_ptr_transient.exit.i14.i - %267 = lshr i64 %263, 15, !dbg !105 - %268 = and i64 %267, 3, !dbg !105 - br label %rb_array_len.exit.i15.i, !dbg !105 - -269: ; preds = %rb_array_const_ptr_transient.exit.i14.i - %270 = load i64, i64* %235, align 8, !dbg !105, !tbaa !27 - br label %rb_array_len.exit.i15.i, !dbg !105 - -rb_array_len.exit.i15.i: ; preds = %269, %266 - %271 = phi i64 [ %268, %266 ], [ %270, %269 ], !dbg !105 - %272 = icmp sgt i64 %271, %262, !dbg !105 - br i1 %272, label %237, label %forward_sorbet_rb_array_each_withBlock.3.exit.i, !dbg !105, !llvm.loop !112 - -forward_sorbet_rb_array_each_withBlock.3.exit.i: ; preds = %rb_array_len.exit.i15.i, %rb_array_len.exit1.i12.i - call void @sorbet_popFrame() #13, !dbg !105 - call void @llvm.lifetime.end.p0i8(i64 noundef 40, i8* noundef nonnull %203) #13, !dbg !101 - %273 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !101, !tbaa !15 - %274 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %273, i64 0, i32 5, !dbg !101 - %275 = load i32, i32* %274, align 8, !dbg !101, !tbaa !37 - %276 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %273, i64 0, i32 6, !dbg !101 - %277 = load i32, i32* %276, align 4, !dbg !101, !tbaa !38 - %278 = xor i32 %277, -1, !dbg !101 - %279 = and i32 %278, %275, !dbg !101 - %280 = icmp eq i32 %279, 0, !dbg !101 - br i1 %280, label %rb_check_arity.1.exit.i17.i, label %281, !dbg !101, !prof !31 - -281: ; preds = %forward_sorbet_rb_array_each_withBlock.3.exit.i - %282 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %273, i64 0, i32 8, !dbg !101 - %283 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %282, align 8, !dbg !101, !tbaa !39 - %284 = call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %283, i32 noundef 0) #13, !dbg !101 - br label %rb_check_arity.1.exit.i17.i, !dbg !101 - -rb_check_arity.1.exit.i17.i: ; preds = %281, %forward_sorbet_rb_array_each_withBlock.3.exit.i - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 23), i64** %40, align 8, !dbg !101, !tbaa !15 - %285 = load i64, i64* @"func_.13$block_4_ifunc", align 8, !dbg !113 - %286 = call %struct.vm_ifunc* @sorbet_globalConstFetchIfunc(i64 %285) #13, !dbg !113 - %287 = bitcast %struct.sorbet_inlineIntrinsicEnv* %3 to i8*, !dbg !113 - call void @llvm.lifetime.start.p0i8(i64 noundef 40, i8* noundef nonnull %287) #13, !dbg !113 - %288 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %3, i64 0, i32 0, !dbg !113 - store i64 %43, i64* %288, align 8, !dbg !113, !tbaa !76 - %289 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %3, i64 0, i32 1, !dbg !113 - store i64 %rubyId_each.i, i64* %289, align 8, !dbg !113, !tbaa !78 - %290 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %3, i64 0, i32 2, !dbg !113 - store i32 0, i32* %290, align 8, !dbg !113, !tbaa !79 - %291 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %3, i64 0, i32 3, !dbg !113 - %292 = bitcast i64** %291 to i8*, !dbg !113 - call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %292, i8 0, i64 16, i1 false) #13, !dbg !113 - %293 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !113, !tbaa !15 - %294 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %293, i64 0, i32 2, !dbg !113 - %295 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %294, align 8, !dbg !113, !tbaa !17 - %296 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %295, i64 0, i32 3, !dbg !113 - %297 = bitcast i64* %296 to %struct.rb_captured_block*, !dbg !113 - %298 = getelementptr inbounds i64, i64* %296, i64 2, !dbg !113 - %299 = bitcast i64* %298 to %struct.vm_ifunc**, !dbg !113 - store %struct.vm_ifunc* %286, %struct.vm_ifunc** %299, align 8, !dbg !113, !tbaa !27 - call void @llvm.experimental.noalias.scope.decl(metadata !114) #13, !dbg !113 - %300 = ptrtoint %struct.rb_captured_block* %297 to i64, !dbg !113 - %301 = or i64 %300, 3, !dbg !113 - %302 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %293, i64 0, i32 17, !dbg !113 - %303 = and i64 %301, -4, !dbg !117 - %304 = inttoptr i64 %303 to %struct.rb_captured_block*, !dbg !117 - store i64 0, i64* %302, align 8, !dbg !117, !tbaa !85 - call void @sorbet_pushBlockFrame(%struct.rb_captured_block* %304) #13, !dbg !117 - %305 = load i64, i64* %65, align 8, !dbg !117, !tbaa !25 - %306 = and i64 %305, 8192, !dbg !117 - %307 = icmp eq i64 %306, 0, !dbg !117 - br i1 %307, label %311, label %308, !dbg !117 - -308: ; preds = %rb_check_arity.1.exit.i17.i - %309 = lshr i64 %305, 15, !dbg !117 - %310 = and i64 %309, 3, !dbg !117 - br label %rb_array_len.exit1.i18.i, !dbg !117 - -311: ; preds = %rb_check_arity.1.exit.i17.i - %312 = inttoptr i64 %43 to %struct.RArray*, !dbg !117 - %313 = getelementptr inbounds %struct.RArray, %struct.RArray* %312, i64 0, i32 1, i32 0, i32 0, !dbg !117 - %314 = load i64, i64* %313, align 8, !dbg !117, !tbaa !27 - br label %rb_array_len.exit1.i18.i, !dbg !117 - -rb_array_len.exit1.i18.i: ; preds = %311, %308 - %315 = phi i64 [ %310, %308 ], [ %314, %311 ], !dbg !117 - %316 = icmp sgt i64 %315, 0, !dbg !117 - br i1 %316, label %317, label %forward_sorbet_rb_array_each_withBlock.6.exit.i, !dbg !117 - -317: ; preds = %rb_array_len.exit1.i18.i - %318 = bitcast i64* %0 to i8*, !dbg !117 - %319 = inttoptr i64 %43 to %struct.RArray*, !dbg !113 - %320 = getelementptr inbounds %struct.RArray, %struct.RArray* %319, i64 0, i32 1, i32 0, i32 0, !dbg !113 - %321 = getelementptr inbounds %struct.RArray, %struct.RArray* %319, i64 0, i32 1, i32 0, i32 2, !dbg !113 - br label %322, !dbg !117 - -322: ; preds = %rb_array_len.exit.i20.i, %317 - %323 = phi i64 [ 0, %317 ], [ %333, %rb_array_len.exit.i20.i ], !dbg !117 - call void @llvm.lifetime.start.p0i8(i64 noundef 8, i8* noundef nonnull align 8 dereferenceable(8) %318) #13, !dbg !117 - %324 = load i64, i64* %65, align 8, !dbg !117, !tbaa !25 - %325 = and i64 %324, 8192, !dbg !117 - %326 = icmp eq i64 %325, 0, !dbg !117 - br i1 %326, label %327, label %rb_array_const_ptr_transient.exit.i19.i, !dbg !117 - -327: ; preds = %322 - %328 = load i64*, i64** %321, align 8, !dbg !117, !tbaa !27 - br label %rb_array_const_ptr_transient.exit.i19.i, !dbg !117 - -rb_array_const_ptr_transient.exit.i19.i: ; preds = %327, %322 - %329 = phi i64* [ %328, %327 ], [ %320, %322 ], !dbg !117 - %330 = getelementptr inbounds i64, i64* %329, i64 %323, !dbg !117 - %331 = load i64, i64* %330, align 8, !dbg !117, !tbaa !6 - store i64 %331, i64* %0, align 8, !dbg !117, !tbaa !6 - %332 = call i64 @"func_.13$block_4"(i64 undef, i64 undef, i32 noundef 1, i64* noalias nocapture noundef nonnull readonly align 8 dereferenceable(8) %0, i64 undef) #13, !dbg !117 - call void @llvm.lifetime.end.p0i8(i64 noundef 8, i8* noundef nonnull %318) #13, !dbg !117 - %333 = add nuw nsw i64 %323, 1, !dbg !117 - %334 = load i64, i64* %65, align 8, !dbg !117, !tbaa !25 - %335 = and i64 %334, 8192, !dbg !117 - %336 = icmp eq i64 %335, 0, !dbg !117 - br i1 %336, label %340, label %337, !dbg !117 - -337: ; preds = %rb_array_const_ptr_transient.exit.i19.i - %338 = lshr i64 %334, 15, !dbg !117 - %339 = and i64 %338, 3, !dbg !117 - br label %rb_array_len.exit.i20.i, !dbg !117 - -340: ; preds = %rb_array_const_ptr_transient.exit.i19.i - %341 = load i64, i64* %320, align 8, !dbg !117, !tbaa !27 - br label %rb_array_len.exit.i20.i, !dbg !117 - -rb_array_len.exit.i20.i: ; preds = %340, %337 - %342 = phi i64 [ %339, %337 ], [ %341, %340 ], !dbg !117 - %343 = icmp sgt i64 %342, %333, !dbg !117 - br i1 %343, label %322, label %forward_sorbet_rb_array_each_withBlock.6.exit.i, !dbg !117, !llvm.loop !119 - -forward_sorbet_rb_array_each_withBlock.6.exit.i: ; preds = %rb_array_len.exit.i20.i, %rb_array_len.exit1.i18.i - call void @sorbet_popFrame() #13, !dbg !117 - call void @llvm.lifetime.end.p0i8(i64 noundef 40, i8* noundef nonnull %287) #13, !dbg !113 - %344 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !113, !tbaa !15 - %345 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %344, i64 0, i32 5, !dbg !113 - %346 = load i32, i32* %345, align 8, !dbg !113, !tbaa !37 - %347 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %344, i64 0, i32 6, !dbg !113 - %348 = load i32, i32* %347, align 4, !dbg !113, !tbaa !38 - %349 = xor i32 %348, -1, !dbg !113 - %350 = and i32 %349, %346, !dbg !113 - %351 = icmp eq i32 %350, 0, !dbg !113 - br i1 %351, label %rb_check_arity.1.exit.i.i, label %352, !dbg !113, !prof !31 - -352: ; preds = %forward_sorbet_rb_array_each_withBlock.6.exit.i - %353 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %344, i64 0, i32 8, !dbg !113 - %354 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %353, align 8, !dbg !113, !tbaa !39 - %355 = call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %354, i32 noundef 0) #13, !dbg !113 - br label %rb_check_arity.1.exit.i.i, !dbg !113 - -rb_check_arity.1.exit.i.i: ; preds = %352, %forward_sorbet_rb_array_each_withBlock.6.exit.i - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 28), i64** %40, align 8, !dbg !113, !tbaa !15 - %356 = load i64, i64* @"func_.13$block_5_ifunc", align 8, !dbg !120 - %357 = call %struct.vm_ifunc* @sorbet_globalConstFetchIfunc(i64 %356) #13, !dbg !120 - %358 = bitcast %struct.sorbet_inlineIntrinsicEnv* %7 to i8*, !dbg !120 - call void @llvm.lifetime.start.p0i8(i64 noundef 40, i8* noundef nonnull %358) #13, !dbg !120 - %359 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %7, i64 0, i32 0, !dbg !120 - store i64 %43, i64* %359, align 8, !dbg !120, !tbaa !76 - %360 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %7, i64 0, i32 1, !dbg !120 - store i64 %rubyId_each.i, i64* %360, align 8, !dbg !120, !tbaa !78 - %361 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %7, i64 0, i32 2, !dbg !120 - store i32 0, i32* %361, align 8, !dbg !120, !tbaa !79 - %362 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %7, i64 0, i32 3, !dbg !120 - %363 = bitcast i64** %362 to i8*, !dbg !120 - call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %363, i8 0, i64 16, i1 false) #13, !dbg !120 - %364 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !120, !tbaa !15 - %365 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %364, i64 0, i32 2, !dbg !120 - %366 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %365, align 8, !dbg !120, !tbaa !17 - %367 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %366, i64 0, i32 3, !dbg !120 - %368 = bitcast i64* %367 to %struct.rb_captured_block*, !dbg !120 - %369 = getelementptr inbounds i64, i64* %367, i64 2, !dbg !120 - %370 = bitcast i64* %369 to %struct.vm_ifunc**, !dbg !120 - store %struct.vm_ifunc* %357, %struct.vm_ifunc** %370, align 8, !dbg !120, !tbaa !27 - call void @llvm.experimental.noalias.scope.decl(metadata !121) #13, !dbg !120 - %371 = ptrtoint %struct.rb_captured_block* %368 to i64, !dbg !120 - %372 = or i64 %371, 3, !dbg !120 - %373 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %364, i64 0, i32 17, !dbg !120 - %374 = and i64 %372, -4, !dbg !124 - %375 = inttoptr i64 %374 to %struct.rb_captured_block*, !dbg !124 - store i64 0, i64* %373, align 8, !dbg !124, !tbaa !85 - call void @sorbet_pushBlockFrame(%struct.rb_captured_block* %375) #13, !dbg !124 - %376 = load i64, i64* %65, align 8, !dbg !124, !tbaa !25 - %377 = and i64 %376, 8192, !dbg !124 - %378 = icmp eq i64 %377, 0, !dbg !124 - br i1 %378, label %382, label %379, !dbg !124 - -379: ; preds = %rb_check_arity.1.exit.i.i - %380 = lshr i64 %376, 15, !dbg !124 - %381 = and i64 %380, 3, !dbg !124 - br label %rb_array_len.exit1.i.i, !dbg !124 - -382: ; preds = %rb_check_arity.1.exit.i.i - %383 = inttoptr i64 %43 to %struct.RArray*, !dbg !124 - %384 = getelementptr inbounds %struct.RArray, %struct.RArray* %383, i64 0, i32 1, i32 0, i32 0, !dbg !124 - %385 = load i64, i64* %384, align 8, !dbg !124, !tbaa !27 - br label %rb_array_len.exit1.i.i, !dbg !124 - -rb_array_len.exit1.i.i: ; preds = %382, %379 - %386 = phi i64 [ %381, %379 ], [ %385, %382 ], !dbg !124 - %387 = icmp sgt i64 %386, 0, !dbg !124 - br i1 %387, label %388, label %forward_sorbet_rb_array_each_withBlock.10.exit.i, !dbg !124 - -388: ; preds = %rb_array_len.exit1.i.i - %389 = bitcast i64* %2 to i8*, !dbg !124 - %390 = inttoptr i64 %43 to %struct.RArray*, !dbg !120 - %391 = getelementptr inbounds %struct.RArray, %struct.RArray* %390, i64 0, i32 1, i32 0, i32 0, !dbg !120 - %392 = getelementptr inbounds %struct.RArray, %struct.RArray* %390, i64 0, i32 1, i32 0, i32 2, !dbg !120 - br label %393, !dbg !124 - -393: ; preds = %rb_array_len.exit.i.i, %388 - %394 = phi i64 [ 0, %388 ], [ %404, %rb_array_len.exit.i.i ], !dbg !124 - call void @llvm.lifetime.start.p0i8(i64 noundef 8, i8* noundef nonnull align 8 dereferenceable(8) %389) #13, !dbg !124 - %395 = load i64, i64* %65, align 8, !dbg !124, !tbaa !25 - %396 = and i64 %395, 8192, !dbg !124 - %397 = icmp eq i64 %396, 0, !dbg !124 - br i1 %397, label %398, label %rb_array_const_ptr_transient.exit.i.i, !dbg !124 - -398: ; preds = %393 - %399 = load i64*, i64** %392, align 8, !dbg !124, !tbaa !27 - br label %rb_array_const_ptr_transient.exit.i.i, !dbg !124 - -rb_array_const_ptr_transient.exit.i.i: ; preds = %398, %393 - %400 = phi i64* [ %399, %398 ], [ %391, %393 ], !dbg !124 - %401 = getelementptr inbounds i64, i64* %400, i64 %394, !dbg !124 - %402 = load i64, i64* %401, align 8, !dbg !124, !tbaa !6 - store i64 %402, i64* %2, align 8, !dbg !124, !tbaa !6 - %403 = call i64 @"func_.13$block_5"(i64 undef, i64 undef, i32 noundef 1, i64* noalias nocapture noundef nonnull readonly align 8 dereferenceable(8) %2, i64 undef) #13, !dbg !124 - call void @llvm.lifetime.end.p0i8(i64 noundef 8, i8* noundef nonnull %389) #13, !dbg !124 - %404 = add nuw nsw i64 %394, 1, !dbg !124 - %405 = load i64, i64* %65, align 8, !dbg !124, !tbaa !25 - %406 = and i64 %405, 8192, !dbg !124 - %407 = icmp eq i64 %406, 0, !dbg !124 - br i1 %407, label %411, label %408, !dbg !124 - -408: ; preds = %rb_array_const_ptr_transient.exit.i.i - %409 = lshr i64 %405, 15, !dbg !124 - %410 = and i64 %409, 3, !dbg !124 - br label %rb_array_len.exit.i.i, !dbg !124 - -411: ; preds = %rb_array_const_ptr_transient.exit.i.i - %412 = load i64, i64* %391, align 8, !dbg !124, !tbaa !27 - br label %rb_array_len.exit.i.i, !dbg !124 - -rb_array_len.exit.i.i: ; preds = %411, %408 - %413 = phi i64 [ %410, %408 ], [ %412, %411 ], !dbg !124 - %414 = icmp sgt i64 %413, %404, !dbg !124 - br i1 %414, label %393, label %forward_sorbet_rb_array_each_withBlock.10.exit.i, !dbg !124, !llvm.loop !126 - -forward_sorbet_rb_array_each_withBlock.10.exit.i: ; preds = %rb_array_len.exit.i.i, %rb_array_len.exit1.i.i - call void @sorbet_popFrame() #13, !dbg !124 - call void @llvm.lifetime.end.p0i8(i64 noundef 40, i8* noundef nonnull %358) #13, !dbg !120 - %415 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !120, !tbaa !15 - %416 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %415, i64 0, i32 5, !dbg !120 - %417 = load i32, i32* %416, align 8, !dbg !120, !tbaa !37 - %418 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %415, i64 0, i32 6, !dbg !120 - %419 = load i32, i32* %418, align 4, !dbg !120, !tbaa !38 - %420 = xor i32 %419, -1, !dbg !120 - %421 = and i32 %420, %417, !dbg !120 - %422 = icmp eq i32 %421, 0, !dbg !120 - br i1 %422, label %"func_.13.exit", label %423, !dbg !120, !prof !31 - -423: ; preds = %forward_sorbet_rb_array_each_withBlock.10.exit.i - %424 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %415, i64 0, i32 8, !dbg !120 - %425 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %424, align 8, !dbg !120, !tbaa !39 - %426 = call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %425, i32 noundef 0) #13, !dbg !120 - br label %"func_.13.exit", !dbg !120 - -"func_.13.exit": ; preds = %forward_sorbet_rb_array_each_withBlock.10.exit.i, %423 - store i64* getelementptr inbounds ([32 x i64], [32 x i64]* @iseqEncodedArray, i64 0, i64 28), i64** %40, align 8, !tbaa !15 - call void @llvm.lifetime.end.p0i8(i64 24, i8* nonnull %33) - ret void -} - -; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly -declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #10 - -; Function Attrs: cold minsize noreturn ssp -define internal fastcc void @"func_.13$block_1.cold.1"(i64 %el2.sroa.0.0) unnamed_addr #11 !dbg !127 { -newFuncRoot: - tail call void @sorbet_cast_failure(i64 %el2.sroa.0.0, i8* getelementptr inbounds ([163 x i8], [163 x i8]* @sorbet_moduleStringTable, i64 0, i64 118), i8* getelementptr inbounds ([163 x i8], [163 x i8]* @sorbet_moduleStringTable, i64 0, i64 124)) #17, !dbg !129 - unreachable, !dbg !129 -} - -attributes #0 = { nounwind readnone willreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #1 = { cold noreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #2 = { "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #3 = { argmemonly nofree nosync nounwind willreturn } -attributes #4 = { noreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #5 = { nofree nosync nounwind readnone speculatable willreturn } -attributes #6 = { inaccessiblememonly nofree nosync nounwind willreturn } -attributes #7 = { nounwind ssp uwtable "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #8 = { ssp } -attributes #9 = { sspreq } -attributes #10 = { argmemonly nofree nosync nounwind willreturn writeonly } -attributes #11 = { cold minsize noreturn ssp } -attributes #12 = { noreturn nounwind } -attributes #13 = { nounwind } -attributes #14 = { noinline } -attributes #15 = { nounwind willreturn } -attributes #16 = { willreturn } -attributes #17 = { noreturn } - -!llvm.module.flags = !{!0, !1, !2} -!llvm.dbg.cu = !{!3} - -!0 = !{i32 2, !"Debug Info Version", i32 3} -!1 = !{i32 4, !"cf-protection-return", i32 1} -!2 = !{i32 4, !"cf-protection-branch", i32 1} -!3 = distinct !DICompileUnit(language: DW_LANG_C, file: !4, producer: "Sorbet LLVM", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !5) -!4 = !DIFile(filename: "test/testdata/compiler/block_arg_expand.rb", directory: ".") -!5 = !{} -!6 = !{!7, !7, i64 0} -!7 = !{!"long", !8, i64 0} -!8 = !{!"omnipotent char", !9, i64 0} -!9 = !{!"Simple C/C++ TBAA"} -!10 = distinct !DISubprogram(name: ".", linkageName: "func_.13$block_1", scope: !11, file: !4, line: 5, type: !12, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!11 = distinct !DISubprogram(name: ".", linkageName: "func_.13", scope: null, file: !4, line: 5, type: !12, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!12 = !DISubroutineType(types: !13) -!13 = !{!14} -!14 = !DIBasicType(name: "VALUE", size: 64, encoding: DW_ATE_signed) -!15 = !{!16, !16, i64 0} -!16 = !{!"any pointer", !8, i64 0} -!17 = !{!18, !16, i64 16} -!18 = !{!"rb_execution_context_struct", !16, i64 0, !7, i64 8, !16, i64 16, !16, i64 24, !16, i64 32, !19, i64 40, !19, i64 44, !16, i64 48, !16, i64 56, !16, i64 64, !7, i64 72, !7, i64 80, !16, i64 88, !7, i64 96, !16, i64 104, !16, i64 112, !7, i64 120, !7, i64 128, !8, i64 136, !8, i64 137, !7, i64 144, !20, i64 152} -!19 = !{!"int", !8, i64 0} -!20 = !{!"", !16, i64 0, !16, i64 8, !7, i64 16, !8, i64 24} -!21 = !{!22, !16, i64 16} -!22 = !{!"rb_control_frame_struct", !16, i64 0, !16, i64 8, !16, i64 16, !7, i64 24, !16, i64 32, !16, i64 40, !16, i64 48} -!23 = !{!22, !16, i64 32} -!24 = !DILocation(line: 5, column: 1, scope: !10) -!25 = !{!26, !7, i64 0} -!26 = !{!"RBasic", !7, i64 0, !7, i64 8} -!27 = !{!8, !8, i64 0} -!28 = !{!"branch_weights", i32 1, i32 2000} -!29 = !DILocation(line: 8, column: 17, scope: !10) -!30 = !DILocation(line: 9, column: 3, scope: !10) -!31 = !{!"branch_weights", i32 2000, i32 1} -!32 = !DILocation(line: 9, column: 25, scope: !10) -!33 = !{!34} -!34 = distinct !{!34, !35, !"sorbet_rb_int_plus: argument 0"} -!35 = distinct !{!35, !"sorbet_rb_int_plus"} -!36 = !{!"branch_weights", i32 4001, i32 4000000} -!37 = !{!18, !19, i64 40} -!38 = !{!18, !19, i64 44} -!39 = !{!18, !16, i64 56} -!40 = distinct !DISubprogram(name: ".", linkageName: "func_.13$block_2", scope: !11, file: !4, line: 5, type: !12, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!41 = !{!22, !7, i64 24} -!42 = !DILocation(line: 5, column: 1, scope: !40) -!43 = !DILocation(line: 13, column: 12, scope: !40) -!44 = !DILocation(line: 14, column: 3, scope: !40) -!45 = distinct !DISubprogram(name: ".", linkageName: "func_.13$block_3", scope: !11, file: !4, line: 5, type: !12, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!46 = !DILocation(line: 5, column: 1, scope: !45) -!47 = !DILocation(line: 18, column: 12, scope: !45) -!48 = !DILocation(line: 18, column: 18, scope: !45) -!49 = !DILocation(line: 0, scope: !45) -!50 = !DILocation(line: 19, column: 3, scope: !45) -!51 = distinct !DISubprogram(name: ".", linkageName: "func_.13$block_4", scope: !11, file: !4, line: 5, type: !12, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!52 = !DILocation(line: 5, column: 1, scope: !51) -!53 = !DILocation(line: 23, column: 14, scope: !51) -!54 = !DILocation(line: 23, column: 24, scope: !51) -!55 = !DILocation(line: 23, column: 26, scope: !51) -!56 = !DILocation(line: 0, scope: !51) -!57 = !DILocation(line: 24, column: 3, scope: !51) -!58 = !DILocation(line: 25, column: 3, scope: !51) -!59 = distinct !DISubprogram(name: ".", linkageName: "func_.13$block_5", scope: !11, file: !4, line: 5, type: !12, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!60 = !DILocation(line: 5, column: 1, scope: !59) -!61 = !DILocation(line: 28, column: 15, scope: !59) -!62 = !DILocation(line: 28, column: 17, scope: !59) -!63 = !DILocation(line: 0, scope: !59) -!64 = !DILocation(line: 29, column: 3, scope: !59) -!65 = !DILocation(line: 30, column: 3, scope: !59) -!66 = !DILocation(line: 0, scope: !11) -!67 = !DILocation(line: 5, column: 6, scope: !11) -!68 = !{!69} -!69 = distinct !{!69, !70, !"sorbet_buildArrayIntrinsic: argument 0"} -!70 = distinct !{!70, !"sorbet_buildArrayIntrinsic"} -!71 = !DILocation(line: 5, column: 5, scope: !11) -!72 = !{!73} -!73 = distinct !{!73, !74, !"sorbet_buildArrayIntrinsic: argument 0"} -!74 = distinct !{!74, !"sorbet_buildArrayIntrinsic"} -!75 = !DILocation(line: 8, column: 1, scope: !11) -!76 = !{!77, !7, i64 0} -!77 = !{!"sorbet_inlineIntrinsicEnv", !7, i64 0, !7, i64 8, !19, i64 16, !16, i64 24, !7, i64 32} -!78 = !{!77, !7, i64 8} -!79 = !{!77, !19, i64 16} -!80 = !{!81} -!81 = distinct !{!81, !82, !"VM_BH_FROM_IFUNC_BLOCK: argument 0"} -!82 = distinct !{!82, !"VM_BH_FROM_IFUNC_BLOCK"} -!83 = !DILocation(line: 8, column: 1, scope: !11, inlinedAt: !84) -!84 = distinct !DILocation(line: 8, column: 1, scope: !11) -!85 = !{!18, !7, i64 128} -!86 = distinct !{!86, !87} -!87 = !{!"llvm.loop.unroll.disable"} -!88 = !DILocation(line: 13, column: 1, scope: !11) -!89 = !{!90} -!90 = distinct !{!90, !91, !"VM_BH_FROM_IFUNC_BLOCK: argument 0"} -!91 = distinct !{!91, !"VM_BH_FROM_IFUNC_BLOCK"} -!92 = !DILocation(line: 13, column: 1, scope: !11, inlinedAt: !93) -!93 = distinct !DILocation(line: 13, column: 1, scope: !11) -!94 = !{!95} -!95 = distinct !{!95, !96, !"func_.13$block_2: %argArray"} -!96 = distinct !{!96, !"func_.13$block_2"} -!97 = !DILocation(line: 13, column: 12, scope: !40, inlinedAt: !98) -!98 = distinct !DILocation(line: 13, column: 1, scope: !11, inlinedAt: !93) -!99 = !DILocation(line: 14, column: 3, scope: !40, inlinedAt: !98) -!100 = distinct !{!100, !87} -!101 = !DILocation(line: 18, column: 1, scope: !11) -!102 = !{!103} -!103 = distinct !{!103, !104, !"VM_BH_FROM_IFUNC_BLOCK: argument 0"} -!104 = distinct !{!104, !"VM_BH_FROM_IFUNC_BLOCK"} -!105 = !DILocation(line: 18, column: 1, scope: !11, inlinedAt: !106) -!106 = distinct !DILocation(line: 18, column: 1, scope: !11) -!107 = !{!108} -!108 = distinct !{!108, !109, !"func_.13$block_3: %argArray"} -!109 = distinct !{!109, !"func_.13$block_3"} -!110 = !DILocation(line: 19, column: 3, scope: !45, inlinedAt: !111) -!111 = distinct !DILocation(line: 18, column: 1, scope: !11, inlinedAt: !106) -!112 = distinct !{!112, !87} -!113 = !DILocation(line: 23, column: 1, scope: !11) -!114 = !{!115} -!115 = distinct !{!115, !116, !"VM_BH_FROM_IFUNC_BLOCK: argument 0"} -!116 = distinct !{!116, !"VM_BH_FROM_IFUNC_BLOCK"} -!117 = !DILocation(line: 23, column: 1, scope: !11, inlinedAt: !118) -!118 = distinct !DILocation(line: 23, column: 1, scope: !11) -!119 = distinct !{!119, !87} -!120 = !DILocation(line: 28, column: 1, scope: !11) -!121 = !{!122} -!122 = distinct !{!122, !123, !"VM_BH_FROM_IFUNC_BLOCK: argument 0"} -!123 = distinct !{!123, !"VM_BH_FROM_IFUNC_BLOCK"} -!124 = !DILocation(line: 28, column: 1, scope: !11, inlinedAt: !125) -!125 = distinct !DILocation(line: 28, column: 1, scope: !11) -!126 = distinct !{!126, !87} -!127 = distinct !DISubprogram(name: "func_.13$block_1.cold.1", linkageName: "func_.13$block_1.cold.1", scope: null, file: !4, type: !128, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !3, retainedNodes: !5) -!128 = !DISubroutineType(types: !5) -!129 = !DILocation(line: 9, column: 25, scope: !127) diff --git a/test/testdata/compiler/block_args.opt.ll.exp b/test/testdata/compiler/block_args.opt.ll.exp deleted file mode 100644 index c8f2e64858..0000000000 --- a/test/testdata/compiler/block_args.opt.ll.exp +++ /dev/null @@ -1,560 +0,0 @@ -; ModuleID = 'payload' -source_filename = "llvm-link" -target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } -%struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.__pthread_cond_s } -%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } -%union.anon = type { i64 } -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } -%struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } -%struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } -%struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } -%struct.anon.0 = type { i16, [2 x i8] } -%struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } -%struct.rb_iseq_location_struct = type { i64, i64, i64, i64, i32, %struct.rb_code_location_struct } -%struct.rb_code_location_struct = type { %struct.rb_code_position_struct, %struct.rb_code_position_struct } -%struct.rb_code_position_struct = type { i32, i32 } -%struct.iseq_insn_info = type { %struct.iseq_insn_info_entry*, i32*, i32, %struct.succ_index_table* } -%struct.iseq_insn_info_entry = type opaque -%struct.succ_index_table = type opaque -%struct.iseq_catch_table = type opaque -%union.iseq_inline_storage_entry = type { %struct.iseq_inline_cache_entry } -%struct.iseq_inline_cache_entry = type { i64, %struct.rb_cref_struct*, i64 } -%struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } -%struct.rb_scope_visi_struct = type { i8, [3 x i8] } -%struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } -%struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } -%union.anon.13 = type { %struct.rb_method_cfunc_struct } -%struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%union.anon.15 = type { i32 } -%struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.16 = type { i64, i64, i64, i64* } -%union.anon.17 = type { %struct.anon.18 } -%struct.anon.18 = type { i64, i32 } -%struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } -%struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } -%struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } -%struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } -%struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } -%struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } -%struct.__sigset_t = type { [16 x i64] } -%struct.rb_calling_info = type { i64, i64, i32, i32 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } -%union.anon.9 = type { %union.pthread_cond_t } -%struct.rb_unblock_callback = type { void (i8*)*, i8* } -%struct.rb_mutex_struct = type opaque -%struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.10 = type { %struct.anon.11 } -%struct.anon.11 = type { i64, i64, i32 } -%struct.rb_fiber_struct = type opaque -%struct.anon.5 = type { [65 x i64] } -%struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } -%struct.rb_event_hook_struct = type opaque -%struct.rb_postponed_job_struct = type opaque -%struct.list_head = type { %struct.list_node } -%union.pthread_mutex_t = type { %struct.__pthread_mutex_s } -%struct.__pthread_mutex_s = type { i32, i32, i32, i32, i32, i16, i16, %struct.__pthread_internal_list } -%struct.__pthread_internal_list = type { %struct.__pthread_internal_list*, %struct.__pthread_internal_list* } -%struct.rb_objspace = type opaque -%struct.rb_at_exit_list = type { void (%struct.rb_vm_struct*)*, %struct.rb_at_exit_list* } -%struct.st_table = type { i8, i8, i8, i32, %struct.st_hash_type*, i64, i64*, i64, i64, %struct.st_table_entry* } -%struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } -%struct.st_table_entry = type opaque -%struct.rb_builtin_function = type opaque -%struct.anon.6 = type { i64, i64, i64, i64 } -%struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } -%struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } -%struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } -%struct.rb_call_info_with_kwarg = type { %struct.rb_call_info, %struct.rb_call_info_kw_arg* } -%struct.rb_call_info_kw_arg = type { i32, [1 x i64] } -%struct.rb_captured_block = type { i64, i64*, %union.anon.20 } -%union.anon.20 = type { %struct.rb_iseq_struct* } -%struct.vm_ifunc = type { i64, i64, i64 (i64, i64, i32, i64*, i64)*, i8*, %struct.rb_code_position_struct } -%struct.iseq_inline_iv_cache_entry = type { i64, i64 } -%struct.sorbet_inlineIntrinsicEnv = type { i64, i64, i32, i64*, i64 } -%struct.RArray = type { %struct.iseq_inline_iv_cache_entry, %union.anon.28 } -%union.anon.28 = type { %struct.anon.29 } -%struct.anon.29 = type { i64, %union.anon, i64* } - -@ruby_current_vm_ptr = external local_unnamed_addr global %struct.rb_vm_struct*, align 8 -@ruby_current_execution_context_ptr = external local_unnamed_addr global %struct.rb_execution_context_struct*, align 8 -@rb_eRuntimeError = external local_unnamed_addr global i64, align 8 -@.str.9 = private unnamed_addr constant [95 x i8] c"sorbet_getBuildSCMRevision: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@.str.10 = private unnamed_addr constant [93 x i8] c"sorbet_getIsReleaseBuild: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@"stackFramePrecomputed_func_.13" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@iseqEncodedArray = internal global [5 x i64] zeroinitializer -@fileLineNumberInfo = internal global %struct.SorbetLineNumberInfo zeroinitializer -@"stackFramePrecomputed_func_.13$block_1" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"func_.13$block_1_ifunc" = internal unnamed_addr global i64 0 -@"ic_+" = internal global %struct.FunctionInlineCache zeroinitializer -@ic_puts = internal global %struct.FunctionInlineCache zeroinitializer -@sorbet_moduleStringTable = internal unnamed_addr constant [112 x i8] c"\00test/testdata/compiler/block_args.rb\00block in \00\00map\00+\00puts\00master\00", align 1 -@sorbet_moduleIDTable = internal unnamed_addr global [6 x i64] zeroinitializer, align 8 -@sorbet_moduleIDDescriptors = internal unnamed_addr constant [6 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 16 }, %struct.rb_code_position_struct { i32 54, i32 25 }, %struct.rb_code_position_struct { i32 80, i32 13 }, %struct.rb_code_position_struct { i32 94, i32 3 }, %struct.rb_code_position_struct { i32 98, i32 1 }, %struct.rb_code_position_struct { i32 100, i32 4 }], align 8 -@sorbet_moduleRubyStringTable = internal unnamed_addr global [3 x i64] zeroinitializer, align 8 -@sorbet_moduleRubyStringDescriptors = internal unnamed_addr constant [3 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 16 }, %struct.rb_code_position_struct { i32 17, i32 36 }, %struct.rb_code_position_struct { i32 54, i32 25 }], align 8 - -declare i64 @rb_ary_push(i64, i64) local_unnamed_addr #0 - -declare %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64, i64, i64, i64, %struct.rb_iseq_struct*, i32, i32, %struct.SorbetLineNumberInfo*, i64*, i32, i32) local_unnamed_addr #0 - -declare void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo*, i64*, i32) local_unnamed_addr #0 - -declare i64 @sorbet_readRealpath() local_unnamed_addr #0 - -declare void @sorbet_pushBlockFrame(%struct.rb_captured_block*) local_unnamed_addr #0 - -declare void @sorbet_popFrame() local_unnamed_addr #0 - -declare void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) local_unnamed_addr #0 - -declare i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache*, i64) local_unnamed_addr #0 - -declare void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_iseq_struct*) local_unnamed_addr #0 - -declare i64 @sorbet_rb_int_plus_slowpath(i64, i64) local_unnamed_addr #0 - -declare i64 @sorbet_globalConstRegister(i64) local_unnamed_addr #0 - -declare %struct.vm_ifunc* @sorbet_globalConstFetchIfunc(i64) local_unnamed_addr #0 - -declare void @sorbet_vm_intern_ids(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #0 - -declare void @sorbet_vm_init_string_table(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #0 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1 - -declare i64 @rb_ary_new_capa(i64) local_unnamed_addr #0 - -declare i64 @rb_ary_new_from_values(i64, i64*) local_unnamed_addr #0 - -; Function Attrs: noreturn -declare void @rb_raise(i64, i8*, ...) local_unnamed_addr #2 - -declare i64 @rb_int2big(i64) local_unnamed_addr #0 - -; Function Attrs: nofree nosync nounwind readnone speculatable willreturn -declare { i64, i1 } @llvm.sadd.with.overflow.i64(i64, i64) #3 - -; Function Attrs: inaccessiblememonly nofree nosync nounwind willreturn -declare void @llvm.experimental.noalias.scope.decl(metadata) #4 - -declare i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct*, i32) local_unnamed_addr #0 - -declare %struct.vm_ifunc* @rb_vm_ifunc_new(i64 (i64, i64, i32, i64*, i64)*, i8*, i32, i32) local_unnamed_addr #0 - -; Function Attrs: nounwind ssp uwtable -define weak i32 @sorbet_getIsReleaseBuild() local_unnamed_addr #5 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([93 x i8], [93 x i8]* @.str.10, i64 0, i64 0)) #9 - unreachable -} - -; Function Attrs: nounwind ssp uwtable -define weak i8* @sorbet_getBuildSCMRevision() local_unnamed_addr #5 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([95 x i8], [95 x i8]* @.str.9, i64 0, i64 0)) #9 - unreachable -} - -; Function Attrs: ssp -define internal i64 @"func_.13$block_1"(i64 %firstYieldArgRaw, i64 %localsOffset, i32 %argc, i64* nocapture readonly %argArray, i64 %blockArg) #6 !dbg !10 { -functionEntryInitializers: - %0 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !15 - %1 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %0, i64 0, i32 2 - %2 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %1, align 8, !tbaa !17 - %stackFrame = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13$block_1", align 8 - %3 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame, %struct.rb_iseq_struct** %3, align 8, !tbaa !21 - %4 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 4 - %5 = load i64*, i64** %4, align 8, !tbaa !23 - %6 = load i64, i64* %5, align 8, !tbaa !6 - %7 = and i64 %6, -129 - store i64 %7, i64* %5, align 8, !tbaa !6 - %8 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 0 - store i64* getelementptr inbounds ([5 x i64], [5 x i64]* @iseqEncodedArray, i64 0, i64 4), i64** %8, align 8, !tbaa !15 - %default0 = icmp eq i32 %argc, 0, !dbg !24 - br i1 %default0, label %fillFromDefaultBlockDone1.thread, label %fillFromDefaultBlockDone1, !dbg !24, !prof !25 - -fillFromDefaultBlockDone1: ; preds = %functionEntryInitializers - %rawArg_x = load i64, i64* %argArray, align 8, !dbg !24 - %9 = and i64 %rawArg_x, 1, !dbg !26 - %10 = icmp eq i64 %9, 0, !dbg !26 - br i1 %10, label %fillFromDefaultBlockDone1.thread, label %"fastSymCallIntrinsic_Integer_+", !dbg !26, !prof !25 - -fillFromDefaultBlockDone1.thread: ; preds = %functionEntryInitializers, %fillFromDefaultBlockDone1 - %x.sroa.0.017 = phi i64 [ %rawArg_x, %fillFromDefaultBlockDone1 ], [ 8, %functionEntryInitializers ] - %11 = and i64 %x.sroa.0.017, 7, !dbg !26 - %12 = icmp ne i64 %11, 0, !dbg !26 - %13 = and i64 %x.sroa.0.017, -9, !dbg !26 - %14 = icmp eq i64 %13, 0, !dbg !26 - %15 = or i1 %12, %14, !dbg !26 - br i1 %15, label %"alternativeCallIntrinsic_Integer_+", label %sorbet_isa_Integer.exit, !dbg !26 - -sorbet_isa_Integer.exit: ; preds = %fillFromDefaultBlockDone1.thread - %16 = inttoptr i64 %x.sroa.0.017 to %struct.iseq_inline_iv_cache_entry*, !dbg !26 - %17 = getelementptr inbounds %struct.iseq_inline_iv_cache_entry, %struct.iseq_inline_iv_cache_entry* %16, i64 0, i32 0, !dbg !26 - %18 = load i64, i64* %17, align 8, !dbg !26, !tbaa !27 - %19 = and i64 %18, 31, !dbg !26 - %20 = icmp eq i64 %19, 10, !dbg !26 - br i1 %20, label %"fastSymCallIntrinsic_Integer_+", label %"alternativeCallIntrinsic_Integer_+", !dbg !26, !prof !29 - -afterSend: ; preds = %46, %sorbet_rb_int_plus.exit, %"alternativeCallIntrinsic_Integer_+" - %"symIntrinsicRawPhi_+" = phi i64 [ %send, %"alternativeCallIntrinsic_Integer_+" ], [ %37, %sorbet_rb_int_plus.exit ], [ %37, %46 ], !dbg !26 - ret i64 %"symIntrinsicRawPhi_+", !dbg !26 - -"alternativeCallIntrinsic_Integer_+": ; preds = %fillFromDefaultBlockDone1.thread, %sorbet_isa_Integer.exit - %x.sroa.0.018 = phi i64 [ %x.sroa.0.017, %fillFromDefaultBlockDone1.thread ], [ %x.sroa.0.017, %sorbet_isa_Integer.exit ] - %21 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 1, !dbg !26 - %22 = load i64*, i64** %21, align 8, !dbg !26 - store i64 %x.sroa.0.018, i64* %22, align 8, !dbg !26, !tbaa !6 - %23 = getelementptr inbounds i64, i64* %22, i64 1, !dbg !26 - store i64 3, i64* %23, align 8, !dbg !26, !tbaa !6 - %24 = getelementptr inbounds i64, i64* %23, i64 1, !dbg !26 - store i64* %24, i64** %21, align 8, !dbg !26 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @"ic_+", i64 0), !dbg !26 - br label %afterSend, !dbg !26 - -"fastSymCallIntrinsic_Integer_+": ; preds = %fillFromDefaultBlockDone1, %sorbet_isa_Integer.exit - %x.sroa.0.016 = phi i64 [ %rawArg_x, %fillFromDefaultBlockDone1 ], [ %x.sroa.0.017, %sorbet_isa_Integer.exit ] - tail call void @llvm.experimental.noalias.scope.decl(metadata !30), !dbg !26 - %25 = and i64 %x.sroa.0.016, 1, !dbg !26 - %26 = icmp eq i64 %25, 0, !dbg !26 - br i1 %26, label %35, label %27, !dbg !26, !prof !33 - -27: ; preds = %"fastSymCallIntrinsic_Integer_+" - %28 = tail call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %x.sroa.0.016, i64 noundef 2) #10, !dbg !26 - %29 = extractvalue { i64, i1 } %28, 1, !dbg !26 - %30 = extractvalue { i64, i1 } %28, 0, !dbg !26 - br i1 %29, label %31, label %sorbet_rb_int_plus.exit, !dbg !26 - -31: ; preds = %27 - %32 = ashr i64 %30, 1, !dbg !26 - %33 = xor i64 %32, -9223372036854775808, !dbg !26 - %34 = tail call i64 @rb_int2big(i64 %33) #11, !dbg !26 - br label %sorbet_rb_int_plus.exit, !dbg !26 - -35: ; preds = %"fastSymCallIntrinsic_Integer_+" - %36 = tail call i64 @sorbet_rb_int_plus_slowpath(i64 %x.sroa.0.016, i64 noundef 3) #11, !dbg !26, !noalias !30 - br label %sorbet_rb_int_plus.exit, !dbg !26 - -sorbet_rb_int_plus.exit: ; preds = %31, %27, %35 - %37 = phi i64 [ %36, %35 ], [ %34, %31 ], [ %30, %27 ], !dbg !26 - %38 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !26, !tbaa !15 - %39 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %38, i64 0, i32 5, !dbg !26 - %40 = load i32, i32* %39, align 8, !dbg !26, !tbaa !34 - %41 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %38, i64 0, i32 6, !dbg !26 - %42 = load i32, i32* %41, align 4, !dbg !26, !tbaa !35 - %43 = xor i32 %42, -1, !dbg !26 - %44 = and i32 %43, %40, !dbg !26 - %45 = icmp eq i32 %44, 0, !dbg !26 - br i1 %45, label %afterSend, label %46, !dbg !26, !prof !29 - -46: ; preds = %sorbet_rb_int_plus.exit - %47 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %38, i64 0, i32 8, !dbg !26 - %48 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %47, align 8, !dbg !26, !tbaa !36 - %49 = tail call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %48, i32 noundef 0) #11, !dbg !26 - br label %afterSend, !dbg !26 -} - -; Function Attrs: sspreq -define void @Init_block_args() local_unnamed_addr #7 { -entry: - %0 = alloca i64, align 8 - %1 = alloca %struct.sorbet_inlineIntrinsicEnv, align 8 - %callArgs.i = alloca [3 x i64], align 8 - %locals.i.i = alloca i64, i32 0, align 8 - %realpath = tail call i64 @sorbet_readRealpath() - tail call void @sorbet_vm_intern_ids(i64* noundef getelementptr inbounds ([6 x i64], [6 x i64]* @sorbet_moduleIDTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([6 x %struct.rb_code_position_struct], [6 x %struct.rb_code_position_struct]* @sorbet_moduleIDDescriptors, i32 0, i32 0), i32 noundef 6, i8* noundef getelementptr inbounds ([112 x i8], [112 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_vm_init_string_table(i64* noundef getelementptr inbounds ([3 x i64], [3 x i64]* @sorbet_moduleRubyStringTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([3 x %struct.rb_code_position_struct], [3 x %struct.rb_code_position_struct]* @sorbet_moduleRubyStringDescriptors, i32 0, i32 0), i32 noundef 3, i8* noundef getelementptr inbounds ([112 x i8], [112 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef getelementptr inbounds ([5 x i64], [5 x i64]* @iseqEncodedArray, i32 0, i32 0), i32 noundef 5) - %"rubyId_.i.i" = load i64, i64* getelementptr inbounds ([6 x i64], [6 x i64]* @sorbet_moduleIDTable, i64 0, i64 0), align 8, !invariant.load !5 - %"rubyStr_.i.i" = load i64, i64* getelementptr inbounds ([3 x i64], [3 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 0), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/block_args.rb.i.i" = load i64, i64* getelementptr inbounds ([3 x i64], [3 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %2 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_.i.i", i64 %"rubyId_.i.i", i64 %"rubyStr_test/testdata/compiler/block_args.rb.i.i", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 0, i32 noundef 4, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals.i.i, i32 noundef 0, i32 noundef 3) - store %struct.rb_iseq_struct* %2, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - %"rubyId_block in .i.i" = load i64, i64* getelementptr inbounds ([6 x i64], [6 x i64]* @sorbet_moduleIDTable, i64 0, i64 1), align 8, !invariant.load !5 - %"rubyStr_block in .i.i" = load i64, i64* getelementptr inbounds ([3 x i64], [3 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 2), align 8, !invariant.load !5 - %3 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_block in .i.i", i64 %"rubyId_block in .i.i", i64 %"rubyStr_test/testdata/compiler/block_args.rb.i.i", i64 %realpath, %struct.rb_iseq_struct* %2, i32 noundef 2, i32 noundef 4, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef null, i32 noundef 0, i32 noundef 3) - store %struct.rb_iseq_struct* %3, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13$block_1", align 8 - %4 = call %struct.vm_ifunc* @rb_vm_ifunc_new(i64 (i64, i64, i32, i64*, i64)* noundef @"func_.13$block_1", i8* noundef null, i32 noundef 1, i32 noundef 1) #11 - %5 = ptrtoint %struct.vm_ifunc* %4 to i64 - %6 = call i64 @sorbet_globalConstRegister(i64 %5) - store i64 %6, i64* @"func_.13$block_1_ifunc", align 8 - %"rubyId_+.i" = load i64, i64* getelementptr inbounds ([6 x i64], [6 x i64]* @sorbet_moduleIDTable, i64 0, i64 4), align 8, !dbg !26, !invariant.load !5 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @"ic_+", i64 %"rubyId_+.i", i32 noundef 16, i32 noundef 1, i32 noundef 0), !dbg !26 - %rubyId_puts.i = load i64, i64* getelementptr inbounds ([6 x i64], [6 x i64]* @sorbet_moduleIDTable, i64 0, i64 5), align 8, !dbg !37, !invariant.load !5 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts, i64 %rubyId_puts.i, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !37 - %7 = load %struct.rb_vm_struct*, %struct.rb_vm_struct** @ruby_current_vm_ptr, align 8, !tbaa !15 - %8 = getelementptr inbounds %struct.rb_vm_struct, %struct.rb_vm_struct* %7, i64 0, i32 18 - %9 = load i64, i64* %8, align 8, !tbaa !38 - %10 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !15 - %11 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %10, i64 0, i32 2 - %12 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %11, align 8, !tbaa !17 - %13 = bitcast [3 x i64]* %callArgs.i to i8* - call void @llvm.lifetime.start.p0i8(i64 24, i8* nonnull %13) - %stackFrame.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - %14 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %11, align 8, !tbaa !17 - %15 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %14, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame.i, %struct.rb_iseq_struct** %15, align 8, !tbaa !21 - %16 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %14, i64 0, i32 4 - %17 = load i64*, i64** %16, align 8, !tbaa !23 - %18 = load i64, i64* %17, align 8, !tbaa !6 - %19 = and i64 %18, -33 - store i64 %19, i64* %17, align 8, !tbaa !6 - call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %10, %struct.rb_control_frame_struct* %14, %struct.rb_iseq_struct* %stackFrame.i) #11 - %20 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %12, i64 0, i32 0 - store i64* getelementptr inbounds ([5 x i64], [5 x i64]* @iseqEncodedArray, i64 0, i64 4), i64** %20, align 8, !dbg !47, !tbaa !15 - %callArgs0Addr.i = getelementptr [3 x i64], [3 x i64]* %callArgs.i, i64 0, i64 0, !dbg !48 - %21 = bitcast i64* %callArgs0Addr.i to <2 x i64>*, !dbg !48 - store <2 x i64> , <2 x i64>* %21, align 8, !dbg !48 - call void @llvm.experimental.noalias.scope.decl(metadata !49) #11, !dbg !48 - %22 = call i64 @rb_ary_new_from_values(i64 noundef 2, i64* noundef nonnull align 8 %callArgs0Addr.i) #11, !dbg !48 - %rubyId_map.i = load i64, i64* getelementptr inbounds ([6 x i64], [6 x i64]* @sorbet_moduleIDTable, i64 0, i64 3), align 8, !dbg !48, !invariant.load !5 - %23 = load i64, i64* @"func_.13$block_1_ifunc", align 8, !dbg !48 - %24 = call %struct.vm_ifunc* @sorbet_globalConstFetchIfunc(i64 %23) #11, !dbg !48 - %25 = bitcast %struct.sorbet_inlineIntrinsicEnv* %1 to i8*, !dbg !48 - call void @llvm.lifetime.start.p0i8(i64 noundef 40, i8* noundef nonnull %25) #11, !dbg !48 - %26 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %1, i64 0, i32 0, !dbg !48 - store i64 %22, i64* %26, align 8, !dbg !48, !tbaa !52 - %27 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %1, i64 0, i32 1, !dbg !48 - store i64 %rubyId_map.i, i64* %27, align 8, !dbg !48, !tbaa !54 - %28 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %1, i64 0, i32 2, !dbg !48 - store i32 0, i32* %28, align 8, !dbg !48, !tbaa !55 - %29 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %1, i64 0, i32 3, !dbg !48 - %30 = bitcast i64** %29 to i8*, !dbg !48 - call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %30, i8 0, i64 16, i1 false) #11, !dbg !48 - %31 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !48, !tbaa !15 - %32 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %31, i64 0, i32 2, !dbg !48 - %33 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %32, align 8, !dbg !48, !tbaa !17 - %34 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %33, i64 0, i32 3, !dbg !48 - %35 = bitcast i64* %34 to %struct.rb_captured_block*, !dbg !48 - %36 = getelementptr inbounds i64, i64* %34, i64 2, !dbg !48 - %37 = bitcast i64* %36 to %struct.vm_ifunc**, !dbg !48 - store %struct.vm_ifunc* %24, %struct.vm_ifunc** %37, align 8, !dbg !48, !tbaa !56 - call void @llvm.experimental.noalias.scope.decl(metadata !57) #11, !dbg !48 - %38 = ptrtoint %struct.rb_captured_block* %35 to i64, !dbg !48 - %39 = or i64 %38, 3, !dbg !48 - %40 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %31, i64 0, i32 17, !dbg !48 - %41 = and i64 %39, -4, !dbg !60 - %42 = inttoptr i64 %41 to %struct.rb_captured_block*, !dbg !60 - store i64 0, i64* %40, align 8, !dbg !60, !tbaa !62 - call void @sorbet_pushBlockFrame(%struct.rb_captured_block* %42) #11, !dbg !60 - %43 = inttoptr i64 %22 to %struct.iseq_inline_iv_cache_entry*, !dbg !60 - %44 = getelementptr inbounds %struct.iseq_inline_iv_cache_entry, %struct.iseq_inline_iv_cache_entry* %43, i64 0, i32 0, !dbg !60 - %45 = load i64, i64* %44, align 8, !dbg !60, !tbaa !27 - %46 = and i64 %45, 8192, !dbg !60 - %47 = icmp eq i64 %46, 0, !dbg !60 - br i1 %47, label %51, label %48, !dbg !60 - -48: ; preds = %entry - %49 = lshr i64 %45, 15, !dbg !60 - %50 = and i64 %49, 3, !dbg !60 - br label %rb_array_len.exit2.i.i, !dbg !60 - -51: ; preds = %entry - %52 = inttoptr i64 %22 to %struct.RArray*, !dbg !60 - %53 = getelementptr inbounds %struct.RArray, %struct.RArray* %52, i64 0, i32 1, i32 0, i32 0, !dbg !60 - %54 = load i64, i64* %53, align 8, !dbg !60, !tbaa !56 - br label %rb_array_len.exit2.i.i, !dbg !60 - -rb_array_len.exit2.i.i: ; preds = %51, %48 - %55 = phi i64 [ %50, %48 ], [ %54, %51 ], !dbg !60 - %56 = call i64 @rb_ary_new_capa(i64 %55) #11, !dbg !60 - %57 = load i64, i64* %44, align 8, !dbg !60, !tbaa !27 - %58 = and i64 %57, 8192, !dbg !60 - %59 = icmp eq i64 %58, 0, !dbg !60 - br i1 %59, label %63, label %60, !dbg !60 - -60: ; preds = %rb_array_len.exit2.i.i - %61 = lshr i64 %57, 15, !dbg !60 - %62 = and i64 %61, 3, !dbg !60 - br label %rb_array_len.exit1.i.i, !dbg !60 - -63: ; preds = %rb_array_len.exit2.i.i - %64 = inttoptr i64 %22 to %struct.RArray*, !dbg !60 - %65 = getelementptr inbounds %struct.RArray, %struct.RArray* %64, i64 0, i32 1, i32 0, i32 0, !dbg !60 - %66 = load i64, i64* %65, align 8, !dbg !60, !tbaa !56 - br label %rb_array_len.exit1.i.i, !dbg !60 - -rb_array_len.exit1.i.i: ; preds = %63, %60 - %67 = phi i64 [ %62, %60 ], [ %66, %63 ], !dbg !60 - %68 = icmp sgt i64 %67, 0, !dbg !60 - br i1 %68, label %69, label %forward_sorbet_rb_array_collect_withBlock.exit.i, !dbg !60 - -69: ; preds = %rb_array_len.exit1.i.i - %70 = bitcast i64* %0 to i8*, !dbg !60 - %71 = inttoptr i64 %22 to %struct.RArray*, !dbg !48 - %72 = getelementptr inbounds %struct.RArray, %struct.RArray* %71, i64 0, i32 1, i32 0, i32 0, !dbg !48 - %73 = getelementptr inbounds %struct.RArray, %struct.RArray* %71, i64 0, i32 1, i32 0, i32 2, !dbg !48 - br label %74, !dbg !60 - -74: ; preds = %rb_array_len.exit.i.i, %69 - %75 = phi i64 [ 0, %69 ], [ %86, %rb_array_len.exit.i.i ], !dbg !60 - call void @llvm.lifetime.start.p0i8(i64 noundef 8, i8* noundef nonnull align 8 dereferenceable(8) %70) #11, !dbg !60 - %76 = load i64, i64* %44, align 8, !dbg !60, !tbaa !27 - %77 = and i64 %76, 8192, !dbg !60 - %78 = icmp eq i64 %77, 0, !dbg !60 - br i1 %78, label %79, label %rb_array_const_ptr_transient.exit.i.i, !dbg !60 - -79: ; preds = %74 - %80 = load i64*, i64** %73, align 8, !dbg !60, !tbaa !56 - br label %rb_array_const_ptr_transient.exit.i.i, !dbg !60 - -rb_array_const_ptr_transient.exit.i.i: ; preds = %79, %74 - %81 = phi i64* [ %80, %79 ], [ %72, %74 ], !dbg !60 - %82 = getelementptr inbounds i64, i64* %81, i64 %75, !dbg !60 - %83 = load i64, i64* %82, align 8, !dbg !60, !tbaa !6 - store i64 %83, i64* %0, align 8, !dbg !60, !tbaa !6 - %84 = call i64 @"func_.13$block_1"(i64 undef, i64 undef, i32 noundef 1, i64* noalias nocapture noundef nonnull readonly align 8 dereferenceable(8) %0, i64 undef) #11, !dbg !60 - %85 = call i64 @rb_ary_push(i64 %56, i64 %84) #11, !dbg !60 - call void @llvm.lifetime.end.p0i8(i64 noundef 8, i8* noundef nonnull %70) #11, !dbg !60 - %86 = add nuw nsw i64 %75, 1, !dbg !60 - %87 = load i64, i64* %44, align 8, !dbg !60, !tbaa !27 - %88 = and i64 %87, 8192, !dbg !60 - %89 = icmp eq i64 %88, 0, !dbg !60 - br i1 %89, label %93, label %90, !dbg !60 - -90: ; preds = %rb_array_const_ptr_transient.exit.i.i - %91 = lshr i64 %87, 15, !dbg !60 - %92 = and i64 %91, 3, !dbg !60 - br label %rb_array_len.exit.i.i, !dbg !60 - -93: ; preds = %rb_array_const_ptr_transient.exit.i.i - %94 = load i64, i64* %72, align 8, !dbg !60, !tbaa !56 - br label %rb_array_len.exit.i.i, !dbg !60 - -rb_array_len.exit.i.i: ; preds = %93, %90 - %95 = phi i64 [ %92, %90 ], [ %94, %93 ], !dbg !60 - %96 = icmp slt i64 %86, %95, !dbg !60 - br i1 %96, label %74, label %forward_sorbet_rb_array_collect_withBlock.exit.i, !dbg !60, !llvm.loop !63 - -forward_sorbet_rb_array_collect_withBlock.exit.i: ; preds = %rb_array_len.exit.i.i, %rb_array_len.exit1.i.i - call void @sorbet_popFrame() #11, !dbg !60 - call void @llvm.lifetime.end.p0i8(i64 noundef 40, i8* noundef nonnull %25) #11, !dbg !48 - %97 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !48, !tbaa !15 - %98 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %97, i64 0, i32 5, !dbg !48 - %99 = load i32, i32* %98, align 8, !dbg !48, !tbaa !34 - %100 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %97, i64 0, i32 6, !dbg !48 - %101 = load i32, i32* %100, align 4, !dbg !48, !tbaa !35 - %102 = xor i32 %101, -1, !dbg !48 - %103 = and i32 %102, %99, !dbg !48 - %104 = icmp eq i32 %103, 0, !dbg !48 - br i1 %104, label %"func_.13.exit", label %105, !dbg !48, !prof !29 - -105: ; preds = %forward_sorbet_rb_array_collect_withBlock.exit.i - %106 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %97, i64 0, i32 8, !dbg !48 - %107 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %106, align 8, !dbg !48, !tbaa !36 - %108 = call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %107, i32 noundef 0) #11, !dbg !48 - br label %"func_.13.exit", !dbg !48 - -"func_.13.exit": ; preds = %forward_sorbet_rb_array_collect_withBlock.exit.i, %105 - store i64* getelementptr inbounds ([5 x i64], [5 x i64]* @iseqEncodedArray, i64 0, i64 4), i64** %20, align 8, !tbaa !15 - %109 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %12, i64 0, i32 1, !dbg !37 - %110 = load i64*, i64** %109, align 8, !dbg !37 - store i64 %9, i64* %110, align 8, !dbg !37, !tbaa !6 - %111 = getelementptr inbounds i64, i64* %110, i64 1, !dbg !37 - store i64 %56, i64* %111, align 8, !dbg !37, !tbaa !6 - %112 = getelementptr inbounds i64, i64* %111, i64 1, !dbg !37 - store i64* %112, i64** %109, align 8, !dbg !37 - %send = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts, i64 0), !dbg !37 - call void @llvm.lifetime.end.p0i8(i64 24, i8* nonnull %13) - ret void -} - -; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly -declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #8 - -attributes #0 = { "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #1 = { argmemonly nofree nosync nounwind willreturn } -attributes #2 = { noreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #3 = { nofree nosync nounwind readnone speculatable willreturn } -attributes #4 = { inaccessiblememonly nofree nosync nounwind willreturn } -attributes #5 = { nounwind ssp uwtable "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #6 = { ssp } -attributes #7 = { sspreq } -attributes #8 = { argmemonly nofree nosync nounwind willreturn writeonly } -attributes #9 = { noreturn nounwind } -attributes #10 = { nounwind willreturn } -attributes #11 = { nounwind } - -!llvm.module.flags = !{!0, !1, !2} -!llvm.dbg.cu = !{!3} - -!0 = !{i32 2, !"Debug Info Version", i32 3} -!1 = !{i32 4, !"cf-protection-return", i32 1} -!2 = !{i32 4, !"cf-protection-branch", i32 1} -!3 = distinct !DICompileUnit(language: DW_LANG_C, file: !4, producer: "Sorbet LLVM", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !5) -!4 = !DIFile(filename: "test/testdata/compiler/block_args.rb", directory: ".") -!5 = !{} -!6 = !{!7, !7, i64 0} -!7 = !{!"long", !8, i64 0} -!8 = !{!"omnipotent char", !9, i64 0} -!9 = !{!"Simple C/C++ TBAA"} -!10 = distinct !DISubprogram(name: ".", linkageName: "func_.13$block_1", scope: !11, file: !4, line: 4, type: !12, scopeLine: 4, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!11 = distinct !DISubprogram(name: ".", linkageName: "func_.13", scope: null, file: !4, line: 4, type: !12, scopeLine: 4, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!12 = !DISubroutineType(types: !13) -!13 = !{!14} -!14 = !DIBasicType(name: "VALUE", size: 64, encoding: DW_ATE_signed) -!15 = !{!16, !16, i64 0} -!16 = !{!"any pointer", !8, i64 0} -!17 = !{!18, !16, i64 16} -!18 = !{!"rb_execution_context_struct", !16, i64 0, !7, i64 8, !16, i64 16, !16, i64 24, !16, i64 32, !19, i64 40, !19, i64 44, !16, i64 48, !16, i64 56, !16, i64 64, !7, i64 72, !7, i64 80, !16, i64 88, !7, i64 96, !16, i64 104, !16, i64 112, !7, i64 120, !7, i64 128, !8, i64 136, !8, i64 137, !7, i64 144, !20, i64 152} -!19 = !{!"int", !8, i64 0} -!20 = !{!"", !16, i64 0, !16, i64 8, !7, i64 16, !8, i64 24} -!21 = !{!22, !16, i64 16} -!22 = !{!"rb_control_frame_struct", !16, i64 0, !16, i64 8, !16, i64 16, !7, i64 24, !16, i64 32, !16, i64 40, !16, i64 48} -!23 = !{!22, !16, i64 32} -!24 = !DILocation(line: 4, column: 1, scope: !10) -!25 = !{!"branch_weights", i32 1, i32 2000} -!26 = !DILocation(line: 4, column: 21, scope: !10) -!27 = !{!28, !7, i64 0} -!28 = !{!"RBasic", !7, i64 0, !7, i64 8} -!29 = !{!"branch_weights", i32 2000, i32 1} -!30 = !{!31} -!31 = distinct !{!31, !32, !"sorbet_rb_int_plus: argument 0"} -!32 = distinct !{!32, !"sorbet_rb_int_plus"} -!33 = !{!"branch_weights", i32 4001, i32 4000000} -!34 = !{!18, !19, i64 40} -!35 = !{!18, !19, i64 44} -!36 = !{!18, !16, i64 56} -!37 = !DILocation(line: 4, column: 1, scope: !11) -!38 = !{!39, !7, i64 400} -!39 = !{!"rb_vm_struct", !7, i64 0, !40, i64 8, !16, i64 192, !16, i64 200, !16, i64 208, !43, i64 216, !8, i64 224, !41, i64 264, !41, i64 280, !41, i64 296, !41, i64 312, !7, i64 328, !19, i64 336, !19, i64 340, !19, i64 344, !19, i64 344, !19, i64 344, !19, i64 344, !19, i64 348, !7, i64 352, !8, i64 360, !7, i64 400, !7, i64 408, !7, i64 416, !7, i64 424, !7, i64 432, !7, i64 440, !7, i64 448, !16, i64 456, !16, i64 464, !44, i64 472, !45, i64 992, !16, i64 1016, !16, i64 1024, !19, i64 1032, !19, i64 1036, !41, i64 1040, !8, i64 1056, !7, i64 1096, !7, i64 1104, !7, i64 1112, !7, i64 1120, !7, i64 1128, !19, i64 1136, !16, i64 1144, !16, i64 1152, !16, i64 1160, !16, i64 1168, !16, i64 1176, !16, i64 1184, !19, i64 1192, !46, i64 1200, !8, i64 1232} -!40 = !{!"rb_global_vm_lock_struct", !16, i64 0, !8, i64 8, !41, i64 48, !16, i64 64, !19, i64 72, !8, i64 80, !8, i64 128, !19, i64 176, !19, i64 180} -!41 = !{!"list_head", !42, i64 0} -!42 = !{!"list_node", !16, i64 0, !16, i64 8} -!43 = !{!"long long", !8, i64 0} -!44 = !{!"", !8, i64 0} -!45 = !{!"rb_hook_list_struct", !16, i64 0, !19, i64 8, !19, i64 12, !19, i64 16} -!46 = !{!"", !7, i64 0, !7, i64 8, !7, i64 16, !7, i64 24} -!47 = !DILocation(line: 0, scope: !11) -!48 = !DILocation(line: 4, column: 6, scope: !11) -!49 = !{!50} -!50 = distinct !{!50, !51, !"sorbet_buildArrayIntrinsic: argument 0"} -!51 = distinct !{!51, !"sorbet_buildArrayIntrinsic"} -!52 = !{!53, !7, i64 0} -!53 = !{!"sorbet_inlineIntrinsicEnv", !7, i64 0, !7, i64 8, !19, i64 16, !16, i64 24, !7, i64 32} -!54 = !{!53, !7, i64 8} -!55 = !{!53, !19, i64 16} -!56 = !{!8, !8, i64 0} -!57 = !{!58} -!58 = distinct !{!58, !59, !"VM_BH_FROM_IFUNC_BLOCK: argument 0"} -!59 = distinct !{!59, !"VM_BH_FROM_IFUNC_BLOCK"} -!60 = !DILocation(line: 4, column: 6, scope: !11, inlinedAt: !61) -!61 = distinct !DILocation(line: 4, column: 6, scope: !11) -!62 = !{!18, !7, i64 128} -!63 = distinct !{!63, !64} -!64 = !{!"llvm.loop.unroll.disable"} diff --git a/test/testdata/compiler/block_as_parameter.rb b/test/testdata/compiler/block_as_parameter.rb index 54074165d8..e6e0b3eec6 100644 --- a/test/testdata/compiler/block_as_parameter.rb +++ b/test/testdata/compiler/block_as_parameter.rb @@ -1,19 +1,13 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL -# run_filecheck: OPT class A def takes_block(arg, &blk) puts arg call_param(blk) end -# INITIAL-LABEL: "func_A#11takes_block" -# INITIAL: call i64 @sorbet_getMethodBlockAsProc -# OPT-LABEL: "func_A#11takes_block" -# OPT: call i64 @rb_block_proc def call_param(proc) proc.call diff --git a/test/testdata/compiler/block_call_rb_iterate__1.rb b/test/testdata/compiler/block_call_rb_iterate__1.rb index 6eb7eff06b..c3989fe8e2 100644 --- a/test/testdata/compiler/block_call_rb_iterate__1.rb +++ b/test/testdata/compiler/block_call_rb_iterate__1.rb @@ -1,7 +1,6 @@ # compiled: true # frozen_string_literal: true # typed: true -# run_filecheck: OPT require_relative './block_call_rb_iterate__2' @@ -10,11 +9,6 @@ # the block doesn't use `break` to change control flow, while the second does # use break and should use `rb_iterate` in the generated code. -# OPT-ENTRY: Init_block_call_rb_iterate__1 -# OPT: call i64 @sorbet_rb_iterate( -# OPT-NOT: call i64 @sorbet_rb_iterate( -# OPT{LITERAL}: } - a = test do puts "without a break" end diff --git a/test/testdata/compiler/block_no_args.opt.ll.exp b/test/testdata/compiler/block_no_args.opt.ll.exp deleted file mode 100644 index d9b1dfaa30..0000000000 --- a/test/testdata/compiler/block_no_args.opt.ll.exp +++ /dev/null @@ -1,341 +0,0 @@ -; ModuleID = 'payload' -source_filename = "llvm-link" -target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } -%struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } -%struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } -%struct.anon.0 = type { i16, [2 x i8] } -%struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } -%struct.rb_iseq_location_struct = type { i64, i64, i64, i64, i32, %struct.rb_code_location_struct } -%struct.rb_code_location_struct = type { %struct.rb_code_position_struct, %struct.rb_code_position_struct } -%struct.rb_code_position_struct = type { i32, i32 } -%struct.iseq_insn_info = type { %struct.iseq_insn_info_entry*, i32*, i32, %struct.succ_index_table* } -%struct.iseq_insn_info_entry = type opaque -%struct.succ_index_table = type opaque -%struct.iseq_catch_table = type opaque -%union.iseq_inline_storage_entry = type { %struct.iseq_inline_cache_entry } -%struct.iseq_inline_cache_entry = type { i64, %struct.rb_cref_struct*, i64 } -%struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } -%struct.rb_scope_visi_struct = type { i8, [3 x i8] } -%struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } -%struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } -%union.anon.13 = type { %struct.rb_method_cfunc_struct } -%struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%struct.rb_calling_info = type { i64, i64, i32, i32 } -%union.anon.15 = type { i32 } -%struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.16 = type { i64, i64, i64, i64* } -%union.anon.17 = type { %struct.anon.18 } -%struct.anon.18 = type { i64, i32 } -%struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } -%struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } -%struct.rb_fiber_struct = type opaque -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } -%struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } -%struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.__pthread_cond_s } -%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } -%union.anon = type { i64 } -%struct.anon.5 = type { [65 x i64] } -%struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } -%struct.rb_event_hook_struct = type opaque -%struct.rb_postponed_job_struct = type opaque -%struct.list_head = type { %struct.list_node } -%struct.rb_objspace = type opaque -%struct.rb_at_exit_list = type { void (%struct.rb_vm_struct*)*, %struct.rb_at_exit_list* } -%struct.rb_builtin_function = type opaque -%struct.anon.6 = type { i64, i64, i64, i64 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } -%union.anon.9 = type { %union.pthread_cond_t } -%union.pthread_mutex_t = type { %struct.__pthread_mutex_s } -%struct.__pthread_mutex_s = type { i32, i32, i32, i32, i32, i16, i16, %struct.__pthread_internal_list } -%struct.__pthread_internal_list = type { %struct.__pthread_internal_list*, %struct.__pthread_internal_list* } -%struct.rb_unblock_callback = type { void (i8*)*, i8* } -%struct.rb_mutex_struct = type opaque -%struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.10 = type { %struct.anon.11 } -%struct.anon.11 = type { i64, i64, i32 } -%struct.st_table = type { i8, i8, i8, i32, %struct.st_hash_type*, i64, i64*, i64, i64, %struct.st_table_entry* } -%struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } -%struct.st_table_entry = type opaque -%struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } -%struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } -%struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } -%struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } -%struct.__sigset_t = type { [16 x i64] } -%struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } -%struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } -%struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } -%struct.rb_call_info_with_kwarg = type { %struct.rb_call_info, %struct.rb_call_info_kw_arg* } -%struct.rb_call_info_kw_arg = type { i32, [1 x i64] } -%struct.rb_captured_block = type { i64, i64*, %union.anon.20 } -%union.anon.20 = type { %struct.rb_iseq_struct* } -%struct.vm_ifunc = type { i64, i64, i64 (i64, i64, i32, i64*, i64)*, i8*, %struct.rb_code_position_struct } - -@ruby_current_execution_context_ptr = external local_unnamed_addr global %struct.rb_execution_context_struct*, align 8 -@rb_eRuntimeError = external local_unnamed_addr global i64, align 8 -@.str.9 = private unnamed_addr constant [95 x i8] c"sorbet_getBuildSCMRevision: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@.str.10 = private unnamed_addr constant [93 x i8] c"sorbet_getIsReleaseBuild: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@"stackFramePrecomputed_func_.13" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@iseqEncodedArray = internal global [7 x i64] zeroinitializer -@fileLineNumberInfo = internal global %struct.SorbetLineNumberInfo zeroinitializer -@"stackFramePrecomputed_func_.13$block_1" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"func_.13$block_1_ifunc" = internal unnamed_addr global i64 0 -@ic_puts = internal global %struct.FunctionInlineCache zeroinitializer -@sorbet_moduleStringTable = internal unnamed_addr constant [111 x i8] c"\00test/testdata/compiler/block_no_args.rb\00block in \00times\00hi\00puts\00Kernel\00master\00", align 1 -@sorbet_moduleIDTable = internal unnamed_addr global [4 x i64] zeroinitializer, align 8 -@sorbet_moduleIDDescriptors = internal unnamed_addr constant [4 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 16 }, %struct.rb_code_position_struct { i32 57, i32 25 }, %struct.rb_code_position_struct { i32 83, i32 5 }, %struct.rb_code_position_struct { i32 92, i32 4 }], align 8 -@sorbet_moduleRubyStringTable = internal unnamed_addr global [4 x i64] zeroinitializer, align 8 -@sorbet_moduleRubyStringDescriptors = internal unnamed_addr constant [4 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 16 }, %struct.rb_code_position_struct { i32 17, i32 39 }, %struct.rb_code_position_struct { i32 57, i32 25 }, %struct.rb_code_position_struct { i32 89, i32 2 }], align 8 -@rb_mKernel = external local_unnamed_addr constant i64 - -declare %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64, i64, i64, i64, %struct.rb_iseq_struct*, i32, i32, %struct.SorbetLineNumberInfo*, i64*, i32, i32) local_unnamed_addr #0 - -declare void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo*, i64*, i32) local_unnamed_addr #0 - -declare i64 @sorbet_readRealpath() local_unnamed_addr #0 - -declare void @sorbet_pushBlockFrame(%struct.rb_captured_block*) local_unnamed_addr #0 - -declare void @sorbet_popFrame() local_unnamed_addr #0 - -declare void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) local_unnamed_addr #0 - -declare i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache*, i64) local_unnamed_addr #0 - -declare void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_iseq_struct*) local_unnamed_addr #0 - -declare i64 @sorbet_globalConstRegister(i64) local_unnamed_addr #0 - -declare %struct.vm_ifunc* @sorbet_globalConstFetchIfunc(i64) local_unnamed_addr #0 - -declare void @sorbet_vm_intern_ids(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #0 - -declare void @sorbet_vm_init_string_table(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #0 - -; Function Attrs: noreturn -declare void @rb_raise(i64, i8*, ...) local_unnamed_addr #1 - -declare i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct*, i32) local_unnamed_addr #0 - -declare %struct.vm_ifunc* @rb_vm_ifunc_new(i64 (i64, i64, i32, i64*, i64)*, i8*, i32, i32) local_unnamed_addr #0 - -; Function Attrs: nounwind ssp uwtable -define weak i32 @sorbet_getIsReleaseBuild() local_unnamed_addr #2 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([93 x i8], [93 x i8]* @.str.10, i64 0, i64 0)) #6 - unreachable -} - -; Function Attrs: nounwind ssp uwtable -define weak i8* @sorbet_getBuildSCMRevision() local_unnamed_addr #2 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([95 x i8], [95 x i8]* @.str.9, i64 0, i64 0)) #6 - unreachable -} - -; Function Attrs: ssp -define internal i64 @"func_.13$block_1"(i64 %firstYieldArgRaw, i64 %localsOffset, i32 %argc, i64* nocapture nofree readnone %argArray, i64 %blockArg) #3 !dbg !10 { -functionEntryInitializers: - %0 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !15 - %1 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %0, i64 0, i32 2 - %2 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %1, align 8, !tbaa !17 - %stackFrame = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13$block_1", align 8 - %3 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame, %struct.rb_iseq_struct** %3, align 8, !tbaa !21 - %4 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 4 - %5 = load i64*, i64** %4, align 8, !tbaa !23 - %6 = load i64, i64* %5, align 8, !tbaa !6 - %7 = and i64 %6, -129 - store i64 %7, i64* %5, align 8, !tbaa !6 - %8 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 0 - store i64* getelementptr inbounds ([7 x i64], [7 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %8, align 8, !dbg !24, !tbaa !15 - %rubyStr_hi = load i64, i64* getelementptr inbounds ([4 x i64], [4 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 3), align 8, !dbg !25, !invariant.load !5 - %9 = load i64, i64* @rb_mKernel, align 8, !dbg !26 - %10 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 1, !dbg !26 - %11 = load i64*, i64** %10, align 8, !dbg !26 - store i64 %9, i64* %11, align 8, !dbg !26, !tbaa !6 - %12 = getelementptr inbounds i64, i64* %11, i64 1, !dbg !26 - store i64 %rubyStr_hi, i64* %12, align 8, !dbg !26, !tbaa !6 - %13 = getelementptr inbounds i64, i64* %12, i64 1, !dbg !26 - store i64* %13, i64** %10, align 8, !dbg !26 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts, i64 0), !dbg !26 - ret i64 %send, !dbg !26 -} - -; Function Attrs: sspreq -define void @Init_block_no_args() local_unnamed_addr #4 { -entry: - %locals.i.i = alloca i64, i32 0, align 8 - %realpath = tail call i64 @sorbet_readRealpath() - tail call void @sorbet_vm_intern_ids(i64* noundef getelementptr inbounds ([4 x i64], [4 x i64]* @sorbet_moduleIDTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([4 x %struct.rb_code_position_struct], [4 x %struct.rb_code_position_struct]* @sorbet_moduleIDDescriptors, i32 0, i32 0), i32 noundef 4, i8* noundef getelementptr inbounds ([111 x i8], [111 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_vm_init_string_table(i64* noundef getelementptr inbounds ([4 x i64], [4 x i64]* @sorbet_moduleRubyStringTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([4 x %struct.rb_code_position_struct], [4 x %struct.rb_code_position_struct]* @sorbet_moduleRubyStringDescriptors, i32 0, i32 0), i32 noundef 4, i8* noundef getelementptr inbounds ([111 x i8], [111 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef getelementptr inbounds ([7 x i64], [7 x i64]* @iseqEncodedArray, i32 0, i32 0), i32 noundef 7) - %"rubyId_.i.i" = load i64, i64* getelementptr inbounds ([4 x i64], [4 x i64]* @sorbet_moduleIDTable, i64 0, i64 0), align 8, !invariant.load !5 - %"rubyStr_.i.i" = load i64, i64* getelementptr inbounds ([4 x i64], [4 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 0), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/block_no_args.rb.i.i" = load i64, i64* getelementptr inbounds ([4 x i64], [4 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_.i.i", i64 %"rubyId_.i.i", i64 %"rubyStr_test/testdata/compiler/block_no_args.rb.i.i", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 0, i32 noundef 4, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals.i.i, i32 noundef 0, i32 noundef 2) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - %"rubyId_block in .i.i" = load i64, i64* getelementptr inbounds ([4 x i64], [4 x i64]* @sorbet_moduleIDTable, i64 0, i64 1), align 8, !invariant.load !5 - %"rubyStr_block in .i.i" = load i64, i64* getelementptr inbounds ([4 x i64], [4 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 2), align 8, !invariant.load !5 - %1 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_block in .i.i", i64 %"rubyId_block in .i.i", i64 %"rubyStr_test/testdata/compiler/block_no_args.rb.i.i", i64 %realpath, %struct.rb_iseq_struct* %0, i32 noundef 2, i32 noundef 4, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef null, i32 noundef 0, i32 noundef 2) - store %struct.rb_iseq_struct* %1, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13$block_1", align 8 - %2 = call %struct.vm_ifunc* @rb_vm_ifunc_new(i64 (i64, i64, i32, i64*, i64)* noundef @"func_.13$block_1", i8* noundef null, i32 noundef 0, i32 noundef 0) #7 - %3 = ptrtoint %struct.vm_ifunc* %2 to i64 - %4 = call i64 @sorbet_globalConstRegister(i64 %3) - store i64 %4, i64* @"func_.13$block_1_ifunc", align 8 - %rubyId_puts.i = load i64, i64* getelementptr inbounds ([4 x i64], [4 x i64]* @sorbet_moduleIDTable, i64 0, i64 3), align 8, !dbg !26, !invariant.load !5 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts, i64 %rubyId_puts.i, i32 noundef 16, i32 noundef 1, i32 noundef 0), !dbg !26 - %5 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !15 - %6 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %5, i64 0, i32 2 - %7 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %6, align 8, !tbaa !17 - %stackFrame.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - %8 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %7, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame.i, %struct.rb_iseq_struct** %8, align 8, !tbaa !21 - %9 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %7, i64 0, i32 4 - %10 = load i64*, i64** %9, align 8, !tbaa !23 - %11 = load i64, i64* %10, align 8, !tbaa !6 - %12 = and i64 %11, -33 - store i64 %12, i64* %10, align 8, !tbaa !6 - call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %5, %struct.rb_control_frame_struct* %7, %struct.rb_iseq_struct* %stackFrame.i) #7 - %13 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %7, i64 0, i32 0 - store i64* getelementptr inbounds ([7 x i64], [7 x i64]* @iseqEncodedArray, i64 0, i64 4), i64** %13, align 8, !dbg !27, !tbaa !15 - %14 = load i64, i64* @"func_.13$block_1_ifunc", align 8, !dbg !28 - %15 = call %struct.vm_ifunc* @sorbet_globalConstFetchIfunc(i64 %14) #7, !dbg !28 - %16 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !28, !tbaa !15 - %17 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %16, i64 0, i32 2, !dbg !28 - %18 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %17, align 8, !dbg !28, !tbaa !17 - %19 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %18, i64 0, i32 3, !dbg !28 - %20 = bitcast i64* %19 to %struct.rb_captured_block*, !dbg !28 - %21 = getelementptr inbounds i64, i64* %19, i64 2, !dbg !28 - %22 = bitcast i64* %21 to %struct.vm_ifunc**, !dbg !28 - store %struct.vm_ifunc* %15, %struct.vm_ifunc** %22, align 8, !dbg !28, !tbaa !29 - call void @llvm.experimental.noalias.scope.decl(metadata !30) #7, !dbg !28 - %23 = ptrtoint %struct.rb_captured_block* %20 to i64, !dbg !28 - %24 = or i64 %23, 3, !dbg !28 - %25 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %16, i64 0, i32 17, !dbg !28 - %26 = and i64 %24, -4, !dbg !33 - %27 = inttoptr i64 %26 to %struct.rb_captured_block*, !dbg !33 - store i64 0, i64* %25, align 8, !dbg !33, !tbaa !35 - call void @sorbet_pushBlockFrame(%struct.rb_captured_block* %27) #7, !dbg !33 - %rubyStr_hi.i2.i.i = load i64, i64* getelementptr inbounds ([4 x i64], [4 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 3), align 8, !dbg !28, !invariant.load !5 - %28 = load i64, i64* @rb_mKernel, align 8, !dbg !28 - br label %29, !dbg !33 - -29: ; preds = %29, %entry - %30 = phi i64 [ 0, %entry ], [ %44, %29 ], !dbg !33 - %31 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !28, !tbaa !15 - %32 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %31, i64 0, i32 2, !dbg !28 - %33 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %32, align 8, !dbg !28, !tbaa !17 - %stackFrame.i1.i.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13$block_1", align 8, !dbg !28 - %34 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %33, i64 0, i32 2, !dbg !28 - store %struct.rb_iseq_struct* %stackFrame.i1.i.i, %struct.rb_iseq_struct** %34, align 8, !dbg !28, !tbaa !21 - %35 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %33, i64 0, i32 4, !dbg !28 - %36 = load i64*, i64** %35, align 8, !dbg !28, !tbaa !23 - %37 = load i64, i64* %36, align 8, !dbg !28, !tbaa !6 - %38 = and i64 %37, -129, !dbg !28 - store i64 %38, i64* %36, align 8, !dbg !28, !tbaa !6 - %39 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %33, i64 0, i32 0, !dbg !28 - store i64* getelementptr inbounds ([7 x i64], [7 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %39, align 8, !dbg !36, !tbaa !15 - %40 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %33, i64 0, i32 1, !dbg !38 - %41 = load i64*, i64** %40, align 8, !dbg !38 - store i64 %28, i64* %41, align 8, !dbg !38, !tbaa !6 - %42 = getelementptr inbounds i64, i64* %41, i64 1, !dbg !38 - store i64 %rubyStr_hi.i2.i.i, i64* %42, align 8, !dbg !38, !tbaa !6 - %43 = getelementptr inbounds i64, i64* %42, i64 1, !dbg !38 - store i64* %43, i64** %40, align 8, !dbg !38 - %send = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts, i64 0), !dbg !38 - %44 = add nuw nsw i64 %30, 1, !dbg !33 - %45 = icmp eq i64 %44, 10, !dbg !33 - br i1 %45, label %forward_sorbet_rb_int_dotimes_withBlock.exit.i, label %29, !dbg !33, !llvm.loop !39 - -forward_sorbet_rb_int_dotimes_withBlock.exit.i: ; preds = %29 - call void @sorbet_popFrame() #7, !dbg !33 - %46 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !28, !tbaa !15 - %47 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %46, i64 0, i32 5, !dbg !28 - %48 = load i32, i32* %47, align 8, !dbg !28, !tbaa !41 - %49 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %46, i64 0, i32 6, !dbg !28 - %50 = load i32, i32* %49, align 4, !dbg !28, !tbaa !42 - %51 = xor i32 %50, -1, !dbg !28 - %52 = and i32 %51, %48, !dbg !28 - %53 = icmp eq i32 %52, 0, !dbg !28 - br i1 %53, label %"func_.13.exit", label %54, !dbg !28, !prof !43 - -54: ; preds = %forward_sorbet_rb_int_dotimes_withBlock.exit.i - %55 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %46, i64 0, i32 8, !dbg !28 - %56 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %55, align 8, !dbg !28, !tbaa !44 - %57 = call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %56, i32 noundef 0) #7, !dbg !28 - br label %"func_.13.exit", !dbg !28 - -"func_.13.exit": ; preds = %forward_sorbet_rb_int_dotimes_withBlock.exit.i, %54 - store i64* getelementptr inbounds ([7 x i64], [7 x i64]* @iseqEncodedArray, i64 0, i64 4), i64** %13, align 8, !tbaa !15 - ret void -} - -; Function Attrs: inaccessiblememonly nofree nosync nounwind willreturn -declare void @llvm.experimental.noalias.scope.decl(metadata) #5 - -attributes #0 = { "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #1 = { noreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #2 = { nounwind ssp uwtable "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #3 = { ssp } -attributes #4 = { sspreq } -attributes #5 = { inaccessiblememonly nofree nosync nounwind willreturn } -attributes #6 = { noreturn nounwind } -attributes #7 = { nounwind } - -!llvm.module.flags = !{!0, !1, !2} -!llvm.dbg.cu = !{!3} - -!0 = !{i32 2, !"Debug Info Version", i32 3} -!1 = !{i32 4, !"cf-protection-return", i32 1} -!2 = !{i32 4, !"cf-protection-branch", i32 1} -!3 = distinct !DICompileUnit(language: DW_LANG_C, file: !4, producer: "Sorbet LLVM", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !5) -!4 = !DIFile(filename: "test/testdata/compiler/block_no_args.rb", directory: ".") -!5 = !{} -!6 = !{!7, !7, i64 0} -!7 = !{!"long", !8, i64 0} -!8 = !{!"omnipotent char", !9, i64 0} -!9 = !{!"Simple C/C++ TBAA"} -!10 = distinct !DISubprogram(name: ".", linkageName: "func_.13$block_1", scope: !11, file: !4, line: 4, type: !12, scopeLine: 4, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!11 = distinct !DISubprogram(name: ".", linkageName: "func_.13", scope: null, file: !4, line: 4, type: !12, scopeLine: 4, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!12 = !DISubroutineType(types: !13) -!13 = !{!14} -!14 = !DIBasicType(name: "VALUE", size: 64, encoding: DW_ATE_signed) -!15 = !{!16, !16, i64 0} -!16 = !{!"any pointer", !8, i64 0} -!17 = !{!18, !16, i64 16} -!18 = !{!"rb_execution_context_struct", !16, i64 0, !7, i64 8, !16, i64 16, !16, i64 24, !16, i64 32, !19, i64 40, !19, i64 44, !16, i64 48, !16, i64 56, !16, i64 64, !7, i64 72, !7, i64 80, !16, i64 88, !7, i64 96, !16, i64 104, !16, i64 112, !7, i64 120, !7, i64 128, !8, i64 136, !8, i64 137, !7, i64 144, !20, i64 152} -!19 = !{!"int", !8, i64 0} -!20 = !{!"", !16, i64 0, !16, i64 8, !7, i64 16, !8, i64 24} -!21 = !{!22, !16, i64 16} -!22 = !{!"rb_control_frame_struct", !16, i64 0, !16, i64 8, !16, i64 16, !7, i64 24, !16, i64 32, !16, i64 40, !16, i64 48} -!23 = !{!22, !16, i64 32} -!24 = !DILocation(line: 4, column: 1, scope: !10) -!25 = !DILocation(line: 5, column: 15, scope: !10) -!26 = !DILocation(line: 5, column: 3, scope: !10) -!27 = !DILocation(line: 0, scope: !11) -!28 = !DILocation(line: 4, column: 1, scope: !11) -!29 = !{!8, !8, i64 0} -!30 = !{!31} -!31 = distinct !{!31, !32, !"VM_BH_FROM_IFUNC_BLOCK: argument 0"} -!32 = distinct !{!32, !"VM_BH_FROM_IFUNC_BLOCK"} -!33 = !DILocation(line: 4, column: 1, scope: !11, inlinedAt: !34) -!34 = distinct !DILocation(line: 4, column: 1, scope: !11) -!35 = !{!18, !7, i64 128} -!36 = !DILocation(line: 4, column: 1, scope: !10, inlinedAt: !37) -!37 = distinct !DILocation(line: 4, column: 1, scope: !11, inlinedAt: !34) -!38 = !DILocation(line: 5, column: 3, scope: !10, inlinedAt: !37) -!39 = distinct !{!39, !40} -!40 = !{!"llvm.loop.unroll.disable"} -!41 = !{!18, !19, i64 40} -!42 = !{!18, !19, i64 44} -!43 = !{!"branch_weights", i32 2000, i32 1} -!44 = !{!18, !16, i64 56} diff --git a/test/testdata/compiler/block_no_args_capture.opt.ll.exp b/test/testdata/compiler/block_no_args_capture.opt.ll.exp deleted file mode 100644 index c0ec191eb1..0000000000 --- a/test/testdata/compiler/block_no_args_capture.opt.ll.exp +++ /dev/null @@ -1,406 +0,0 @@ -; ModuleID = 'payload' -source_filename = "llvm-link" -target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } -%struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } -%struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } -%struct.anon.0 = type { i16, [2 x i8] } -%struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } -%struct.rb_iseq_location_struct = type { i64, i64, i64, i64, i32, %struct.rb_code_location_struct } -%struct.rb_code_location_struct = type { %struct.rb_code_position_struct, %struct.rb_code_position_struct } -%struct.rb_code_position_struct = type { i32, i32 } -%struct.iseq_insn_info = type { %struct.iseq_insn_info_entry*, i32*, i32, %struct.succ_index_table* } -%struct.iseq_insn_info_entry = type opaque -%struct.succ_index_table = type opaque -%struct.iseq_catch_table = type opaque -%union.iseq_inline_storage_entry = type { %struct.iseq_inline_cache_entry } -%struct.iseq_inline_cache_entry = type { i64, %struct.rb_cref_struct*, i64 } -%struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } -%struct.rb_scope_visi_struct = type { i8, [3 x i8] } -%struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } -%struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } -%union.anon.13 = type { %struct.rb_method_cfunc_struct } -%struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%struct.rb_calling_info = type { i64, i64, i32, i32 } -%union.anon.15 = type { i32 } -%struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.16 = type { i64, i64, i64, i64* } -%union.anon.17 = type { %struct.anon.18 } -%struct.anon.18 = type { i64, i32 } -%struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } -%struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } -%struct.rb_fiber_struct = type opaque -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } -%struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } -%struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.__pthread_cond_s } -%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } -%union.anon = type { i64 } -%struct.anon.5 = type { [65 x i64] } -%struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } -%struct.rb_event_hook_struct = type opaque -%struct.rb_postponed_job_struct = type opaque -%struct.list_head = type { %struct.list_node } -%struct.rb_objspace = type opaque -%struct.rb_at_exit_list = type { void (%struct.rb_vm_struct*)*, %struct.rb_at_exit_list* } -%struct.rb_builtin_function = type opaque -%struct.anon.6 = type { i64, i64, i64, i64 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } -%union.anon.9 = type { %union.pthread_cond_t } -%union.pthread_mutex_t = type { %struct.__pthread_mutex_s } -%struct.__pthread_mutex_s = type { i32, i32, i32, i32, i32, i16, i16, %struct.__pthread_internal_list } -%struct.__pthread_internal_list = type { %struct.__pthread_internal_list*, %struct.__pthread_internal_list* } -%struct.rb_unblock_callback = type { void (i8*)*, i8* } -%struct.rb_mutex_struct = type opaque -%struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.10 = type { %struct.anon.11 } -%struct.anon.11 = type { i64, i64, i32 } -%struct.st_table = type { i8, i8, i8, i32, %struct.st_hash_type*, i64, i64*, i64, i64, %struct.st_table_entry* } -%struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } -%struct.st_table_entry = type opaque -%struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } -%struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } -%struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } -%struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } -%struct.__sigset_t = type { [16 x i64] } -%struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } -%struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } -%struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } -%struct.rb_call_info_with_kwarg = type { %struct.rb_call_info, %struct.rb_call_info_kw_arg* } -%struct.rb_call_info_kw_arg = type { i32, [1 x i64] } -%struct.rb_captured_block = type { i64, i64*, %union.anon.20 } -%union.anon.20 = type { %struct.rb_iseq_struct* } -%struct.vm_ifunc = type { i64, i64, i64 (i64, i64, i32, i64*, i64)*, i8*, %struct.rb_code_position_struct } -%struct.sorbet_inlineIntrinsicEnv = type { i64, i64, i32, i64*, i64 } - -@ruby_current_execution_context_ptr = external local_unnamed_addr global %struct.rb_execution_context_struct*, align 8 -@rb_eRuntimeError = external local_unnamed_addr global i64, align 8 -@.str.9 = private unnamed_addr constant [95 x i8] c"sorbet_getBuildSCMRevision: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@.str.10 = private unnamed_addr constant [93 x i8] c"sorbet_getIsReleaseBuild: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@"stackFramePrecomputed_func_.13" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@iseqEncodedArray = internal global [8 x i64] zeroinitializer -@fileLineNumberInfo = internal global %struct.SorbetLineNumberInfo zeroinitializer -@"stackFramePrecomputed_func_.13$block_1" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"func_.13$block_1_ifunc" = internal unnamed_addr global i64 0 -@ic_puts = internal global %struct.FunctionInlineCache zeroinitializer -@sorbet_moduleStringTable = internal unnamed_addr constant [121 x i8] c"\00test/testdata/compiler/block_no_args_capture.rb\00s\00block in \00hi\00times\00puts\00Kernel\00master\00", align 1 -@sorbet_moduleIDTable = internal unnamed_addr global [5 x i64] zeroinitializer, align 8 -@sorbet_moduleIDDescriptors = internal unnamed_addr constant [5 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 16 }, %struct.rb_code_position_struct { i32 65, i32 1 }, %struct.rb_code_position_struct { i32 67, i32 25 }, %struct.rb_code_position_struct { i32 96, i32 5 }, %struct.rb_code_position_struct { i32 102, i32 4 }], align 8 -@sorbet_moduleRubyStringTable = internal unnamed_addr global [4 x i64] zeroinitializer, align 8 -@sorbet_moduleRubyStringDescriptors = internal unnamed_addr constant [4 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 16 }, %struct.rb_code_position_struct { i32 17, i32 47 }, %struct.rb_code_position_struct { i32 67, i32 25 }, %struct.rb_code_position_struct { i32 93, i32 2 }], align 8 -@rb_mKernel = external local_unnamed_addr constant i64 - -declare %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64, i64, i64, i64, %struct.rb_iseq_struct*, i32, i32, %struct.SorbetLineNumberInfo*, i64*, i32, i32) local_unnamed_addr #0 - -declare void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo*, i64*, i32) local_unnamed_addr #0 - -declare i64 @sorbet_readRealpath() local_unnamed_addr #0 - -declare void @sorbet_pushBlockFrame(%struct.rb_captured_block*) local_unnamed_addr #0 - -declare void @sorbet_popFrame() local_unnamed_addr #0 - -declare void @sorbet_vm_env_write_slowpath(i64*, i32, i64) local_unnamed_addr #0 - -declare void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) local_unnamed_addr #0 - -declare i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache*, i64) local_unnamed_addr #0 - -declare void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_iseq_struct*) local_unnamed_addr #0 - -declare i64 @sorbet_globalConstRegister(i64) local_unnamed_addr #0 - -declare %struct.vm_ifunc* @sorbet_globalConstFetchIfunc(i64) local_unnamed_addr #0 - -declare void @sorbet_vm_intern_ids(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #0 - -declare void @sorbet_vm_init_string_table(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #0 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1 - -; Function Attrs: noreturn -declare void @rb_raise(i64, i8*, ...) local_unnamed_addr #2 - -declare i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct*, i32) local_unnamed_addr #0 - -declare %struct.vm_ifunc* @rb_vm_ifunc_new(i64 (i64, i64, i32, i64*, i64)*, i8*, i32, i32) local_unnamed_addr #0 - -; Function Attrs: nounwind ssp uwtable -define weak i32 @sorbet_getIsReleaseBuild() local_unnamed_addr #3 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([93 x i8], [93 x i8]* @.str.10, i64 0, i64 0)) #8 - unreachable -} - -; Function Attrs: nounwind ssp uwtable -define weak i8* @sorbet_getBuildSCMRevision() local_unnamed_addr #3 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([95 x i8], [95 x i8]* @.str.9, i64 0, i64 0)) #8 - unreachable -} - -; Function Attrs: ssp -define internal i64 @"func_.13$block_1"(i64 %firstYieldArgRaw, i64 %localsOffset, i32 %argc, i64* nocapture nofree readnone %argArray, i64 %blockArg) #4 !dbg !10 { -vm_get_ep.exit: - %0 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !15 - %1 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %0, i64 0, i32 2 - %2 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %1, align 8, !tbaa !17 - %stackFrame = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13$block_1", align 8 - %3 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame, %struct.rb_iseq_struct** %3, align 8, !tbaa !21 - %4 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 4 - %5 = load i64*, i64** %4, align 8 - %6 = load i64, i64* %5, align 8, !tbaa !6 - %7 = and i64 %6, -129 - store i64 %7, i64* %5, align 8, !tbaa !6 - %8 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 0 - store i64* getelementptr inbounds ([8 x i64], [8 x i64]* @iseqEncodedArray, i64 0, i64 6), i64** %8, align 8, !dbg !23, !tbaa !15 - %9 = getelementptr inbounds i64, i64* %5, i64 -1, !dbg !24 - %10 = load i64, i64* %9, align 8, !dbg !24, !tbaa !6 - %11 = and i64 %10, -4, !dbg !24 - %12 = inttoptr i64 %11 to i64*, !dbg !24 - %13 = getelementptr inbounds i64, i64* %12, i64 -3, !dbg !24 - %14 = load i64, i64* %13, align 8, !dbg !24, !tbaa !6 - %15 = load i64, i64* @rb_mKernel, align 8, !dbg !24 - %16 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 1, !dbg !24 - %17 = load i64*, i64** %16, align 8, !dbg !24 - store i64 %15, i64* %17, align 8, !dbg !24, !tbaa !6 - %18 = getelementptr inbounds i64, i64* %17, i64 1, !dbg !24 - store i64 %14, i64* %18, align 8, !dbg !24, !tbaa !6 - %19 = getelementptr inbounds i64, i64* %18, i64 1, !dbg !24 - store i64* %19, i64** %16, align 8, !dbg !24 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts, i64 0), !dbg !24 - ret i64 %send, !dbg !24 -} - -; Function Attrs: sspreq -define void @Init_block_no_args_capture() local_unnamed_addr #5 { -entry: - %0 = alloca %struct.sorbet_inlineIntrinsicEnv, align 8 - %locals.i.i = alloca i64, align 8 - %realpath = tail call i64 @sorbet_readRealpath() - tail call void @sorbet_vm_intern_ids(i64* noundef getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleIDTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([5 x %struct.rb_code_position_struct], [5 x %struct.rb_code_position_struct]* @sorbet_moduleIDDescriptors, i32 0, i32 0), i32 noundef 5, i8* noundef getelementptr inbounds ([121 x i8], [121 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_vm_init_string_table(i64* noundef getelementptr inbounds ([4 x i64], [4 x i64]* @sorbet_moduleRubyStringTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([4 x %struct.rb_code_position_struct], [4 x %struct.rb_code_position_struct]* @sorbet_moduleRubyStringDescriptors, i32 0, i32 0), i32 noundef 4, i8* noundef getelementptr inbounds ([121 x i8], [121 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef getelementptr inbounds ([8 x i64], [8 x i64]* @iseqEncodedArray, i32 0, i32 0), i32 noundef 8) - %1 = bitcast i64* %locals.i.i to i8* - call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %1) - %"rubyId_.i.i" = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleIDTable, i64 0, i64 0), align 8, !invariant.load !5 - %"rubyStr_.i.i" = load i64, i64* getelementptr inbounds ([4 x i64], [4 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 0), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/block_no_args_capture.rb.i.i" = load i64, i64* getelementptr inbounds ([4 x i64], [4 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %rubyId_s.i.i = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleIDTable, i64 0, i64 1), align 8, !invariant.load !5 - store i64 %rubyId_s.i.i, i64* %locals.i.i, align 8 - %2 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_.i.i", i64 %"rubyId_.i.i", i64 %"rubyStr_test/testdata/compiler/block_no_args_capture.rb.i.i", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 0, i32 noundef 4, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull align 8 %locals.i.i, i32 noundef 1, i32 noundef 2) - store %struct.rb_iseq_struct* %2, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %1) - %"rubyId_block in .i.i" = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleIDTable, i64 0, i64 2), align 8, !invariant.load !5 - %"rubyStr_block in .i.i" = load i64, i64* getelementptr inbounds ([4 x i64], [4 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 2), align 8, !invariant.load !5 - %3 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_block in .i.i", i64 %"rubyId_block in .i.i", i64 %"rubyStr_test/testdata/compiler/block_no_args_capture.rb.i.i", i64 %realpath, %struct.rb_iseq_struct* %2, i32 noundef 2, i32 noundef 4, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef null, i32 noundef 0, i32 noundef 2) - store %struct.rb_iseq_struct* %3, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13$block_1", align 8 - %4 = call %struct.vm_ifunc* @rb_vm_ifunc_new(i64 (i64, i64, i32, i64*, i64)* noundef @"func_.13$block_1", i8* noundef null, i32 noundef 0, i32 noundef 0) #9 - %5 = ptrtoint %struct.vm_ifunc* %4 to i64 - %6 = call i64 @sorbet_globalConstRegister(i64 %5) - store i64 %6, i64* @"func_.13$block_1_ifunc", align 8 - %rubyId_puts.i = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleIDTable, i64 0, i64 4), align 8, !dbg !24, !invariant.load !5 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts, i64 %rubyId_puts.i, i32 noundef 16, i32 noundef 1, i32 noundef 0), !dbg !24 - %7 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !15 - %8 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %7, i64 0, i32 2 - %9 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %8, align 8, !tbaa !17 - %stackFrame.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - %10 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %9, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame.i, %struct.rb_iseq_struct** %10, align 8, !tbaa !21 - %11 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %9, i64 0, i32 4 - %12 = load i64*, i64** %11, align 8, !tbaa !25 - %13 = load i64, i64* %12, align 8, !tbaa !6 - %14 = and i64 %13, -33 - store i64 %14, i64* %12, align 8, !tbaa !6 - call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %7, %struct.rb_control_frame_struct* %9, %struct.rb_iseq_struct* %stackFrame.i) #9 - %15 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %9, i64 0, i32 0 - store i64* getelementptr inbounds ([8 x i64], [8 x i64]* @iseqEncodedArray, i64 0, i64 4), i64** %15, align 8, !dbg !26, !tbaa !15 - %rubyStr_hi.i = load i64, i64* getelementptr inbounds ([4 x i64], [4 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 3), align 8, !dbg !27, !invariant.load !5 - %16 = load i64*, i64** %11, align 8, !dbg !27, !tbaa !25 - %17 = load i64, i64* %16, align 8, !dbg !27, !tbaa !6 - %18 = and i64 %17, 8, !dbg !27 - %19 = icmp eq i64 %18, 0, !dbg !27 - br i1 %19, label %20, label %22, !dbg !27, !prof !28 - -20: ; preds = %entry - %21 = getelementptr inbounds i64, i64* %16, i64 -3, !dbg !27 - store i64 %rubyStr_hi.i, i64* %21, align 8, !dbg !27, !tbaa !6 - br label %23, !dbg !27 - -22: ; preds = %entry - call void @sorbet_vm_env_write_slowpath(i64* nonnull align 8 dereferenceable(8) %16, i32 noundef -3, i64 %rubyStr_hi.i) #9, !dbg !27 - br label %23, !dbg !27 - -23: ; preds = %22, %20 - store i64* getelementptr inbounds ([8 x i64], [8 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %15, align 8, !dbg !27, !tbaa !15 - %rubyId_times.i = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleIDTable, i64 0, i64 3), align 8, !dbg !29, !invariant.load !5 - %24 = load i64, i64* @"func_.13$block_1_ifunc", align 8, !dbg !29 - %25 = call %struct.vm_ifunc* @sorbet_globalConstFetchIfunc(i64 %24) #9, !dbg !29 - %26 = bitcast %struct.sorbet_inlineIntrinsicEnv* %0 to i8*, !dbg !29 - call void @llvm.lifetime.start.p0i8(i64 noundef 40, i8* noundef nonnull %26) #9, !dbg !29 - %27 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %0, i64 0, i32 0, !dbg !29 - store i64 21, i64* %27, align 8, !dbg !29, !tbaa !30 - %28 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %0, i64 0, i32 1, !dbg !29 - store i64 %rubyId_times.i, i64* %28, align 8, !dbg !29, !tbaa !32 - %29 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %0, i64 0, i32 2, !dbg !29 - store i32 0, i32* %29, align 8, !dbg !29, !tbaa !33 - %30 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %0, i64 0, i32 3, !dbg !29 - %31 = bitcast i64** %30 to i8*, !dbg !29 - call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %31, i8 0, i64 16, i1 false) #9, !dbg !29 - %32 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !29, !tbaa !15 - %33 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %32, i64 0, i32 2, !dbg !29 - %34 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %33, align 8, !dbg !29, !tbaa !17 - %35 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %34, i64 0, i32 3, !dbg !29 - %36 = bitcast i64* %35 to %struct.rb_captured_block*, !dbg !29 - %37 = getelementptr inbounds i64, i64* %35, i64 2, !dbg !29 - %38 = bitcast i64* %37 to %struct.vm_ifunc**, !dbg !29 - store %struct.vm_ifunc* %25, %struct.vm_ifunc** %38, align 8, !dbg !29, !tbaa !34 - call void @llvm.experimental.noalias.scope.decl(metadata !35) #9, !dbg !29 - %39 = ptrtoint %struct.rb_captured_block* %36 to i64, !dbg !29 - %40 = or i64 %39, 3, !dbg !29 - %41 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %32, i64 0, i32 17, !dbg !29 - %42 = and i64 %40, -4, !dbg !38 - %43 = inttoptr i64 %42 to %struct.rb_captured_block*, !dbg !38 - store i64 0, i64* %41, align 8, !dbg !38, !tbaa !40 - call void @sorbet_pushBlockFrame(%struct.rb_captured_block* %43) #9, !dbg !38 - %44 = load i64, i64* @rb_mKernel, align 8, !dbg !29 - br label %45, !dbg !38 - -45: ; preds = %45, %23 - %46 = phi i64 [ 0, %23 ], [ %66, %45 ], !dbg !38 - %47 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !29, !tbaa !15 - %48 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %47, i64 0, i32 2, !dbg !29 - %49 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %48, align 8, !dbg !29, !tbaa !17 - %stackFrame.i1.i.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13$block_1", align 8, !dbg !29 - %50 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %49, i64 0, i32 2, !dbg !29 - store %struct.rb_iseq_struct* %stackFrame.i1.i.i, %struct.rb_iseq_struct** %50, align 8, !dbg !29, !tbaa !21 - %51 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %49, i64 0, i32 4, !dbg !29 - %52 = load i64*, i64** %51, align 8, !dbg !29 - %53 = load i64, i64* %52, align 8, !dbg !29, !tbaa !6 - %54 = and i64 %53, -129, !dbg !29 - store i64 %54, i64* %52, align 8, !dbg !29, !tbaa !6 - %55 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %49, i64 0, i32 0, !dbg !29 - store i64* getelementptr inbounds ([8 x i64], [8 x i64]* @iseqEncodedArray, i64 0, i64 6), i64** %55, align 8, !dbg !41, !tbaa !15 - %56 = getelementptr inbounds i64, i64* %52, i64 -1, !dbg !43 - %57 = load i64, i64* %56, align 8, !dbg !43, !tbaa !6 - %58 = and i64 %57, -4, !dbg !43 - %59 = inttoptr i64 %58 to i64*, !dbg !43 - %60 = getelementptr inbounds i64, i64* %59, i64 -3, !dbg !43 - %61 = load i64, i64* %60, align 8, !dbg !43, !tbaa !6 - %62 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %49, i64 0, i32 1, !dbg !43 - %63 = load i64*, i64** %62, align 8, !dbg !43 - store i64 %44, i64* %63, align 8, !dbg !43, !tbaa !6 - %64 = getelementptr inbounds i64, i64* %63, i64 1, !dbg !43 - store i64 %61, i64* %64, align 8, !dbg !43, !tbaa !6 - %65 = getelementptr inbounds i64, i64* %64, i64 1, !dbg !43 - store i64* %65, i64** %62, align 8, !dbg !43 - %send = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts, i64 0), !dbg !43 - %66 = add nuw nsw i64 %46, 1, !dbg !38 - %67 = icmp eq i64 %66, 10, !dbg !38 - br i1 %67, label %forward_sorbet_rb_int_dotimes_withBlock.exit.i, label %45, !dbg !38, !llvm.loop !44 - -forward_sorbet_rb_int_dotimes_withBlock.exit.i: ; preds = %45 - call void @sorbet_popFrame() #9, !dbg !38 - call void @llvm.lifetime.end.p0i8(i64 noundef 40, i8* noundef nonnull %26) #9, !dbg !29 - %68 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !29, !tbaa !15 - %69 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %68, i64 0, i32 5, !dbg !29 - %70 = load i32, i32* %69, align 8, !dbg !29, !tbaa !46 - %71 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %68, i64 0, i32 6, !dbg !29 - %72 = load i32, i32* %71, align 4, !dbg !29, !tbaa !47 - %73 = xor i32 %72, -1, !dbg !29 - %74 = and i32 %73, %70, !dbg !29 - %75 = icmp eq i32 %74, 0, !dbg !29 - br i1 %75, label %"func_.13.exit", label %76, !dbg !29, !prof !28 - -76: ; preds = %forward_sorbet_rb_int_dotimes_withBlock.exit.i - %77 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %68, i64 0, i32 8, !dbg !29 - %78 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %77, align 8, !dbg !29, !tbaa !48 - %79 = call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %78, i32 noundef 0) #9, !dbg !29 - br label %"func_.13.exit", !dbg !29 - -"func_.13.exit": ; preds = %forward_sorbet_rb_int_dotimes_withBlock.exit.i, %76 - store i64* getelementptr inbounds ([8 x i64], [8 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %15, align 8, !tbaa !15 - ret void -} - -; Function Attrs: inaccessiblememonly nofree nosync nounwind willreturn -declare void @llvm.experimental.noalias.scope.decl(metadata) #6 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly -declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #7 - -attributes #0 = { "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #1 = { argmemonly nofree nosync nounwind willreturn } -attributes #2 = { noreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #3 = { nounwind ssp uwtable "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #4 = { ssp } -attributes #5 = { sspreq } -attributes #6 = { inaccessiblememonly nofree nosync nounwind willreturn } -attributes #7 = { argmemonly nofree nosync nounwind willreturn writeonly } -attributes #8 = { noreturn nounwind } -attributes #9 = { nounwind } - -!llvm.module.flags = !{!0, !1, !2} -!llvm.dbg.cu = !{!3} - -!0 = !{i32 2, !"Debug Info Version", i32 3} -!1 = !{i32 4, !"cf-protection-return", i32 1} -!2 = !{i32 4, !"cf-protection-branch", i32 1} -!3 = distinct !DICompileUnit(language: DW_LANG_C, file: !4, producer: "Sorbet LLVM", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !5) -!4 = !DIFile(filename: "test/testdata/compiler/block_no_args_capture.rb", directory: ".") -!5 = !{} -!6 = !{!7, !7, i64 0} -!7 = !{!"long", !8, i64 0} -!8 = !{!"omnipotent char", !9, i64 0} -!9 = !{!"Simple C/C++ TBAA"} -!10 = distinct !DISubprogram(name: ".", linkageName: "func_.13$block_1", scope: !11, file: !4, line: 4, type: !12, scopeLine: 4, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!11 = distinct !DISubprogram(name: ".", linkageName: "func_.13", scope: null, file: !4, line: 4, type: !12, scopeLine: 4, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!12 = !DISubroutineType(types: !13) -!13 = !{!14} -!14 = !DIBasicType(name: "VALUE", size: 64, encoding: DW_ATE_signed) -!15 = !{!16, !16, i64 0} -!16 = !{!"any pointer", !8, i64 0} -!17 = !{!18, !16, i64 16} -!18 = !{!"rb_execution_context_struct", !16, i64 0, !7, i64 8, !16, i64 16, !16, i64 24, !16, i64 32, !19, i64 40, !19, i64 44, !16, i64 48, !16, i64 56, !16, i64 64, !7, i64 72, !7, i64 80, !16, i64 88, !7, i64 96, !16, i64 104, !16, i64 112, !7, i64 120, !7, i64 128, !8, i64 136, !8, i64 137, !7, i64 144, !20, i64 152} -!19 = !{!"int", !8, i64 0} -!20 = !{!"", !16, i64 0, !16, i64 8, !7, i64 16, !8, i64 24} -!21 = !{!22, !16, i64 16} -!22 = !{!"rb_control_frame_struct", !16, i64 0, !16, i64 8, !16, i64 16, !7, i64 24, !16, i64 32, !16, i64 40, !16, i64 48} -!23 = !DILocation(line: 5, column: 1, scope: !10) -!24 = !DILocation(line: 6, column: 3, scope: !10) -!25 = !{!22, !16, i64 32} -!26 = !DILocation(line: 0, scope: !11) -!27 = !DILocation(line: 4, column: 5, scope: !11) -!28 = !{!"branch_weights", i32 2000, i32 1} -!29 = !DILocation(line: 5, column: 1, scope: !11) -!30 = !{!31, !7, i64 0} -!31 = !{!"sorbet_inlineIntrinsicEnv", !7, i64 0, !7, i64 8, !19, i64 16, !16, i64 24, !7, i64 32} -!32 = !{!31, !7, i64 8} -!33 = !{!31, !19, i64 16} -!34 = !{!8, !8, i64 0} -!35 = !{!36} -!36 = distinct !{!36, !37, !"VM_BH_FROM_IFUNC_BLOCK: argument 0"} -!37 = distinct !{!37, !"VM_BH_FROM_IFUNC_BLOCK"} -!38 = !DILocation(line: 5, column: 1, scope: !11, inlinedAt: !39) -!39 = distinct !DILocation(line: 5, column: 1, scope: !11) -!40 = !{!18, !7, i64 128} -!41 = !DILocation(line: 5, column: 1, scope: !10, inlinedAt: !42) -!42 = distinct !DILocation(line: 5, column: 1, scope: !11, inlinedAt: !39) -!43 = !DILocation(line: 6, column: 3, scope: !10, inlinedAt: !42) -!44 = distinct !{!44, !45} -!45 = !{!"llvm.loop.unroll.disable"} -!46 = !{!18, !19, i64 40} -!47 = !{!18, !19, i64 44} -!48 = !{!18, !16, i64 56} diff --git a/test/testdata/compiler/block_no_args_captures_constant.opt.ll.exp b/test/testdata/compiler/block_no_args_captures_constant.opt.ll.exp deleted file mode 100644 index 1daf688dbe..0000000000 --- a/test/testdata/compiler/block_no_args_captures_constant.opt.ll.exp +++ /dev/null @@ -1,484 +0,0 @@ -; ModuleID = 'payload' -source_filename = "llvm-link" -target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } -%struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.__pthread_cond_s } -%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } -%union.anon = type { i64 } -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } -%struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } -%struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } -%struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } -%struct.anon.0 = type { i16, [2 x i8] } -%struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } -%struct.rb_iseq_location_struct = type { i64, i64, i64, i64, i32, %struct.rb_code_location_struct } -%struct.rb_code_location_struct = type { %struct.rb_code_position_struct, %struct.rb_code_position_struct } -%struct.rb_code_position_struct = type { i32, i32 } -%struct.iseq_insn_info = type { %struct.iseq_insn_info_entry*, i32*, i32, %struct.succ_index_table* } -%struct.iseq_insn_info_entry = type opaque -%struct.succ_index_table = type opaque -%struct.iseq_catch_table = type opaque -%union.iseq_inline_storage_entry = type { %struct.iseq_inline_cache_entry } -%struct.iseq_inline_cache_entry = type { i64, %struct.rb_cref_struct*, i64 } -%struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } -%struct.rb_scope_visi_struct = type { i8, [3 x i8] } -%struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } -%struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } -%union.anon.13 = type { %struct.rb_method_cfunc_struct } -%struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%union.anon.15 = type { i32 } -%struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.16 = type { i64, i64, i64, i64* } -%union.anon.17 = type { %struct.anon.18 } -%struct.anon.18 = type { i64, i32 } -%struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } -%struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } -%struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } -%struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } -%struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } -%struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } -%struct.__sigset_t = type { [16 x i64] } -%struct.rb_calling_info = type { i64, i64, i32, i32 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } -%union.anon.9 = type { %union.pthread_cond_t } -%struct.rb_unblock_callback = type { void (i8*)*, i8* } -%struct.rb_mutex_struct = type opaque -%struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.10 = type { %struct.anon.11 } -%struct.anon.11 = type { i64, i64, i32 } -%struct.rb_fiber_struct = type opaque -%struct.anon.5 = type { [65 x i64] } -%struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } -%struct.rb_event_hook_struct = type opaque -%struct.rb_postponed_job_struct = type opaque -%struct.list_head = type { %struct.list_node } -%union.pthread_mutex_t = type { %struct.__pthread_mutex_s } -%struct.__pthread_mutex_s = type { i32, i32, i32, i32, i32, i16, i16, %struct.__pthread_internal_list } -%struct.__pthread_internal_list = type { %struct.__pthread_internal_list*, %struct.__pthread_internal_list* } -%struct.rb_objspace = type opaque -%struct.rb_at_exit_list = type { void (%struct.rb_vm_struct*)*, %struct.rb_at_exit_list* } -%struct.st_table = type { i8, i8, i8, i32, %struct.st_hash_type*, i64, i64*, i64, i64, %struct.st_table_entry* } -%struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } -%struct.st_table_entry = type opaque -%struct.rb_builtin_function = type opaque -%struct.anon.6 = type { i64, i64, i64, i64 } -%struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } -%struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } -%struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } -%struct.rb_call_info_with_kwarg = type { %struct.rb_call_info, %struct.rb_call_info_kw_arg* } -%struct.rb_call_info_kw_arg = type { i32, [1 x i64] } -%struct.rb_captured_block = type { i64, i64*, %union.anon.20 } -%union.anon.20 = type { %struct.rb_iseq_struct* } -%struct.vm_ifunc = type { i64, i64, i64 (i64, i64, i32, i64*, i64)*, i8*, %struct.rb_code_position_struct } - -@ruby_current_vm_ptr = external local_unnamed_addr global %struct.rb_vm_struct*, align 8 -@ruby_current_execution_context_ptr = external local_unnamed_addr global %struct.rb_execution_context_struct*, align 8 -@ruby_vm_global_constant_state = external local_unnamed_addr global i64, align 8 -@rb_eRuntimeError = external local_unnamed_addr global i64, align 8 -@.str.9 = private unnamed_addr constant [95 x i8] c"sorbet_getBuildSCMRevision: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@.str.10 = private unnamed_addr constant [93 x i8] c"sorbet_getIsReleaseBuild: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@"stackFramePrecomputed_func_Object#3foo" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@iseqEncodedArray = internal global [13 x i64] zeroinitializer -@fileLineNumberInfo = internal global %struct.SorbetLineNumberInfo zeroinitializer -@"stackFramePrecomputed_func_Object#3foo$block_1" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@ic_puts = internal global %struct.FunctionInlineCache zeroinitializer -@"func_Object#3foo$block_1_ifunc" = internal unnamed_addr global i64 0 -@ic_puts.1 = internal global %struct.FunctionInlineCache zeroinitializer -@"stackFramePrecomputed_func_.13" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@ic_foo = internal global %struct.FunctionInlineCache zeroinitializer -@sorbet_moduleStringTable = internal unnamed_addr constant [136 x i8] c"foo\00test/testdata/compiler/block_no_args_captures_constant.rb\00block in foo\00A\00puts\00Kernel\00times\00\00hi\00Object\00normal\00master\00", align 1 -@sorbet_moduleIDTable = internal unnamed_addr global [6 x i64] zeroinitializer, align 8 -@sorbet_moduleIDDescriptors = internal unnamed_addr constant [6 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 3 }, %struct.rb_code_position_struct { i32 62, i32 12 }, %struct.rb_code_position_struct { i32 77, i32 4 }, %struct.rb_code_position_struct { i32 89, i32 5 }, %struct.rb_code_position_struct { i32 95, i32 16 }, %struct.rb_code_position_struct { i32 122, i32 6 }], align 8 -@sorbet_moduleRubyStringTable = internal unnamed_addr global [5 x i64] zeroinitializer, align 8 -@sorbet_moduleRubyStringDescriptors = internal unnamed_addr constant [5 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 3 }, %struct.rb_code_position_struct { i32 4, i32 57 }, %struct.rb_code_position_struct { i32 62, i32 12 }, %struct.rb_code_position_struct { i32 95, i32 16 }, %struct.rb_code_position_struct { i32 112, i32 2 }], align 8 -@guard_epoch_A = linkonce local_unnamed_addr global i64 0 -@guarded_const_A = linkonce local_unnamed_addr global i64 0 -@rb_mKernel = external local_unnamed_addr constant i64 -@rb_cObject = external local_unnamed_addr constant i64 - -; Function Attrs: noreturn -declare void @sorbet_raiseArity(i32, i32, i32) local_unnamed_addr #0 - -declare %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64, i64, i64, i64, %struct.rb_iseq_struct*, i32, i32, %struct.SorbetLineNumberInfo*, i64*, i32, i32) local_unnamed_addr #1 - -declare void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo*, i64*, i32) local_unnamed_addr #1 - -declare i64 @sorbet_getConstant(i8*, i64) local_unnamed_addr #1 - -declare i64 @sorbet_setConstant(i64, i8*, i64, i64) local_unnamed_addr #1 - -declare i64 @sorbet_readRealpath() local_unnamed_addr #1 - -declare void @sorbet_pushBlockFrame(%struct.rb_captured_block*) local_unnamed_addr #1 - -declare void @sorbet_popFrame() local_unnamed_addr #1 - -declare void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) local_unnamed_addr #1 - -declare i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache*, i64) local_unnamed_addr #1 - -declare void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_iseq_struct*) local_unnamed_addr #1 - -declare i64 @sorbet_globalConstRegister(i64) local_unnamed_addr #1 - -declare %struct.vm_ifunc* @sorbet_globalConstFetchIfunc(i64) local_unnamed_addr #1 - -declare void @sorbet_vm_define_method(i64, i8*, i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)*, i8*, %struct.rb_iseq_struct*, i1 zeroext) local_unnamed_addr #1 - -declare void @sorbet_vm_intern_ids(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #1 - -declare void @sorbet_vm_init_string_table(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #1 - -; Function Attrs: noreturn -declare void @rb_raise(i64, i8*, ...) local_unnamed_addr #0 - -declare i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct*, i32) local_unnamed_addr #1 - -declare %struct.vm_ifunc* @rb_vm_ifunc_new(i64 (i64, i64, i32, i64*, i64)*, i8*, i32, i32) local_unnamed_addr #1 - -; Function Attrs: allocsize(0,1) -declare noalias nonnull i8* @ruby_xcalloc(i64, i64) local_unnamed_addr #2 - -; Function Attrs: nounwind ssp uwtable -define weak i32 @sorbet_getIsReleaseBuild() local_unnamed_addr #3 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([93 x i8], [93 x i8]* @.str.10, i64 0, i64 0)) #10 - unreachable -} - -; Function Attrs: nounwind ssp uwtable -define weak i8* @sorbet_getBuildSCMRevision() local_unnamed_addr #3 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([95 x i8], [95 x i8]* @.str.9, i64 0, i64 0)) #10 - unreachable -} - -; Function Attrs: nounwind sspreq uwtable -define internal i64 @"func_Object#3foo"(i32 %argc, i64* nocapture nofree readnone %argArray, i64 %selfRaw, %struct.rb_control_frame_struct* nonnull align 8 dereferenceable(8) %cfp, i8* nocapture nofree readnone %calling, i8* nocapture nofree readnone %callData) #4 !dbg !10 { -functionEntryInitializers: - %0 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 0 - store i64* getelementptr inbounds ([13 x i64], [13 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %0, align 8, !tbaa !14 - %tooManyArgs = icmp ugt i32 %argc, 0, !dbg !16 - br i1 %tooManyArgs, label %argCountFailBlock, label %1, !dbg !16, !prof !17 - -argCountFailBlock: ; preds = %functionEntryInitializers - tail call void @sorbet_raiseArity(i32 %argc, i32 noundef 0, i32 noundef 0) #11, !dbg !16 - unreachable, !dbg !16 - -1: ; preds = %functionEntryInitializers - store i64* getelementptr inbounds ([13 x i64], [13 x i64]* @iseqEncodedArray, i64 0, i64 6), i64** %0, align 8, !dbg !18, !tbaa !14 - %2 = load i64, i64* @guard_epoch_A, align 8, !dbg !19 - %3 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !19, !tbaa !20 - %needTakeSlowPath = icmp ne i64 %2, %3, !dbg !19 - br i1 %needTakeSlowPath, label %4, label %5, !dbg !19, !prof !22 - -4: ; preds = %1 - tail call void @const_recompute_A(), !dbg !19 - br label %5, !dbg !19 - -5: ; preds = %1, %4 - %6 = load i64, i64* @guarded_const_A, align 8, !dbg !19 - %7 = load i64, i64* @guard_epoch_A, align 8, !dbg !19 - %8 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !19, !tbaa !20 - %guardUpdated = icmp eq i64 %7, %8, !dbg !19 - tail call void @llvm.assume(i1 %guardUpdated), !dbg !19 - %9 = load i64, i64* @rb_mKernel, align 8, !dbg !19 - %10 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 1, !dbg !19 - %11 = load i64*, i64** %10, align 8, !dbg !19 - store i64 %9, i64* %11, align 8, !dbg !19, !tbaa !6 - %12 = getelementptr inbounds i64, i64* %11, i64 1, !dbg !19 - store i64 %6, i64* %12, align 8, !dbg !19, !tbaa !6 - %13 = getelementptr inbounds i64, i64* %12, i64 1, !dbg !19 - store i64* %13, i64** %10, align 8, !dbg !19 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts, i64 0), !dbg !19 - store i64* getelementptr inbounds ([13 x i64], [13 x i64]* @iseqEncodedArray, i64 0, i64 7), i64** %0, align 8, !dbg !19, !tbaa !14 - %14 = load i64, i64* @"func_Object#3foo$block_1_ifunc", align 8, !dbg !23 - %15 = tail call %struct.vm_ifunc* @sorbet_globalConstFetchIfunc(i64 %14), !dbg !23 - %16 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !23, !tbaa !14 - %17 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %16, i64 0, i32 2, !dbg !23 - %18 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %17, align 8, !dbg !23, !tbaa !24 - %19 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %18, i64 0, i32 3, !dbg !23 - %20 = bitcast i64* %19 to %struct.rb_captured_block*, !dbg !23 - %21 = getelementptr inbounds i64, i64* %19, i64 2, !dbg !23 - %22 = bitcast i64* %21 to %struct.vm_ifunc**, !dbg !23 - store %struct.vm_ifunc* %15, %struct.vm_ifunc** %22, align 8, !dbg !23, !tbaa !28 - tail call void @llvm.experimental.noalias.scope.decl(metadata !29), !dbg !23 - %23 = ptrtoint %struct.rb_captured_block* %20 to i64, !dbg !23 - %24 = or i64 %23, 3, !dbg !23 - %25 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %16, i64 0, i32 17, !dbg !23 - %26 = and i64 %24, -4, !dbg !32 - %27 = inttoptr i64 %26 to %struct.rb_captured_block*, !dbg !32 - store i64 0, i64* %25, align 8, !dbg !32, !tbaa !34 - tail call void @sorbet_pushBlockFrame(%struct.rb_captured_block* %27) #12, !dbg !32 - br label %28, !dbg !32 - -28: ; preds = %28, %5 - %29 = phi i64 [ 0, %5 ], [ %43, %28 ], !dbg !32 - %30 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !23, !tbaa !14 - %31 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %30, i64 0, i32 2, !dbg !23 - %32 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %31, align 8, !dbg !23, !tbaa !24 - %stackFrame.i1.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Object#3foo$block_1", align 8, !dbg !23 - %33 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %32, i64 0, i32 2, !dbg !23 - store %struct.rb_iseq_struct* %stackFrame.i1.i, %struct.rb_iseq_struct** %33, align 8, !dbg !23, !tbaa !35 - %34 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %32, i64 0, i32 4, !dbg !23 - %35 = load i64*, i64** %34, align 8, !dbg !23, !tbaa !37 - %36 = load i64, i64* %35, align 8, !dbg !23, !tbaa !6 - %37 = and i64 %36, -129, !dbg !23 - store i64 %37, i64* %35, align 8, !dbg !23, !tbaa !6 - %38 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %32, i64 0, i32 0, !dbg !23 - store i64* getelementptr inbounds ([13 x i64], [13 x i64]* @iseqEncodedArray, i64 0, i64 8), i64** %38, align 8, !dbg !38, !tbaa !14 - %39 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %32, i64 0, i32 1, !dbg !41 - %40 = load i64*, i64** %39, align 8, !dbg !41 - store i64 %9, i64* %40, align 8, !dbg !41, !tbaa !6 - %41 = getelementptr inbounds i64, i64* %40, i64 1, !dbg !41 - store i64 %6, i64* %41, align 8, !dbg !41, !tbaa !6 - %42 = getelementptr inbounds i64, i64* %41, i64 1, !dbg !41 - store i64* %42, i64** %39, align 8, !dbg !41 - %send23 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts.1, i64 0), !dbg !41 - %43 = add nuw nsw i64 %29, 1, !dbg !32 - %44 = icmp eq i64 %43, 10, !dbg !32 - br i1 %44, label %forward_sorbet_rb_int_dotimes_withBlock.exit, label %28, !dbg !32, !llvm.loop !42 - -forward_sorbet_rb_int_dotimes_withBlock.exit: ; preds = %28 - tail call void @sorbet_popFrame() #12, !dbg !32 - %45 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !23, !tbaa !14 - %46 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %45, i64 0, i32 5, !dbg !23 - %47 = load i32, i32* %46, align 8, !dbg !23, !tbaa !44 - %48 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %45, i64 0, i32 6, !dbg !23 - %49 = load i32, i32* %48, align 4, !dbg !23, !tbaa !45 - %50 = xor i32 %49, -1, !dbg !23 - %51 = and i32 %50, %47, !dbg !23 - %52 = icmp eq i32 %51, 0, !dbg !23 - br i1 %52, label %rb_vm_check_ints.exit, label %53, !dbg !23, !prof !46 - -53: ; preds = %forward_sorbet_rb_int_dotimes_withBlock.exit - %54 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %45, i64 0, i32 8, !dbg !23 - %55 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %54, align 8, !dbg !23, !tbaa !47 - %56 = tail call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %55, i32 noundef 0) #12, !dbg !23 - br label %rb_vm_check_ints.exit, !dbg !23 - -rb_vm_check_ints.exit: ; preds = %forward_sorbet_rb_int_dotimes_withBlock.exit, %53 - store i64* getelementptr inbounds ([13 x i64], [13 x i64]* @iseqEncodedArray, i64 0, i64 7), i64** %0, align 8, !tbaa !14 - ret i64 21 -} - -; Function Attrs: ssp -define internal i64 @"func_Object#3foo$block_1"(i64 %firstYieldArgRaw, i64 %localsOffset, i32 %argc, i64* nocapture nofree readnone %argArray, i64 %blockArg) #5 !dbg !39 { -functionEntryInitializers: - %0 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !14 - %1 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %0, i64 0, i32 2 - %2 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %1, align 8, !tbaa !24 - %stackFrame = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Object#3foo$block_1", align 8 - %3 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame, %struct.rb_iseq_struct** %3, align 8, !tbaa !35 - %4 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 4 - %5 = load i64*, i64** %4, align 8, !tbaa !37 - %6 = load i64, i64* %5, align 8, !tbaa !6 - %7 = and i64 %6, -129 - store i64 %7, i64* %5, align 8, !tbaa !6 - %8 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 0 - store i64* getelementptr inbounds ([13 x i64], [13 x i64]* @iseqEncodedArray, i64 0, i64 8), i64** %8, align 8, !dbg !48, !tbaa !14 - %9 = load i64, i64* @guard_epoch_A, align 8, !dbg !49 - %10 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !49, !tbaa !20 - %needTakeSlowPath = icmp ne i64 %9, %10, !dbg !49 - br i1 %needTakeSlowPath, label %11, label %12, !dbg !49, !prof !22 - -11: ; preds = %functionEntryInitializers - tail call void @const_recompute_A(), !dbg !49 - br label %12, !dbg !49 - -12: ; preds = %functionEntryInitializers, %11 - %13 = load i64, i64* @guarded_const_A, align 8, !dbg !49 - %14 = load i64, i64* @guard_epoch_A, align 8, !dbg !49 - %15 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !49, !tbaa !20 - %guardUpdated = icmp eq i64 %14, %15, !dbg !49 - tail call void @llvm.assume(i1 %guardUpdated), !dbg !49 - %16 = load i64, i64* @rb_mKernel, align 8, !dbg !49 - %17 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 1, !dbg !49 - %18 = load i64*, i64** %17, align 8, !dbg !49 - store i64 %16, i64* %18, align 8, !dbg !49, !tbaa !6 - %19 = getelementptr inbounds i64, i64* %18, i64 1, !dbg !49 - store i64 %13, i64* %19, align 8, !dbg !49, !tbaa !6 - %20 = getelementptr inbounds i64, i64* %19, i64 1, !dbg !49 - store i64* %20, i64** %17, align 8, !dbg !49 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts.1, i64 0), !dbg !49 - ret i64 %send, !dbg !49 -} - -; Function Attrs: sspreq -define void @Init_block_no_args_captures_constant() local_unnamed_addr #6 { -entry: - %locals.i4.i = alloca i64, i32 0, align 8 - %locals.i.i = alloca i64, i32 0, align 8 - %realpath = tail call i64 @sorbet_readRealpath() - tail call void @sorbet_vm_intern_ids(i64* noundef getelementptr inbounds ([6 x i64], [6 x i64]* @sorbet_moduleIDTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([6 x %struct.rb_code_position_struct], [6 x %struct.rb_code_position_struct]* @sorbet_moduleIDDescriptors, i32 0, i32 0), i32 noundef 6, i8* noundef getelementptr inbounds ([136 x i8], [136 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_vm_init_string_table(i64* noundef getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleRubyStringTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([5 x %struct.rb_code_position_struct], [5 x %struct.rb_code_position_struct]* @sorbet_moduleRubyStringDescriptors, i32 0, i32 0), i32 noundef 5, i8* noundef getelementptr inbounds ([136 x i8], [136 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef getelementptr inbounds ([13 x i64], [13 x i64]* @iseqEncodedArray, i32 0, i32 0), i32 noundef 13) - %rubyId_foo.i.i = load i64, i64* getelementptr inbounds ([6 x i64], [6 x i64]* @sorbet_moduleIDTable, i64 0, i64 0), align 8, !invariant.load !5 - %rubyStr_foo.i.i = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 0), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/block_no_args_captures_constant.rb.i.i" = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %rubyStr_foo.i.i, i64 %rubyId_foo.i.i, i64 %"rubyStr_test/testdata/compiler/block_no_args_captures_constant.rb.i.i", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 1, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals.i.i, i32 noundef 0, i32 noundef 2) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Object#3foo", align 8 - %"rubyId_block in foo.i.i" = load i64, i64* getelementptr inbounds ([6 x i64], [6 x i64]* @sorbet_moduleIDTable, i64 0, i64 1), align 8, !invariant.load !5 - %"rubyStr_block in foo.i.i" = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 2), align 8, !invariant.load !5 - %1 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_block in foo.i.i", i64 %"rubyId_block in foo.i.i", i64 %"rubyStr_test/testdata/compiler/block_no_args_captures_constant.rb.i.i", i64 %realpath, %struct.rb_iseq_struct* %0, i32 noundef 2, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef null, i32 noundef 0, i32 noundef 2) - store %struct.rb_iseq_struct* %1, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Object#3foo$block_1", align 8 - %rubyId_puts.i = load i64, i64* getelementptr inbounds ([6 x i64], [6 x i64]* @sorbet_moduleIDTable, i64 0, i64 2), align 8, !dbg !19, !invariant.load !5 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts, i64 %rubyId_puts.i, i32 noundef 16, i32 noundef 1, i32 noundef 0), !dbg !19 - %2 = call %struct.vm_ifunc* @rb_vm_ifunc_new(i64 (i64, i64, i32, i64*, i64)* noundef @"func_Object#3foo$block_1", i8* noundef null, i32 noundef 0, i32 noundef 0) #12 - %3 = ptrtoint %struct.vm_ifunc* %2 to i64 - %4 = call i64 @sorbet_globalConstRegister(i64 %3) - store i64 %4, i64* @"func_Object#3foo$block_1_ifunc", align 8 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts.1, i64 %rubyId_puts.i, i32 noundef 16, i32 noundef 1, i32 noundef 0), !dbg !49 - %"rubyId_.i.i" = load i64, i64* getelementptr inbounds ([6 x i64], [6 x i64]* @sorbet_moduleIDTable, i64 0, i64 4), align 8, !invariant.load !5 - %"rubyStr_.i.i" = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 3), align 8, !invariant.load !5 - %5 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_.i.i", i64 %"rubyId_.i.i", i64 %"rubyStr_test/testdata/compiler/block_no_args_captures_constant.rb.i.i", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 0, i32 noundef 4, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals.i4.i, i32 noundef 0, i32 noundef 4) - store %struct.rb_iseq_struct* %5, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_foo, i64 %rubyId_foo.i.i, i32 noundef 20, i32 noundef 0, i32 noundef 0), !dbg !50 - %6 = load %struct.rb_vm_struct*, %struct.rb_vm_struct** @ruby_current_vm_ptr, align 8, !tbaa !14 - %7 = getelementptr inbounds %struct.rb_vm_struct, %struct.rb_vm_struct* %6, i64 0, i32 18 - %8 = load i64, i64* %7, align 8, !tbaa !52 - %9 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !14 - %10 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %9, i64 0, i32 2 - %11 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %10, align 8, !tbaa !24 - %stackFrame.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - %12 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %11, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame.i, %struct.rb_iseq_struct** %12, align 8, !tbaa !35 - %13 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %11, i64 0, i32 4 - %14 = load i64*, i64** %13, align 8, !tbaa !37 - %15 = load i64, i64* %14, align 8, !tbaa !6 - %16 = and i64 %15, -33 - store i64 %16, i64* %14, align 8, !tbaa !6 - call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %9, %struct.rb_control_frame_struct* %11, %struct.rb_iseq_struct* %stackFrame.i) #12 - %17 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %11, i64 0, i32 0 - store i64* getelementptr inbounds ([13 x i64], [13 x i64]* @iseqEncodedArray, i64 0, i64 4), i64** %17, align 8, !dbg !60, !tbaa !14 - %rubyStr_hi.i = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 4), align 8, !dbg !61, !invariant.load !5 - %18 = load i64, i64* @rb_cObject, align 8, !dbg !61 - %19 = call i64 @sorbet_setConstant(i64 %18, i8* getelementptr inbounds ([136 x i8], [136 x i8]* @sorbet_moduleStringTable, i64 0, i64 75), i64 noundef 1, i64 %rubyStr_hi.i) #12, !dbg !61 - store i64* getelementptr inbounds ([13 x i64], [13 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %17, align 8, !dbg !61, !tbaa !14 - %stackFrame12.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Object#3foo", align 8, !dbg !62 - %20 = call noalias nonnull i8* @ruby_xcalloc(i64 noundef 1, i64 noundef 64) #13, !dbg !62 - %21 = bitcast i8* %20 to i16*, !dbg !62 - %22 = load i16, i16* %21, align 8, !dbg !62 - %23 = and i16 %22, -384, !dbg !62 - store i16 %23, i16* %21, align 8, !dbg !62 - %24 = getelementptr inbounds i8, i8* %20, i64 4, !dbg !62 - call void @llvm.memset.p0i8.i64(i8* nonnull align 4 %24, i8 0, i64 28, i1 false) #12, !dbg !62 - call void @sorbet_vm_define_method(i64 %18, i8* noundef getelementptr inbounds ([136 x i8], [136 x i8]* @sorbet_moduleStringTable, i64 0, i64 0), i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)* noundef @"func_Object#3foo", i8* nonnull %20, %struct.rb_iseq_struct* %stackFrame12.i, i1 noundef zeroext false) #12, !dbg !62 - store i64* getelementptr inbounds ([13 x i64], [13 x i64]* @iseqEncodedArray, i64 0, i64 12), i64** %17, align 8, !dbg !62, !tbaa !14 - %25 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %11, i64 0, i32 1, !dbg !50 - %26 = load i64*, i64** %25, align 8, !dbg !50 - store i64 %8, i64* %26, align 8, !dbg !50, !tbaa !6 - %27 = getelementptr inbounds i64, i64* %26, i64 1, !dbg !50 - store i64* %27, i64** %25, align 8, !dbg !50 - %send = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_foo, i64 0), !dbg !50 - ret void -} - -; Function Attrs: inaccessiblememonly nofree nosync nounwind willreturn -declare void @llvm.experimental.noalias.scope.decl(metadata) #7 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly -declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #8 - -; Function Attrs: nofree nosync nounwind willreturn -declare void @llvm.assume(i1 noundef) #9 - -; Function Attrs: ssp -define linkonce void @const_recompute_A() local_unnamed_addr #5 { - %1 = tail call i64 @sorbet_getConstant(i8* getelementptr inbounds ([136 x i8], [136 x i8]* @sorbet_moduleStringTable, i64 0, i64 75), i64 1) - store i64 %1, i64* @guarded_const_A, align 8 - %2 = load i64, i64* @ruby_vm_global_constant_state, align 8, !tbaa !20 - store i64 %2, i64* @guard_epoch_A, align 8 - ret void -} - -attributes #0 = { noreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #1 = { "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #2 = { allocsize(0,1) "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #3 = { nounwind ssp uwtable "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #4 = { nounwind sspreq uwtable } -attributes #5 = { ssp } -attributes #6 = { sspreq } -attributes #7 = { inaccessiblememonly nofree nosync nounwind willreturn } -attributes #8 = { argmemonly nofree nosync nounwind willreturn writeonly } -attributes #9 = { nofree nosync nounwind willreturn } -attributes #10 = { noreturn nounwind } -attributes #11 = { noreturn } -attributes #12 = { nounwind } -attributes #13 = { nounwind allocsize(0,1) } - -!llvm.module.flags = !{!0, !1, !2} -!llvm.dbg.cu = !{!3} - -!0 = !{i32 2, !"Debug Info Version", i32 3} -!1 = !{i32 4, !"cf-protection-return", i32 1} -!2 = !{i32 4, !"cf-protection-branch", i32 1} -!3 = distinct !DICompileUnit(language: DW_LANG_C, file: !4, producer: "Sorbet LLVM", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !5) -!4 = !DIFile(filename: "test/testdata/compiler/block_no_args_captures_constant.rb", directory: ".") -!5 = !{} -!6 = !{!7, !7, i64 0} -!7 = !{!"long", !8, i64 0} -!8 = !{!"omnipotent char", !9, i64 0} -!9 = !{!"Simple C/C++ TBAA"} -!10 = distinct !DISubprogram(name: "Object#foo", linkageName: "func_Object#3foo", scope: null, file: !4, line: 5, type: !11, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!11 = !DISubroutineType(types: !12) -!12 = !{!13} -!13 = !DIBasicType(name: "VALUE", size: 64, encoding: DW_ATE_signed) -!14 = !{!15, !15, i64 0} -!15 = !{!"any pointer", !8, i64 0} -!16 = !DILocation(line: 5, column: 1, scope: !10) -!17 = !{!"branch_weights", i32 1, i32 2000} -!18 = !DILocation(line: 0, scope: !10) -!19 = !DILocation(line: 6, column: 3, scope: !10) -!20 = !{!21, !21, i64 0} -!21 = !{!"long long", !8, i64 0} -!22 = !{!"branch_weights", i32 1, i32 10000} -!23 = !DILocation(line: 7, column: 3, scope: !10) -!24 = !{!25, !15, i64 16} -!25 = !{!"rb_execution_context_struct", !15, i64 0, !7, i64 8, !15, i64 16, !15, i64 24, !15, i64 32, !26, i64 40, !26, i64 44, !15, i64 48, !15, i64 56, !15, i64 64, !7, i64 72, !7, i64 80, !15, i64 88, !7, i64 96, !15, i64 104, !15, i64 112, !7, i64 120, !7, i64 128, !8, i64 136, !8, i64 137, !7, i64 144, !27, i64 152} -!26 = !{!"int", !8, i64 0} -!27 = !{!"", !15, i64 0, !15, i64 8, !7, i64 16, !8, i64 24} -!28 = !{!8, !8, i64 0} -!29 = !{!30} -!30 = distinct !{!30, !31, !"VM_BH_FROM_IFUNC_BLOCK: argument 0"} -!31 = distinct !{!31, !"VM_BH_FROM_IFUNC_BLOCK"} -!32 = !DILocation(line: 7, column: 3, scope: !10, inlinedAt: !33) -!33 = distinct !DILocation(line: 7, column: 3, scope: !10) -!34 = !{!25, !7, i64 128} -!35 = !{!36, !15, i64 16} -!36 = !{!"rb_control_frame_struct", !15, i64 0, !15, i64 8, !15, i64 16, !7, i64 24, !15, i64 32, !15, i64 40, !15, i64 48} -!37 = !{!36, !15, i64 32} -!38 = !DILocation(line: 7, column: 3, scope: !39, inlinedAt: !40) -!39 = distinct !DISubprogram(name: "Object#foo", linkageName: "func_Object#3foo$block_1", scope: !10, file: !4, line: 5, type: !11, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!40 = distinct !DILocation(line: 7, column: 3, scope: !10, inlinedAt: !33) -!41 = !DILocation(line: 8, column: 5, scope: !39, inlinedAt: !40) -!42 = distinct !{!42, !43} -!43 = !{!"llvm.loop.unroll.disable"} -!44 = !{!25, !26, i64 40} -!45 = !{!25, !26, i64 44} -!46 = !{!"branch_weights", i32 2000, i32 1} -!47 = !{!25, !15, i64 56} -!48 = !DILocation(line: 7, column: 3, scope: !39) -!49 = !DILocation(line: 8, column: 5, scope: !39) -!50 = !DILocation(line: 12, column: 1, scope: !51) -!51 = distinct !DISubprogram(name: ".", linkageName: "func_.13", scope: null, file: !4, line: 4, type: !11, scopeLine: 4, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!52 = !{!53, !7, i64 400} -!53 = !{!"rb_vm_struct", !7, i64 0, !54, i64 8, !15, i64 192, !15, i64 200, !15, i64 208, !21, i64 216, !8, i64 224, !55, i64 264, !55, i64 280, !55, i64 296, !55, i64 312, !7, i64 328, !26, i64 336, !26, i64 340, !26, i64 344, !26, i64 344, !26, i64 344, !26, i64 344, !26, i64 348, !7, i64 352, !8, i64 360, !7, i64 400, !7, i64 408, !7, i64 416, !7, i64 424, !7, i64 432, !7, i64 440, !7, i64 448, !15, i64 456, !15, i64 464, !57, i64 472, !58, i64 992, !15, i64 1016, !15, i64 1024, !26, i64 1032, !26, i64 1036, !55, i64 1040, !8, i64 1056, !7, i64 1096, !7, i64 1104, !7, i64 1112, !7, i64 1120, !7, i64 1128, !26, i64 1136, !15, i64 1144, !15, i64 1152, !15, i64 1160, !15, i64 1168, !15, i64 1176, !15, i64 1184, !26, i64 1192, !59, i64 1200, !8, i64 1232} -!54 = !{!"rb_global_vm_lock_struct", !15, i64 0, !8, i64 8, !55, i64 48, !15, i64 64, !26, i64 72, !8, i64 80, !8, i64 128, !26, i64 176, !26, i64 180} -!55 = !{!"list_head", !56, i64 0} -!56 = !{!"list_node", !15, i64 0, !15, i64 8} -!57 = !{!"", !8, i64 0} -!58 = !{!"rb_hook_list_struct", !15, i64 0, !26, i64 8, !26, i64 12, !26, i64 16} -!59 = !{!"", !7, i64 0, !7, i64 8, !7, i64 16, !7, i64 24} -!60 = !DILocation(line: 0, scope: !51) -!61 = !DILocation(line: 4, column: 5, scope: !51) -!62 = !DILocation(line: 5, column: 1, scope: !51) diff --git a/test/testdata/compiler/block_type_checking.opt.ll.exp b/test/testdata/compiler/block_type_checking.opt.ll.exp deleted file mode 100644 index 405c5a01c0..0000000000 --- a/test/testdata/compiler/block_type_checking.opt.ll.exp +++ /dev/null @@ -1,791 +0,0 @@ -; ModuleID = 'payload' -source_filename = "llvm-link" -target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } -%struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.__pthread_cond_s } -%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } -%union.anon = type { i64 } -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } -%struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } -%struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } -%struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } -%struct.anon.0 = type { i16, [2 x i8] } -%struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } -%struct.rb_iseq_location_struct = type { i64, i64, i64, i64, i32, %struct.rb_code_location_struct } -%struct.rb_code_location_struct = type { %struct.rb_code_position_struct, %struct.rb_code_position_struct } -%struct.rb_code_position_struct = type { i32, i32 } -%struct.iseq_insn_info = type { %struct.iseq_insn_info_entry*, i32*, i32, %struct.succ_index_table* } -%struct.iseq_insn_info_entry = type opaque -%struct.succ_index_table = type opaque -%struct.iseq_catch_table = type opaque -%union.iseq_inline_storage_entry = type { %struct.iseq_inline_cache_entry } -%struct.iseq_inline_cache_entry = type { i64, %struct.rb_cref_struct*, i64 } -%struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } -%struct.rb_scope_visi_struct = type { i8, [3 x i8] } -%struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } -%struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } -%union.anon.13 = type { %struct.rb_method_cfunc_struct } -%struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%union.anon.15 = type { i32 } -%struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.16 = type { i64, i64, i64, i64* } -%union.anon.17 = type { %struct.anon.18 } -%struct.anon.18 = type { i64, i32 } -%struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } -%struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } -%struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } -%struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } -%struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } -%struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } -%struct.__sigset_t = type { [16 x i64] } -%struct.rb_calling_info = type { i64, i64, i32, i32 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } -%union.anon.9 = type { %union.pthread_cond_t } -%struct.rb_unblock_callback = type { void (i8*)*, i8* } -%struct.rb_mutex_struct = type opaque -%struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.10 = type { %struct.anon.11 } -%struct.anon.11 = type { i64, i64, i32 } -%struct.rb_fiber_struct = type opaque -%struct.anon.5 = type { [65 x i64] } -%struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } -%struct.rb_event_hook_struct = type opaque -%struct.rb_postponed_job_struct = type opaque -%struct.list_head = type { %struct.list_node } -%union.pthread_mutex_t = type { %struct.__pthread_mutex_s } -%struct.__pthread_mutex_s = type { i32, i32, i32, i32, i32, i16, i16, %struct.__pthread_internal_list } -%struct.__pthread_internal_list = type { %struct.__pthread_internal_list*, %struct.__pthread_internal_list* } -%struct.rb_objspace = type opaque -%struct.rb_at_exit_list = type { void (%struct.rb_vm_struct*)*, %struct.rb_at_exit_list* } -%struct.st_table = type { i8, i8, i8, i32, %struct.st_hash_type*, i64, i64*, i64, i64, %struct.st_table_entry* } -%struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } -%struct.st_table_entry = type opaque -%struct.rb_builtin_function = type opaque -%struct.anon.6 = type { i64, i64, i64, i64 } -%struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } -%struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } -%struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } -%struct.rb_call_info_with_kwarg = type { %struct.rb_call_info, %struct.rb_call_info_kw_arg* } -%struct.rb_call_info_kw_arg = type { i32, [1 x i64] } -%struct.vm_ifunc = type { i64, i64, i64 (i64, i64, i32, i64*, i64)*, i8*, %struct.rb_code_position_struct } -%struct.rb_captured_block = type { i64, i64*, %union.anon.20 } -%union.anon.20 = type { %struct.rb_iseq_struct* } -%struct.iseq_inline_iv_cache_entry = type { i64, i64 } - -@ruby_current_vm_ptr = external local_unnamed_addr global %struct.rb_vm_struct*, align 8 -@ruby_current_execution_context_ptr = external local_unnamed_addr global %struct.rb_execution_context_struct*, align 8 -@ruby_vm_global_constant_state = external local_unnamed_addr global i64, align 8 -@rb_eRuntimeError = external local_unnamed_addr global i64, align 8 -@.str.9 = private unnamed_addr constant [95 x i8] c"sorbet_getBuildSCMRevision: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@.str.10 = private unnamed_addr constant [93 x i8] c"sorbet_getIsReleaseBuild: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@"stackFramePrecomputed_func_.13" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@iseqEncodedArray = internal global [19 x i64] zeroinitializer -@fileLineNumberInfo = internal global %struct.SorbetLineNumberInfo zeroinitializer -@"stackFramePrecomputed_func_.13$block_1" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@ic_new = internal global %struct.FunctionInlineCache zeroinitializer -@ic_initialize = internal global %struct.FunctionInlineCache zeroinitializer -@ic_bar = internal global %struct.FunctionInlineCache zeroinitializer -@"func_.13$block_1_ifunc" = internal unnamed_addr global i64 0 -@ic_p = internal global %struct.FunctionInlineCache zeroinitializer -@"stackFramePrecomputed_func_Foo#3bar" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"stackFramePrecomputed_func_Foo.13" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"stackFramePrecomputed_func_Foo.13$block_1" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@ic_proc = internal global %struct.FunctionInlineCache zeroinitializer -@ic_returns = internal global %struct.FunctionInlineCache zeroinitializer -@ic_params = internal global %struct.FunctionInlineCache zeroinitializer -@ic_returns.1 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_extend = internal global %struct.FunctionInlineCache zeroinitializer -@sorbet_moduleStringTable = internal unnamed_addr constant [247 x i8] c"\00test/testdata/compiler/block_type_checking.rb\00block in \00Foo\00Object\00new\00initialize\00bar\00p\00master\00Parameter 'x'\00Integer\00+\00Return value\00\00block in \00x\00blk\00proc\00T\00returns\00params\00T::Sig\00extend\00normal\00", align 1 -@sorbet_moduleIDTable = internal unnamed_addr global [16 x i64] zeroinitializer, align 8 -@sorbet_moduleIDDescriptors = internal unnamed_addr constant [16 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 16 }, %struct.rb_code_position_struct { i32 63, i32 25 }, %struct.rb_code_position_struct { i32 100, i32 3 }, %struct.rb_code_position_struct { i32 104, i32 10 }, %struct.rb_code_position_struct { i32 115, i32 3 }, %struct.rb_code_position_struct { i32 119, i32 1 }, %struct.rb_code_position_struct { i32 150, i32 1 }, %struct.rb_code_position_struct { i32 165, i32 11 }, %struct.rb_code_position_struct { i32 177, i32 20 }, %struct.rb_code_position_struct { i32 198, i32 1 }, %struct.rb_code_position_struct { i32 200, i32 3 }, %struct.rb_code_position_struct { i32 204, i32 4 }, %struct.rb_code_position_struct { i32 211, i32 7 }, %struct.rb_code_position_struct { i32 219, i32 6 }, %struct.rb_code_position_struct { i32 233, i32 6 }, %struct.rb_code_position_struct { i32 240, i32 6 }], align 8 -@sorbet_moduleRubyStringTable = internal unnamed_addr global [6 x i64] zeroinitializer, align 8 -@sorbet_moduleRubyStringDescriptors = internal unnamed_addr constant [6 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 16 }, %struct.rb_code_position_struct { i32 17, i32 45 }, %struct.rb_code_position_struct { i32 63, i32 25 }, %struct.rb_code_position_struct { i32 115, i32 3 }, %struct.rb_code_position_struct { i32 165, i32 11 }, %struct.rb_code_position_struct { i32 177, i32 20 }], align 8 -@rb_cObject = external local_unnamed_addr constant i64 -@"guard_epoch_T::Sig" = linkonce local_unnamed_addr global i64 0 -@"guarded_const_T::Sig" = linkonce local_unnamed_addr global i64 0 -@guard_epoch_Foo = linkonce local_unnamed_addr global i64 0 -@guarded_const_Foo = linkonce local_unnamed_addr global i64 0 -@guard_epoch_T = linkonce local_unnamed_addr global i64 0 -@guarded_const_T = linkonce local_unnamed_addr global i64 0 -@rb_cInteger = external local_unnamed_addr constant i64 - -; Function Attrs: nounwind readnone willreturn -declare i64 @rb_id2sym(i64) local_unnamed_addr #0 - -; Function Attrs: cold noreturn -declare void @sorbet_cast_failure(i64, i8*, i8*) local_unnamed_addr #1 - -; Function Attrs: noreturn -declare void @sorbet_raiseArity(i32, i32, i32) local_unnamed_addr #2 - -declare %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64, i64, i64, i64, %struct.rb_iseq_struct*, i32, i32, %struct.SorbetLineNumberInfo*, i64*, i32, i32) local_unnamed_addr #3 - -declare void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo*, i64*, i32) local_unnamed_addr #3 - -declare i64 @sorbet_getConstant(i8*, i64) local_unnamed_addr #3 - -declare i64 @sorbet_readRealpath() local_unnamed_addr #3 - -declare %struct.rb_control_frame_struct* @sorbet_pushStaticInitFrame(i64) local_unnamed_addr #3 - -declare void @sorbet_popFrame() local_unnamed_addr #3 - -declare void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) local_unnamed_addr #3 - -declare i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache*, i64) local_unnamed_addr #3 - -declare void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_iseq_struct*) local_unnamed_addr #3 - -declare i64 @sorbet_rb_int_plus_slowpath(i64, i64) local_unnamed_addr #3 - -declare i64 @sorbet_globalConstRegister(i64) local_unnamed_addr #3 - -declare %struct.vm_ifunc* @sorbet_globalConstFetchIfunc(i64) local_unnamed_addr #3 - -declare void @sorbet_vm_register_sig(i64, i64, i64, i64, i64 (i64, i64, i32, i64*, i64)*) local_unnamed_addr #3 - -declare void @sorbet_vm_define_method(i64, i8*, i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)*, i8*, %struct.rb_iseq_struct*, i1 zeroext) local_unnamed_addr #3 - -declare void @sorbet_vm_intern_ids(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #3 - -declare void @sorbet_vm_init_string_table(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #3 - -declare i64 @sorbet_maybeAllocateObjectFastPath(i64, %struct.FunctionInlineCache*) local_unnamed_addr #3 - -declare i64 @sorbet_vm_callBlock(%struct.rb_control_frame_struct*, i32, i64* nocapture, i32) local_unnamed_addr #3 - -declare i64 @rb_define_class(i8*, i64) local_unnamed_addr #3 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #4 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #4 - -; Function Attrs: noreturn -declare void @rb_raise(i64, i8*, ...) local_unnamed_addr #2 - -declare i64 @rb_int2big(i64) local_unnamed_addr #3 - -; Function Attrs: nofree nosync nounwind readnone speculatable willreturn -declare { i64, i1 } @llvm.sadd.with.overflow.i64(i64, i64) #5 - -; Function Attrs: inaccessiblememonly nofree nosync nounwind willreturn -declare void @llvm.experimental.noalias.scope.decl(metadata) #6 - -declare i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct*, i32) local_unnamed_addr #3 - -declare %struct.vm_ifunc* @rb_vm_ifunc_new(i64 (i64, i64, i32, i64*, i64)*, i8*, i32, i32) local_unnamed_addr #3 - -; Function Attrs: allocsize(0,1) -declare noalias nonnull i8* @ruby_xcalloc(i64, i64) local_unnamed_addr #7 - -; Function Attrs: allocsize(0,1) -declare noalias nonnull i8* @ruby_xmalloc2(i64, i64) local_unnamed_addr #7 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) #4 - -; Function Attrs: nounwind ssp uwtable -define weak i32 @sorbet_getIsReleaseBuild() local_unnamed_addr #8 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([93 x i8], [93 x i8]* @.str.10, i64 0, i64 0)) #16 - unreachable -} - -; Function Attrs: nounwind ssp uwtable -define weak i8* @sorbet_getBuildSCMRevision() local_unnamed_addr #8 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([95 x i8], [95 x i8]* @.str.9, i64 0, i64 0)) #16 - unreachable -} - -; Function Attrs: nofree nosync nounwind ssp willreturn -define internal noundef i64 @"func_.13$block_1"(i64 %firstYieldArgRaw, i64 %localsOffset, i32 %argc, i64* nocapture nofree readnone %argArray, i64 %blockArg) #9 !dbg !10 { -functionEntryInitializers: - %0 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !15 - %1 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %0, i64 0, i32 2 - %2 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %1, align 8, !tbaa !17 - %stackFrame = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13$block_1", align 8 - %3 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame, %struct.rb_iseq_struct** %3, align 8, !tbaa !21 - %4 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 4 - %5 = load i64*, i64** %4, align 8, !tbaa !23 - %6 = load i64, i64* %5, align 8, !tbaa !6 - %7 = and i64 %6, -129 - store i64 %7, i64* %5, align 8, !tbaa !6 - %8 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 0 - store i64* getelementptr inbounds ([19 x i64], [19 x i64]* @iseqEncodedArray, i64 0, i64 16), i64** %8, align 8, !dbg !24, !tbaa !15 - ret i64 13, !dbg !25 -} - -; Function Attrs: sspreq -define void @Init_block_type_checking() local_unnamed_addr #10 { -entry: - %positional_table.i.i = alloca i64, i32 2, align 8, !dbg !26 - %locals.i6.i = alloca i64, i32 0, align 8 - %locals.i4.i = alloca i64, i32 0, align 8 - %locals.i.i = alloca i64, i32 0, align 8 - %realpath = tail call i64 @sorbet_readRealpath() - tail call void @sorbet_vm_intern_ids(i64* noundef getelementptr inbounds ([16 x i64], [16 x i64]* @sorbet_moduleIDTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([16 x %struct.rb_code_position_struct], [16 x %struct.rb_code_position_struct]* @sorbet_moduleIDDescriptors, i32 0, i32 0), i32 noundef 16, i8* noundef getelementptr inbounds ([247 x i8], [247 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_vm_init_string_table(i64* noundef getelementptr inbounds ([6 x i64], [6 x i64]* @sorbet_moduleRubyStringTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([6 x %struct.rb_code_position_struct], [6 x %struct.rb_code_position_struct]* @sorbet_moduleRubyStringDescriptors, i32 0, i32 0), i32 noundef 6, i8* noundef getelementptr inbounds ([247 x i8], [247 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef getelementptr inbounds ([19 x i64], [19 x i64]* @iseqEncodedArray, i32 0, i32 0), i32 noundef 19) - %"rubyId_.i.i" = load i64, i64* getelementptr inbounds ([16 x i64], [16 x i64]* @sorbet_moduleIDTable, i64 0, i64 0), align 8, !invariant.load !5 - %"rubyStr_.i.i" = load i64, i64* getelementptr inbounds ([6 x i64], [6 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 0), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/block_type_checking.rb.i.i" = load i64, i64* getelementptr inbounds ([6 x i64], [6 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_.i.i", i64 %"rubyId_.i.i", i64 %"rubyStr_test/testdata/compiler/block_type_checking.rb.i.i", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 0, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals.i.i, i32 noundef 0, i32 noundef 2) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - %"rubyId_block in .i.i" = load i64, i64* getelementptr inbounds ([16 x i64], [16 x i64]* @sorbet_moduleIDTable, i64 0, i64 1), align 8, !invariant.load !5 - %"rubyStr_block in .i.i" = load i64, i64* getelementptr inbounds ([6 x i64], [6 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 2), align 8, !invariant.load !5 - %1 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_block in .i.i", i64 %"rubyId_block in .i.i", i64 %"rubyStr_test/testdata/compiler/block_type_checking.rb.i.i", i64 %realpath, %struct.rb_iseq_struct* %0, i32 noundef 2, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef null, i32 noundef 0, i32 noundef 2) - store %struct.rb_iseq_struct* %1, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13$block_1", align 8 - %rubyId_new.i = load i64, i64* getelementptr inbounds ([16 x i64], [16 x i64]* @sorbet_moduleIDTable, i64 0, i64 2), align 8, !dbg !29, !invariant.load !5 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_new, i64 %rubyId_new.i, i32 noundef 16, i32 noundef 0, i32 noundef 0), !dbg !29 - %rubyId_initialize.i = load i64, i64* getelementptr inbounds ([16 x i64], [16 x i64]* @sorbet_moduleIDTable, i64 0, i64 3), align 8, !dbg !29, !invariant.load !5 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_initialize, i64 %rubyId_initialize.i, i32 noundef 20, i32 noundef 0, i32 noundef 0), !dbg !29 - %rubyId_bar.i = load i64, i64* getelementptr inbounds ([16 x i64], [16 x i64]* @sorbet_moduleIDTable, i64 0, i64 4), align 8, !dbg !29, !invariant.load !5 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_bar, i64 %rubyId_bar.i, i32 noundef 16, i32 noundef 1, i32 noundef 0), !dbg !29 - %2 = call %struct.vm_ifunc* @rb_vm_ifunc_new(i64 (i64, i64, i32, i64*, i64)* noundef @"func_.13$block_1", i8* noundef null, i32 noundef 0, i32 noundef 0) #17 - %3 = ptrtoint %struct.vm_ifunc* %2 to i64 - %4 = call i64 @sorbet_globalConstRegister(i64 %3) - store i64 %4, i64* @"func_.13$block_1_ifunc", align 8 - %rubyId_p.i = load i64, i64* getelementptr inbounds ([16 x i64], [16 x i64]* @sorbet_moduleIDTable, i64 0, i64 5), align 8, !dbg !30, !invariant.load !5 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_p, i64 %rubyId_p.i, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !30 - %rubyStr_bar.i.i = load i64, i64* getelementptr inbounds ([6 x i64], [6 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 3), align 8, !invariant.load !5 - %5 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %rubyStr_bar.i.i, i64 %rubyId_bar.i, i64 %"rubyStr_test/testdata/compiler/block_type_checking.rb.i.i", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 1, i32 noundef 9, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals.i4.i, i32 noundef 0, i32 noundef 2) - store %struct.rb_iseq_struct* %5, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Foo#3bar", align 8 - %"rubyId_.i.i" = load i64, i64* getelementptr inbounds ([16 x i64], [16 x i64]* @sorbet_moduleIDTable, i64 0, i64 7), align 8, !invariant.load !5 - %"rubyStr_.i.i" = load i64, i64* getelementptr inbounds ([6 x i64], [6 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 4), align 8, !invariant.load !5 - %6 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_.i.i", i64 %"rubyId_.i.i", i64 %"rubyStr_test/testdata/compiler/block_type_checking.rb.i.i", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 3, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals.i6.i, i32 noundef 0, i32 noundef 4) - store %struct.rb_iseq_struct* %6, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Foo.13", align 8 - %"rubyId_block in .i.i" = load i64, i64* getelementptr inbounds ([16 x i64], [16 x i64]* @sorbet_moduleIDTable, i64 0, i64 8), align 8, !invariant.load !5 - %"rubyStr_block in .i.i" = load i64, i64* getelementptr inbounds ([6 x i64], [6 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 5), align 8, !invariant.load !5 - %7 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_block in .i.i", i64 %"rubyId_block in .i.i", i64 %"rubyStr_test/testdata/compiler/block_type_checking.rb.i.i", i64 %realpath, %struct.rb_iseq_struct* %6, i32 noundef 2, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef null, i32 noundef 0, i32 noundef 4) - store %struct.rb_iseq_struct* %7, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Foo.13$block_1", align 8 - %rubyId_proc.i = load i64, i64* getelementptr inbounds ([16 x i64], [16 x i64]* @sorbet_moduleIDTable, i64 0, i64 11), align 8, !dbg !31, !invariant.load !5 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_proc, i64 %rubyId_proc.i, i32 noundef 16, i32 noundef 0, i32 noundef 0), !dbg !31 - %rubyId_returns.i = load i64, i64* getelementptr inbounds ([16 x i64], [16 x i64]* @sorbet_moduleIDTable, i64 0, i64 12), align 8, !dbg !31, !invariant.load !5 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_returns, i64 %rubyId_returns.i, i32 noundef 16, i32 noundef 1, i32 noundef 0), !dbg !31 - %rubyId_params.i = load i64, i64* getelementptr inbounds ([16 x i64], [16 x i64]* @sorbet_moduleIDTable, i64 0, i64 13), align 8, !dbg !33, !invariant.load !5 - %rubyId_x.i = load i64, i64* getelementptr inbounds ([16 x i64], [16 x i64]* @sorbet_moduleIDTable, i64 0, i64 9), align 8, !dbg !33, !invariant.load !5 - %rubyId_blk.i = load i64, i64* getelementptr inbounds ([16 x i64], [16 x i64]* @sorbet_moduleIDTable, i64 0, i64 10), align 8, !dbg !33, !invariant.load !5 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_params, i64 %rubyId_params.i, i32 noundef 68, i32 noundef 2, i32 noundef 2, i64 %rubyId_x.i, i64 %rubyId_blk.i), !dbg !33 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_returns.1, i64 %rubyId_returns.i, i32 noundef 16, i32 noundef 1, i32 noundef 0), !dbg !33 - %rubyId_extend.i = load i64, i64* getelementptr inbounds ([16 x i64], [16 x i64]* @sorbet_moduleIDTable, i64 0, i64 14), align 8, !dbg !34, !invariant.load !5 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_extend, i64 %rubyId_extend.i, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !34 - %8 = load %struct.rb_vm_struct*, %struct.rb_vm_struct** @ruby_current_vm_ptr, align 8, !tbaa !15 - %9 = getelementptr inbounds %struct.rb_vm_struct, %struct.rb_vm_struct* %8, i64 0, i32 18 - %10 = load i64, i64* %9, align 8, !tbaa !35 - %11 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !15 - %12 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %11, i64 0, i32 2 - %13 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %12, align 8, !tbaa !17 - %stackFrame.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - %14 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %13, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame.i, %struct.rb_iseq_struct** %14, align 8, !tbaa !21 - %15 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %13, i64 0, i32 4 - %16 = load i64*, i64** %15, align 8, !tbaa !23 - %17 = load i64, i64* %16, align 8, !tbaa !6 - %18 = and i64 %17, -33 - store i64 %18, i64* %16, align 8, !tbaa !6 - call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %11, %struct.rb_control_frame_struct* %13, %struct.rb_iseq_struct* %stackFrame.i) #17 - %19 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %13, i64 0, i32 0 - store i64* getelementptr inbounds ([19 x i64], [19 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %19, align 8, !dbg !44, !tbaa !15 - %20 = load i64, i64* @rb_cObject, align 8, !dbg !45 - %21 = call i64 @rb_define_class(i8* getelementptr inbounds ([247 x i8], [247 x i8]* @sorbet_moduleStringTable, i64 0, i64 89), i64 %20) #17, !dbg !45 - %22 = call %struct.rb_control_frame_struct* @sorbet_pushStaticInitFrame(i64 %21) #17, !dbg !45 - %23 = bitcast i64* %positional_table.i.i to i8* - call void @llvm.lifetime.start.p0i8(i64 16, i8* nonnull %23) #17 - %stackFrame.i.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Foo.13", align 8 - %24 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !15 - %25 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %24, i64 0, i32 2 - %26 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %25, align 8, !tbaa !17 - %27 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %26, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame.i.i, %struct.rb_iseq_struct** %27, align 8, !tbaa !21 - %28 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %26, i64 0, i32 4 - %29 = load i64*, i64** %28, align 8, !tbaa !23 - %30 = load i64, i64* %29, align 8, !tbaa !6 - %31 = and i64 %30, -33 - store i64 %31, i64* %29, align 8, !tbaa !6 - call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %24, %struct.rb_control_frame_struct* %26, %struct.rb_iseq_struct* %stackFrame.i.i) #17 - %32 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %22, i64 0, i32 0 - store i64* getelementptr inbounds ([19 x i64], [19 x i64]* @iseqEncodedArray, i64 0, i64 8), i64** %32, align 8, !dbg !46, !tbaa !15 - %rawSym.i.i = call i64 @rb_id2sym(i64 %rubyId_bar.i) #17, !dbg !47 - call void @sorbet_vm_register_sig(i64 noundef 0, i64 %rawSym.i.i, i64 %21, i64 noundef 8, i64 (i64, i64, i32, i64*, i64)* noundef @"func_Foo.13L62$block_1") #17, !dbg !47 - store i64* getelementptr inbounds ([19 x i64], [19 x i64]* @iseqEncodedArray, i64 0, i64 6), i64** %32, align 8, !dbg !47, !tbaa !15 - %33 = load i64, i64* @"guard_epoch_T::Sig", align 8, !dbg !48 - %34 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !48, !tbaa !49 - %needTakeSlowPath = icmp ne i64 %33, %34, !dbg !48 - br i1 %needTakeSlowPath, label %35, label %36, !dbg !48, !prof !50 - -35: ; preds = %entry - call void @"const_recompute_T::Sig"(), !dbg !48 - br label %36, !dbg !48 - -36: ; preds = %entry, %35 - %37 = load i64, i64* @"guarded_const_T::Sig", align 8, !dbg !48 - %38 = load i64, i64* @"guard_epoch_T::Sig", align 8, !dbg !48 - %39 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !48, !tbaa !49 - %guardUpdated = icmp eq i64 %38, %39, !dbg !48 - call void @llvm.assume(i1 %guardUpdated), !dbg !48 - %40 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %22, i64 0, i32 1, !dbg !48 - %41 = load i64*, i64** %40, align 8, !dbg !48 - store i64 %21, i64* %41, align 8, !dbg !48, !tbaa !6 - %42 = getelementptr inbounds i64, i64* %41, i64 1, !dbg !48 - store i64 %37, i64* %42, align 8, !dbg !48, !tbaa !6 - %43 = getelementptr inbounds i64, i64* %42, i64 1, !dbg !48 - store i64* %43, i64** %40, align 8, !dbg !48 - %send = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_extend, i64 0), !dbg !48 - store i64* getelementptr inbounds ([19 x i64], [19 x i64]* @iseqEncodedArray, i64 0, i64 9), i64** %32, align 8, !dbg !48, !tbaa !15 - %44 = load i64, i64* @guard_epoch_Foo, align 8, !dbg !26 - %45 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !26, !tbaa !49 - %needTakeSlowPath1 = icmp ne i64 %44, %45, !dbg !26 - br i1 %needTakeSlowPath1, label %46, label %47, !dbg !26, !prof !50 - -46: ; preds = %36 - call void @const_recompute_Foo(), !dbg !26 - br label %47, !dbg !26 - -47: ; preds = %36, %46 - %48 = load i64, i64* @guarded_const_Foo, align 8, !dbg !26 - %49 = load i64, i64* @guard_epoch_Foo, align 8, !dbg !26 - %50 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !26, !tbaa !49 - %guardUpdated2 = icmp eq i64 %49, %50, !dbg !26 - call void @llvm.assume(i1 %guardUpdated2), !dbg !26 - %stackFrame42.i.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Foo#3bar", align 8, !dbg !26 - %51 = call noalias nonnull i8* @ruby_xcalloc(i64 noundef 1, i64 noundef 64) #18, !dbg !26 - %52 = bitcast i8* %51 to i16*, !dbg !26 - %53 = load i16, i16* %52, align 8, !dbg !26 - %54 = and i16 %53, -384, !dbg !26 - %55 = or i16 %54, 65, !dbg !26 - store i16 %55, i16* %52, align 8, !dbg !26 - %56 = getelementptr inbounds i8, i8* %51, i64 8, !dbg !26 - %57 = bitcast i8* %56 to i32*, !dbg !26 - store i32 1, i32* %57, align 8, !dbg !26, !tbaa !51 - %58 = getelementptr inbounds i8, i8* %51, i64 12, !dbg !26 - %59 = getelementptr inbounds i8, i8* %51, i64 28, !dbg !26 - %60 = bitcast i8* %59 to i32*, !dbg !26 - call void @llvm.memset.p0i8.i64(i8* nonnull align 4 %58, i8 0, i64 16, i1 false) #17, !dbg !26 - store i32 1, i32* %60, align 4, !dbg !26, !tbaa !54 - %61 = getelementptr inbounds i8, i8* %51, i64 4, !dbg !26 - %62 = bitcast i8* %61 to i32*, !dbg !26 - store i32 2, i32* %62, align 4, !dbg !26, !tbaa !55 - store i64 %rubyId_x.i, i64* %positional_table.i.i, align 8, !dbg !26 - %63 = getelementptr i64, i64* %positional_table.i.i, i32 1, !dbg !26 - store i64 %rubyId_blk.i, i64* %63, align 8, !dbg !26 - %64 = call noalias nonnull i8* @ruby_xmalloc2(i64 noundef 2, i64 noundef 8) #18, !dbg !26 - call void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture nonnull writeonly align 1 %64, i8* nocapture noundef nonnull readonly align 8 dereferenceable(16) %23, i64 noundef 16, i1 noundef false) #17, !dbg !26 - %65 = getelementptr inbounds i8, i8* %51, i64 32, !dbg !26 - %66 = bitcast i8* %65 to i8**, !dbg !26 - store i8* %64, i8** %66, align 8, !dbg !26, !tbaa !56 - call void @sorbet_vm_define_method(i64 %48, i8* getelementptr inbounds ([247 x i8], [247 x i8]* @sorbet_moduleStringTable, i64 0, i64 115), i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)* noundef @"func_Foo#3bar", i8* nonnull %51, %struct.rb_iseq_struct* %stackFrame42.i.i, i1 noundef zeroext false) #17, !dbg !26 - call void @llvm.lifetime.end.p0i8(i64 16, i8* nonnull %23) #17 - call void @sorbet_popFrame() #17, !dbg !45 - store i64* getelementptr inbounds ([19 x i64], [19 x i64]* @iseqEncodedArray, i64 0, i64 15), i64** %19, align 8, !dbg !45, !tbaa !15 - %67 = call i64 @sorbet_maybeAllocateObjectFastPath(i64 %48, %struct.FunctionInlineCache* noundef @ic_new) #17, !dbg !29 - %68 = icmp eq i64 %67, 52, !dbg !29 - br i1 %68, label %slowNew.i, label %fastNew.i, !dbg !29 - -slowNew.i: ; preds = %47 - %69 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %13, i64 0, i32 1, !dbg !29 - %70 = load i64*, i64** %69, align 8, !dbg !29 - store i64 %48, i64* %70, align 8, !dbg !29, !tbaa !6 - %71 = getelementptr inbounds i64, i64* %70, i64 1, !dbg !29 - store i64* %71, i64** %69, align 8, !dbg !29 - %72 = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* noundef @ic_new, i64 noundef 0) #17, !dbg !29 - br label %"func_.13.exit", !dbg !29 - -fastNew.i: ; preds = %47 - %73 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %13, i64 0, i32 1, !dbg !29 - %74 = load i64*, i64** %73, align 8, !dbg !29 - store i64 %67, i64* %74, align 8, !dbg !29, !tbaa !6 - %75 = getelementptr inbounds i64, i64* %74, i64 1, !dbg !29 - store i64* %75, i64** %73, align 8, !dbg !29 - %76 = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* noundef @ic_initialize, i64 noundef 0) #17, !dbg !29 - br label %"func_.13.exit", !dbg !29 - -"func_.13.exit": ; preds = %slowNew.i, %fastNew.i - %initializedObject.i = phi i64 [ %72, %slowNew.i ], [ %67, %fastNew.i ], !dbg !29 - %77 = load i64, i64* @"func_.13$block_1_ifunc", align 8, !dbg !29 - %78 = call %struct.vm_ifunc* @sorbet_globalConstFetchIfunc(i64 %77) #17, !dbg !29 - %79 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %13, i64 0, i32 1, !dbg !29 - %80 = load i64*, i64** %79, align 8, !dbg !29 - store i64 %initializedObject.i, i64* %80, align 8, !dbg !29, !tbaa !6 - %81 = getelementptr inbounds i64, i64* %80, i64 1, !dbg !29 - store i64 11, i64* %81, align 8, !dbg !29, !tbaa !6 - %82 = getelementptr inbounds i64, i64* %81, i64 1, !dbg !29 - store i64* %82, i64** %79, align 8, !dbg !29 - %83 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !29, !tbaa !15 - %84 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %83, i64 0, i32 2, !dbg !29 - %85 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %84, align 8, !dbg !29, !tbaa !17 - %86 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %85, i64 0, i32 3, !dbg !29 - %87 = bitcast i64* %86 to %struct.rb_captured_block*, !dbg !29 - %88 = getelementptr inbounds i64, i64* %86, i64 2, !dbg !29 - %89 = bitcast i64* %88 to %struct.vm_ifunc**, !dbg !29 - store %struct.vm_ifunc* %78, %struct.vm_ifunc** %89, align 8, !dbg !29, !tbaa !57 - %90 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %83, i64 0, i32 17, !dbg !29 - store i64 0, i64* %90, align 8, !dbg !29, !tbaa !58 - call void @llvm.experimental.noalias.scope.decl(metadata !59) #17, !dbg !29 - %91 = ptrtoint %struct.rb_captured_block* %87 to i64, !dbg !29 - %92 = or i64 %91, 3, !dbg !29 - %93 = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_bar, i64 %92) #17, !dbg !29 - store i64* getelementptr inbounds ([19 x i64], [19 x i64]* @iseqEncodedArray, i64 0, i64 18), i64** %19, align 8, !dbg !29, !tbaa !15 - %94 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %13, i64 0, i32 1, !dbg !30 - %95 = load i64*, i64** %94, align 8, !dbg !30 - store i64 %10, i64* %95, align 8, !dbg !30, !tbaa !6 - %96 = getelementptr inbounds i64, i64* %95, i64 1, !dbg !30 - store i64 %93, i64* %96, align 8, !dbg !30, !tbaa !6 - %97 = getelementptr inbounds i64, i64* %96, i64 1, !dbg !30 - store i64* %97, i64** %94, align 8, !dbg !30 - %send4 = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_p, i64 0), !dbg !30 - ret void -} - -; Function Attrs: nounwind sspreq uwtable -define internal i64 @"func_Foo#3bar"(i32 %argc, i64* nocapture readonly %argArray, i64 %selfRaw, %struct.rb_control_frame_struct* nonnull align 8 dereferenceable(8) %cfp, i8* nocapture nofree readnone %calling, i8* nocapture nofree readnone %callData) #11 !dbg !62 { -functionEntryInitializers: - %0 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 0 - store i64* getelementptr inbounds ([19 x i64], [19 x i64]* @iseqEncodedArray, i64 0, i64 9), i64** %0, align 8, !tbaa !15 - %tooManyArgs = icmp ugt i32 %argc, 1, !dbg !63 - %tooFewArgs = icmp ult i32 %argc, 1, !dbg !63 - %or.cond = or i1 %tooManyArgs, %tooFewArgs, !dbg !63 - br i1 %or.cond, label %argCountFailBlock, label %fillRequiredArgs, !dbg !63, !prof !64 - -argCountFailBlock: ; preds = %functionEntryInitializers - tail call void @sorbet_raiseArity(i32 %argc, i32 noundef 1, i32 noundef 1) #19, !dbg !63 - unreachable, !dbg !63 - -fillRequiredArgs: ; preds = %functionEntryInitializers - %rawArg_x = load i64, i64* %argArray, align 8, !dbg !63 - %1 = and i64 %rawArg_x, 1, !dbg !65 - %2 = icmp eq i64 %1, 0, !dbg !65 - br i1 %2, label %3, label %typeTestSuccess, !dbg !65, !prof !66 - -3: ; preds = %fillRequiredArgs - %4 = and i64 %rawArg_x, 7, !dbg !65 - %5 = icmp ne i64 %4, 0, !dbg !65 - %6 = and i64 %rawArg_x, -9, !dbg !65 - %7 = icmp eq i64 %6, 0, !dbg !65 - %8 = or i1 %5, %7, !dbg !65 - br i1 %8, label %codeRepl20, label %sorbet_isa_Integer.exit, !dbg !65 - -sorbet_isa_Integer.exit: ; preds = %3 - %9 = inttoptr i64 %rawArg_x to %struct.iseq_inline_iv_cache_entry*, !dbg !65 - %10 = getelementptr inbounds %struct.iseq_inline_iv_cache_entry, %struct.iseq_inline_iv_cache_entry* %9, i64 0, i32 0, !dbg !65 - %11 = load i64, i64* %10, align 8, !dbg !65, !tbaa !67 - %12 = and i64 %11, 31, !dbg !65 - %13 = icmp eq i64 %12, 10, !dbg !65 - br i1 %13, label %typeTestSuccess, label %codeRepl20, !dbg !65, !prof !69 - -typeTestSuccess: ; preds = %fillRequiredArgs, %sorbet_isa_Integer.exit - store i64* getelementptr inbounds ([19 x i64], [19 x i64]* @iseqEncodedArray, i64 0, i64 10), i64** %0, align 8, !dbg !70, !tbaa !15 - %rawBlockSendResult = tail call i64 @sorbet_vm_callBlock(%struct.rb_control_frame_struct* nonnull %cfp, i32 noundef 0, i64* noundef null, i32 noundef 0), !dbg !71 - store i64* getelementptr inbounds ([19 x i64], [19 x i64]* @iseqEncodedArray, i64 0, i64 11), i64** %0, align 8, !dbg !71, !tbaa !15 - tail call void @llvm.experimental.noalias.scope.decl(metadata !72), !dbg !75 - %14 = and i64 %rawArg_x, 1, !dbg !75 - %15 = and i64 %14, %rawBlockSendResult, !dbg !75 - %16 = icmp eq i64 %15, 0, !dbg !75 - br i1 %16, label %26, label %17, !dbg !75, !prof !64 - -17: ; preds = %typeTestSuccess - %18 = add nsw i64 %rawBlockSendResult, -1, !dbg !75 - %19 = tail call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %rawArg_x, i64 %18) #20, !dbg !75 - %20 = extractvalue { i64, i1 } %19, 1, !dbg !75 - %21 = extractvalue { i64, i1 } %19, 0, !dbg !75 - br i1 %20, label %22, label %sorbet_rb_int_plus.exit, !dbg !75 - -22: ; preds = %17 - %23 = ashr i64 %21, 1, !dbg !75 - %24 = xor i64 %23, -9223372036854775808, !dbg !75 - %25 = tail call i64 @rb_int2big(i64 %24) #17, !dbg !75, !noalias !72 - br label %sorbet_rb_int_plus.exit, !dbg !75 - -26: ; preds = %typeTestSuccess - %27 = tail call i64 @sorbet_rb_int_plus_slowpath(i64 %rawArg_x, i64 %rawBlockSendResult) #17, !dbg !75, !noalias !72 - br label %sorbet_rb_int_plus.exit, !dbg !75 - -sorbet_rb_int_plus.exit: ; preds = %22, %17, %26 - %28 = phi i64 [ %27, %26 ], [ %25, %22 ], [ %21, %17 ], !dbg !75 - %29 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !75, !tbaa !15 - %30 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %29, i64 0, i32 5, !dbg !75 - %31 = load i32, i32* %30, align 8, !dbg !75, !tbaa !76 - %32 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %29, i64 0, i32 6, !dbg !75 - %33 = load i32, i32* %32, align 4, !dbg !75, !tbaa !77 - %34 = xor i32 %33, -1, !dbg !75 - %35 = and i32 %34, %31, !dbg !75 - %36 = icmp eq i32 %35, 0, !dbg !75 - br i1 %36, label %rb_vm_check_ints.exit, label %37, !dbg !75, !prof !69 - -37: ; preds = %sorbet_rb_int_plus.exit - %38 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %29, i64 0, i32 8, !dbg !75 - %39 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %38, align 8, !dbg !75, !tbaa !78 - %40 = tail call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %39, i32 noundef 0) #17, !dbg !75 - br label %rb_vm_check_ints.exit, !dbg !75 - -rb_vm_check_ints.exit: ; preds = %sorbet_rb_int_plus.exit, %37 - %41 = and i64 %28, 1 - %42 = icmp eq i64 %41, 0 - br i1 %42, label %43, label %typeTestSuccess13, !prof !66 - -43: ; preds = %rb_vm_check_ints.exit - %44 = and i64 %28, 7 - %45 = icmp ne i64 %44, 0 - %46 = and i64 %28, -9 - %47 = icmp eq i64 %46, 0 - %48 = or i1 %45, %47 - br i1 %48, label %codeRepl, label %sorbet_isa_Integer.exit21 - -sorbet_isa_Integer.exit21: ; preds = %43 - %49 = inttoptr i64 %28 to %struct.iseq_inline_iv_cache_entry* - %50 = getelementptr inbounds %struct.iseq_inline_iv_cache_entry, %struct.iseq_inline_iv_cache_entry* %49, i64 0, i32 0 - %51 = load i64, i64* %50, align 8, !tbaa !67 - %52 = and i64 %51, 31 - %53 = icmp eq i64 %52, 10 - br i1 %53, label %typeTestSuccess13, label %codeRepl, !prof !69 - -codeRepl20: ; preds = %3, %sorbet_isa_Integer.exit - tail call fastcc void @"func_Foo#3bar.cold.2"(i64 %rawArg_x) #21, !dbg !65 - unreachable - -typeTestSuccess13: ; preds = %rb_vm_check_ints.exit, %sorbet_isa_Integer.exit21 - ret i64 %28 - -codeRepl: ; preds = %43, %sorbet_isa_Integer.exit21 - tail call fastcc void @"func_Foo#3bar.cold.1"(i64 %28) #21, !dbg !79 - unreachable -} - -; Function Attrs: ssp -define internal i64 @"func_Foo.13L62$block_1"(i64 %firstYieldArgRaw, i64 %localsOffset, i32 %argc, i64* nocapture nofree readnone %argArray, i64 %blockArg) #12 !dbg !32 { -functionEntryInitializers: - %0 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !15 - %1 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %0, i64 0, i32 2 - %2 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %1, align 8, !tbaa !17 - %3 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 3 - %4 = load i64, i64* %3, align 8, !tbaa !80 - %stackFrame = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Foo.13$block_1", align 8 - %5 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame, %struct.rb_iseq_struct** %5, align 8, !tbaa !21 - %6 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 4 - %7 = load i64*, i64** %6, align 8, !tbaa !23 - %8 = load i64, i64* %7, align 8, !tbaa !6 - %9 = and i64 %8, -129 - store i64 %9, i64* %7, align 8, !tbaa !6 - %10 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 0 - store i64* getelementptr inbounds ([19 x i64], [19 x i64]* @iseqEncodedArray, i64 0, i64 8), i64** %10, align 8, !tbaa !15 - %11 = load i64, i64* @guard_epoch_T, align 8, !dbg !31 - %12 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !31, !tbaa !49 - %needTakeSlowPath = icmp ne i64 %11, %12, !dbg !31 - br i1 %needTakeSlowPath, label %13, label %14, !dbg !31, !prof !50 - -13: ; preds = %functionEntryInitializers - tail call void @const_recompute_T(), !dbg !31 - br label %14, !dbg !31 - -14: ; preds = %functionEntryInitializers, %13 - %15 = load i64, i64* @guarded_const_T, align 8, !dbg !31 - %16 = load i64, i64* @guard_epoch_T, align 8, !dbg !31 - %17 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !31, !tbaa !49 - %guardUpdated = icmp eq i64 %16, %17, !dbg !31 - tail call void @llvm.assume(i1 %guardUpdated), !dbg !31 - %18 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 1, !dbg !31 - %19 = load i64*, i64** %18, align 8, !dbg !31 - store i64 %15, i64* %19, align 8, !dbg !31, !tbaa !6 - %20 = getelementptr inbounds i64, i64* %19, i64 1, !dbg !31 - store i64* %20, i64** %18, align 8, !dbg !31 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_proc, i64 0), !dbg !31 - %21 = load i64, i64* @rb_cInteger, align 8, !dbg !31 - %22 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 1, !dbg !31 - %23 = load i64*, i64** %22, align 8, !dbg !31 - store i64 %send, i64* %23, align 8, !dbg !31, !tbaa !6 - %24 = getelementptr inbounds i64, i64* %23, i64 1, !dbg !31 - store i64 %21, i64* %24, align 8, !dbg !31, !tbaa !6 - %25 = getelementptr inbounds i64, i64* %24, i64 1, !dbg !31 - store i64* %25, i64** %22, align 8, !dbg !31 - %send41 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_returns, i64 0), !dbg !31 - %26 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 1, !dbg !33 - %27 = load i64*, i64** %26, align 8, !dbg !33 - store i64 %4, i64* %27, align 8, !dbg !33, !tbaa !6 - %28 = getelementptr inbounds i64, i64* %27, i64 1, !dbg !33 - store i64 %21, i64* %28, align 8, !dbg !33, !tbaa !6 - %29 = getelementptr inbounds i64, i64* %28, i64 1, !dbg !33 - store i64 %send41, i64* %29, align 8, !dbg !33, !tbaa !6 - %30 = getelementptr inbounds i64, i64* %29, i64 1, !dbg !33 - store i64* %30, i64** %26, align 8, !dbg !33 - %send43 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_params, i64 0), !dbg !33 - %31 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 1, !dbg !33 - %32 = load i64*, i64** %31, align 8, !dbg !33 - store i64 %send43, i64* %32, align 8, !dbg !33, !tbaa !6 - %33 = getelementptr inbounds i64, i64* %32, i64 1, !dbg !33 - store i64 %21, i64* %33, align 8, !dbg !33, !tbaa !6 - %34 = getelementptr inbounds i64, i64* %33, i64 1, !dbg !33 - store i64* %34, i64** %31, align 8, !dbg !33 - %send45 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_returns.1, i64 0), !dbg !33 - ret i64 %send45, !dbg !33 -} - -; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly -declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #13 - -; Function Attrs: cold minsize noreturn nounwind sspreq uwtable -define internal fastcc void @"func_Foo#3bar.cold.1"(i64 %0) unnamed_addr #14 !dbg !81 { -newFuncRoot: - tail call void @sorbet_cast_failure(i64 %0, i8* getelementptr inbounds ([247 x i8], [247 x i8]* @sorbet_moduleStringTable, i64 0, i64 152), i8* getelementptr inbounds ([247 x i8], [247 x i8]* @sorbet_moduleStringTable, i64 0, i64 142)) #19 - unreachable -} - -; Function Attrs: cold minsize noreturn nounwind sspreq uwtable -define internal fastcc void @"func_Foo#3bar.cold.2"(i64 %rawArg_x) unnamed_addr #14 !dbg !83 { -newFuncRoot: - tail call void @sorbet_cast_failure(i64 %rawArg_x, i8* getelementptr inbounds ([247 x i8], [247 x i8]* @sorbet_moduleStringTable, i64 0, i64 128), i8* getelementptr inbounds ([247 x i8], [247 x i8]* @sorbet_moduleStringTable, i64 0, i64 142)) #19, !dbg !84 - unreachable, !dbg !84 -} - -; Function Attrs: nofree nosync nounwind willreturn -declare void @llvm.assume(i1 noundef) #15 - -; Function Attrs: ssp -define linkonce void @"const_recompute_T::Sig"() local_unnamed_addr #12 { - %1 = tail call i64 @sorbet_getConstant(i8* getelementptr inbounds ([247 x i8], [247 x i8]* @sorbet_moduleStringTable, i64 0, i64 226), i64 6) - store i64 %1, i64* @"guarded_const_T::Sig", align 8 - %2 = load i64, i64* @ruby_vm_global_constant_state, align 8, !tbaa !49 - store i64 %2, i64* @"guard_epoch_T::Sig", align 8 - ret void -} - -; Function Attrs: ssp -define linkonce void @const_recompute_Foo() local_unnamed_addr #12 { - %1 = tail call i64 @sorbet_getConstant(i8* getelementptr inbounds ([247 x i8], [247 x i8]* @sorbet_moduleStringTable, i64 0, i64 89), i64 3) - store i64 %1, i64* @guarded_const_Foo, align 8 - %2 = load i64, i64* @ruby_vm_global_constant_state, align 8, !tbaa !49 - store i64 %2, i64* @guard_epoch_Foo, align 8 - ret void -} - -; Function Attrs: ssp -define linkonce void @const_recompute_T() local_unnamed_addr #12 { - %1 = tail call i64 @sorbet_getConstant(i8* getelementptr inbounds ([247 x i8], [247 x i8]* @sorbet_moduleStringTable, i64 0, i64 209), i64 1) - store i64 %1, i64* @guarded_const_T, align 8 - %2 = load i64, i64* @ruby_vm_global_constant_state, align 8, !tbaa !49 - store i64 %2, i64* @guard_epoch_T, align 8 - ret void -} - -attributes #0 = { nounwind readnone willreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #1 = { cold noreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #2 = { noreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #3 = { "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #4 = { argmemonly nofree nosync nounwind willreturn } -attributes #5 = { nofree nosync nounwind readnone speculatable willreturn } -attributes #6 = { inaccessiblememonly nofree nosync nounwind willreturn } -attributes #7 = { allocsize(0,1) "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #8 = { nounwind ssp uwtable "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #9 = { nofree nosync nounwind ssp willreturn } -attributes #10 = { sspreq } -attributes #11 = { nounwind sspreq uwtable } -attributes #12 = { ssp } -attributes #13 = { argmemonly nofree nosync nounwind willreturn writeonly } -attributes #14 = { cold minsize noreturn nounwind sspreq uwtable } -attributes #15 = { nofree nosync nounwind willreturn } -attributes #16 = { noreturn nounwind } -attributes #17 = { nounwind } -attributes #18 = { nounwind allocsize(0,1) } -attributes #19 = { noreturn } -attributes #20 = { nounwind willreturn } -attributes #21 = { noinline } - -!llvm.module.flags = !{!0, !1, !2} -!llvm.dbg.cu = !{!3} - -!0 = !{i32 2, !"Debug Info Version", i32 3} -!1 = !{i32 4, !"cf-protection-return", i32 1} -!2 = !{i32 4, !"cf-protection-branch", i32 1} -!3 = distinct !DICompileUnit(language: DW_LANG_C, file: !4, producer: "Sorbet LLVM", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !5) -!4 = !DIFile(filename: "test/testdata/compiler/block_type_checking.rb", directory: ".") -!5 = !{} -!6 = !{!7, !7, i64 0} -!7 = !{!"long", !8, i64 0} -!8 = !{!"omnipotent char", !9, i64 0} -!9 = !{!"Simple C/C++ TBAA"} -!10 = distinct !DISubprogram(name: ".", linkageName: "func_.13$block_1", scope: !11, file: !4, line: 5, type: !12, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!11 = distinct !DISubprogram(name: ".", linkageName: "func_.13", scope: null, file: !4, line: 5, type: !12, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!12 = !DISubroutineType(types: !13) -!13 = !{!14} -!14 = !DIBasicType(name: "VALUE", size: 64, encoding: DW_ATE_signed) -!15 = !{!16, !16, i64 0} -!16 = !{!"any pointer", !8, i64 0} -!17 = !{!18, !16, i64 16} -!18 = !{!"rb_execution_context_struct", !16, i64 0, !7, i64 8, !16, i64 16, !16, i64 24, !16, i64 32, !19, i64 40, !19, i64 44, !16, i64 48, !16, i64 56, !16, i64 64, !7, i64 72, !7, i64 80, !16, i64 88, !7, i64 96, !16, i64 104, !16, i64 112, !7, i64 120, !7, i64 128, !8, i64 136, !8, i64 137, !7, i64 144, !20, i64 152} -!19 = !{!"int", !8, i64 0} -!20 = !{!"", !16, i64 0, !16, i64 8, !7, i64 16, !8, i64 24} -!21 = !{!22, !16, i64 16} -!22 = !{!"rb_control_frame_struct", !16, i64 0, !16, i64 8, !16, i64 16, !7, i64 24, !16, i64 32, !16, i64 40, !16, i64 48} -!23 = !{!22, !16, i64 32} -!24 = !DILocation(line: 15, column: 10, scope: !10) -!25 = !DILocation(line: 16, column: 3, scope: !10) -!26 = !DILocation(line: 9, column: 3, scope: !27, inlinedAt: !28) -!27 = distinct !DISubprogram(name: "Foo.", linkageName: "func_Foo.13L62", scope: null, file: !4, line: 5, type: !12, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!28 = distinct !DILocation(line: 5, column: 1, scope: !11) -!29 = !DILocation(line: 15, column: 10, scope: !11) -!30 = !DILocation(line: 18, column: 1, scope: !11) -!31 = !DILocation(line: 8, column: 32, scope: !32) -!32 = distinct !DISubprogram(name: "Foo.", linkageName: "func_Foo.13L62$block_1", scope: !27, file: !4, line: 5, type: !12, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!33 = !DILocation(line: 8, column: 8, scope: !32) -!34 = !DILocation(line: 6, column: 3, scope: !27) -!35 = !{!36, !7, i64 400} -!36 = !{!"rb_vm_struct", !7, i64 0, !37, i64 8, !16, i64 192, !16, i64 200, !16, i64 208, !40, i64 216, !8, i64 224, !38, i64 264, !38, i64 280, !38, i64 296, !38, i64 312, !7, i64 328, !19, i64 336, !19, i64 340, !19, i64 344, !19, i64 344, !19, i64 344, !19, i64 344, !19, i64 348, !7, i64 352, !8, i64 360, !7, i64 400, !7, i64 408, !7, i64 416, !7, i64 424, !7, i64 432, !7, i64 440, !7, i64 448, !16, i64 456, !16, i64 464, !41, i64 472, !42, i64 992, !16, i64 1016, !16, i64 1024, !19, i64 1032, !19, i64 1036, !38, i64 1040, !8, i64 1056, !7, i64 1096, !7, i64 1104, !7, i64 1112, !7, i64 1120, !7, i64 1128, !19, i64 1136, !16, i64 1144, !16, i64 1152, !16, i64 1160, !16, i64 1168, !16, i64 1176, !16, i64 1184, !19, i64 1192, !43, i64 1200, !8, i64 1232} -!37 = !{!"rb_global_vm_lock_struct", !16, i64 0, !8, i64 8, !38, i64 48, !16, i64 64, !19, i64 72, !8, i64 80, !8, i64 128, !19, i64 176, !19, i64 180} -!38 = !{!"list_head", !39, i64 0} -!39 = !{!"list_node", !16, i64 0, !16, i64 8} -!40 = !{!"long long", !8, i64 0} -!41 = !{!"", !8, i64 0} -!42 = !{!"rb_hook_list_struct", !16, i64 0, !19, i64 8, !19, i64 12, !19, i64 16} -!43 = !{!"", !7, i64 0, !7, i64 8, !7, i64 16, !7, i64 24} -!44 = !DILocation(line: 0, scope: !11) -!45 = !DILocation(line: 5, column: 1, scope: !11) -!46 = !DILocation(line: 0, scope: !27, inlinedAt: !28) -!47 = !DILocation(line: 8, column: 3, scope: !27, inlinedAt: !28) -!48 = !DILocation(line: 6, column: 3, scope: !27, inlinedAt: !28) -!49 = !{!40, !40, i64 0} -!50 = !{!"branch_weights", i32 1, i32 10000} -!51 = !{!52, !19, i64 8} -!52 = !{!"rb_sorbet_param_struct", !53, i64 0, !19, i64 4, !19, i64 8, !19, i64 12, !19, i64 16, !19, i64 20, !19, i64 24, !19, i64 28, !16, i64 32, !19, i64 40, !19, i64 44, !19, i64 48, !19, i64 52, !16, i64 56} -!53 = !{!"", !19, i64 0, !19, i64 0, !19, i64 0, !19, i64 0, !19, i64 0, !19, i64 0, !19, i64 0, !19, i64 0, !19, i64 1, !19, i64 1} -!54 = !{!52, !19, i64 28} -!55 = !{!52, !19, i64 4} -!56 = !{!52, !16, i64 32} -!57 = !{!8, !8, i64 0} -!58 = !{!18, !7, i64 128} -!59 = !{!60} -!60 = distinct !{!60, !61, !"VM_BH_FROM_IFUNC_BLOCK: argument 0"} -!61 = distinct !{!61, !"VM_BH_FROM_IFUNC_BLOCK"} -!62 = distinct !DISubprogram(name: "Foo#bar", linkageName: "func_Foo#3bar", scope: null, file: !4, line: 9, type: !12, scopeLine: 9, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!63 = !DILocation(line: 9, column: 3, scope: !62) -!64 = !{!"branch_weights", i32 4001, i32 4000000} -!65 = !DILocation(line: 9, column: 11, scope: !62) -!66 = !{!"branch_weights", i32 1, i32 2000} -!67 = !{!68, !7, i64 0} -!68 = !{!"RBasic", !7, i64 0, !7, i64 8} -!69 = !{!"branch_weights", i32 2000, i32 1} -!70 = !DILocation(line: 9, column: 15, scope: !62) -!71 = !DILocation(line: 10, column: 9, scope: !62) -!72 = !{!73} -!73 = distinct !{!73, !74, !"sorbet_rb_int_plus: argument 0"} -!74 = distinct !{!74, !"sorbet_rb_int_plus"} -!75 = !DILocation(line: 11, column: 5, scope: !62) -!76 = !{!18, !19, i64 40} -!77 = !{!18, !19, i64 44} -!78 = !{!18, !16, i64 56} -!79 = !DILocation(line: 0, scope: !62) -!80 = !{!22, !7, i64 24} -!81 = distinct !DISubprogram(name: "func_Foo#3bar.cold.1", linkageName: "func_Foo#3bar.cold.1", scope: null, file: !4, type: !82, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !3, retainedNodes: !5) -!82 = !DISubroutineType(types: !5) -!83 = distinct !DISubprogram(name: "func_Foo#3bar.cold.2", linkageName: "func_Foo#3bar.cold.2", scope: null, file: !4, type: !82, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !3, retainedNodes: !5) -!84 = !DILocation(line: 9, column: 11, scope: !83) diff --git a/test/testdata/compiler/boolean_ops.opt.ll.exp b/test/testdata/compiler/boolean_ops.opt.ll.exp deleted file mode 100644 index 349e6329a8..0000000000 --- a/test/testdata/compiler/boolean_ops.opt.ll.exp +++ /dev/null @@ -1,266 +0,0 @@ -; ModuleID = 'payload' -source_filename = "llvm-link" -target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } -%struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.__pthread_cond_s } -%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } -%union.anon = type { i64 } -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } -%struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } -%struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } -%struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } -%struct.anon.0 = type { i16, [2 x i8] } -%struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } -%struct.rb_iseq_location_struct = type { i64, i64, i64, i64, i32, %struct.rb_code_location_struct } -%struct.rb_code_location_struct = type { %struct.rb_code_position_struct, %struct.rb_code_position_struct } -%struct.rb_code_position_struct = type { i32, i32 } -%struct.iseq_insn_info = type { %struct.iseq_insn_info_entry*, i32*, i32, %struct.succ_index_table* } -%struct.iseq_insn_info_entry = type opaque -%struct.succ_index_table = type opaque -%struct.iseq_catch_table = type opaque -%union.iseq_inline_storage_entry = type { %struct.iseq_inline_cache_entry } -%struct.iseq_inline_cache_entry = type { i64, %struct.rb_cref_struct*, i64 } -%struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } -%struct.rb_scope_visi_struct = type { i8, [3 x i8] } -%struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } -%struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } -%union.anon.13 = type { %struct.rb_method_cfunc_struct } -%struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%union.anon.15 = type { i32 } -%struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.16 = type { i64, i64, i64, i64* } -%union.anon.17 = type { %struct.anon.18 } -%struct.anon.18 = type { i64, i32 } -%struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } -%struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } -%struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } -%struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } -%struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } -%struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } -%struct.__sigset_t = type { [16 x i64] } -%struct.rb_calling_info = type { i64, i64, i32, i32 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } -%union.anon.9 = type { %union.pthread_cond_t } -%struct.rb_unblock_callback = type { void (i8*)*, i8* } -%struct.rb_mutex_struct = type opaque -%struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.10 = type { %struct.anon.11 } -%struct.anon.11 = type { i64, i64, i32 } -%struct.rb_fiber_struct = type opaque -%struct.anon.5 = type { [65 x i64] } -%struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } -%struct.rb_event_hook_struct = type opaque -%struct.rb_postponed_job_struct = type opaque -%struct.list_head = type { %struct.list_node } -%union.pthread_mutex_t = type { %struct.__pthread_mutex_s } -%struct.__pthread_mutex_s = type { i32, i32, i32, i32, i32, i16, i16, %struct.__pthread_internal_list } -%struct.__pthread_internal_list = type { %struct.__pthread_internal_list*, %struct.__pthread_internal_list* } -%struct.rb_objspace = type opaque -%struct.rb_at_exit_list = type { void (%struct.rb_vm_struct*)*, %struct.rb_at_exit_list* } -%struct.st_table = type { i8, i8, i8, i32, %struct.st_hash_type*, i64, i64*, i64, i64, %struct.st_table_entry* } -%struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } -%struct.st_table_entry = type opaque -%struct.rb_builtin_function = type opaque -%struct.anon.6 = type { i64, i64, i64, i64 } -%struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } -%struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } -%struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } -%struct.rb_call_info_with_kwarg = type { %struct.rb_call_info, %struct.rb_call_info_kw_arg* } -%struct.rb_call_info_kw_arg = type { i32, [1 x i64] } - -@ruby_current_vm_ptr = external local_unnamed_addr global %struct.rb_vm_struct*, align 8 -@ruby_current_execution_context_ptr = external local_unnamed_addr global %struct.rb_execution_context_struct*, align 8 -@rb_eRuntimeError = external local_unnamed_addr global i64, align 8 -@.str.9 = private unnamed_addr constant [95 x i8] c"sorbet_getBuildSCMRevision: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@.str.10 = private unnamed_addr constant [93 x i8] c"sorbet_getIsReleaseBuild: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@"stackFramePrecomputed_func_.13" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@iseqEncodedArray = internal global [6 x i64] zeroinitializer -@fileLineNumberInfo = internal global %struct.SorbetLineNumberInfo zeroinitializer -@"ic_!" = internal global %struct.FunctionInlineCache zeroinitializer -@ic_puts = internal global %struct.FunctionInlineCache zeroinitializer -@sorbet_moduleStringTable = internal unnamed_addr constant [71 x i8] c"\00test/testdata/compiler/boolean_ops.rb\00>\00!\00puts\00master\00", align 1 -@sorbet_moduleIDTable = internal unnamed_addr global [4 x i64] zeroinitializer, align 8 -@sorbet_moduleIDDescriptors = internal unnamed_addr constant [4 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 16 }, %struct.rb_code_position_struct { i32 55, i32 1 }, %struct.rb_code_position_struct { i32 57, i32 1 }, %struct.rb_code_position_struct { i32 59, i32 4 }], align 8 -@sorbet_moduleRubyStringTable = internal unnamed_addr global [2 x i64] zeroinitializer, align 8 -@sorbet_moduleRubyStringDescriptors = internal unnamed_addr constant [2 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 16 }, %struct.rb_code_position_struct { i32 17, i32 37 }], align 8 - -declare %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64, i64, i64, i64, %struct.rb_iseq_struct*, i32, i32, %struct.SorbetLineNumberInfo*, i64*, i32, i32) local_unnamed_addr #0 - -declare void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo*, i64*, i32) local_unnamed_addr #0 - -declare i64 @sorbet_readRealpath() local_unnamed_addr #0 - -declare void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) local_unnamed_addr #0 - -declare i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache*, i64) local_unnamed_addr #0 - -declare void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_iseq_struct*) local_unnamed_addr #0 - -declare void @sorbet_vm_intern_ids(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #0 - -declare void @sorbet_vm_init_string_table(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #0 - -declare i64 @sorbet_vm_bang(%struct.FunctionInlineCache*, %struct.rb_control_frame_struct*, i64) local_unnamed_addr #0 - -; Function Attrs: noreturn -declare void @rb_raise(i64, i8*, ...) local_unnamed_addr #1 - -; Function Attrs: inaccessiblememonly nofree nosync nounwind willreturn -declare void @llvm.experimental.noalias.scope.decl(metadata) #2 - -declare i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct*, i32) local_unnamed_addr #0 - -; Function Attrs: nounwind ssp uwtable -define weak i32 @sorbet_getIsReleaseBuild() local_unnamed_addr #3 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([93 x i8], [93 x i8]* @.str.10, i64 0, i64 0)) #6 - unreachable -} - -; Function Attrs: nounwind ssp uwtable -define weak i8* @sorbet_getBuildSCMRevision() local_unnamed_addr #3 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([95 x i8], [95 x i8]* @.str.9, i64 0, i64 0)) #6 - unreachable -} - -define internal fastcc void @sorbet_globalConstructors(i64 %realpath) unnamed_addr { -allocRubyIds: - tail call void @sorbet_vm_intern_ids(i64* noundef getelementptr inbounds ([4 x i64], [4 x i64]* @sorbet_moduleIDTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([4 x %struct.rb_code_position_struct], [4 x %struct.rb_code_position_struct]* @sorbet_moduleIDDescriptors, i32 0, i32 0), i32 noundef 4, i8* noundef getelementptr inbounds ([71 x i8], [71 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_vm_init_string_table(i64* noundef getelementptr inbounds ([2 x i64], [2 x i64]* @sorbet_moduleRubyStringTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([2 x %struct.rb_code_position_struct], [2 x %struct.rb_code_position_struct]* @sorbet_moduleRubyStringDescriptors, i32 0, i32 0), i32 noundef 2, i8* noundef getelementptr inbounds ([71 x i8], [71 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef getelementptr inbounds ([6 x i64], [6 x i64]* @iseqEncodedArray, i32 0, i32 0), i32 noundef 6) - tail call fastcc void @"Constr_stackFramePrecomputed_func_.13"(i64 %realpath) - %"rubyId_!" = load i64, i64* getelementptr inbounds ([4 x i64], [4 x i64]* @sorbet_moduleIDTable, i64 0, i64 2), align 8, !dbg !10, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @"ic_!", i64 %"rubyId_!", i32 noundef 16, i32 noundef 0, i32 noundef 0), !dbg !10 - %rubyId_puts = load i64, i64* getelementptr inbounds ([4 x i64], [4 x i64]* @sorbet_moduleIDTable, i64 0, i64 3), align 8, !dbg !15, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts, i64 %rubyId_puts, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !15 - ret void -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_.13"(i64 %realpath) unnamed_addr #4 { -entryInitializers: - %"rubyId_" = load i64, i64* getelementptr inbounds ([4 x i64], [4 x i64]* @sorbet_moduleIDTable, i64 0, i64 0), align 8, !invariant.load !5 - %"rubyStr_" = load i64, i64* getelementptr inbounds ([2 x i64], [2 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 0), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/boolean_ops.rb" = load i64, i64* getelementptr inbounds ([2 x i64], [2 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_", i64 %"rubyId_", i64 %"rubyStr_test/testdata/compiler/boolean_ops.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 0, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 2) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - ret void -} - -; Function Attrs: sspreq -define void @Init_boolean_ops() local_unnamed_addr #5 { -entry: - %realpath = tail call i64 @sorbet_readRealpath() - tail call fastcc void @sorbet_globalConstructors(i64 %realpath) - %0 = load %struct.rb_vm_struct*, %struct.rb_vm_struct** @ruby_current_vm_ptr, align 8, !tbaa !16 - %1 = getelementptr inbounds %struct.rb_vm_struct, %struct.rb_vm_struct* %0, i64 0, i32 18 - %2 = load i64, i64* %1, align 8, !tbaa !18 - %3 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !16 - %4 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %3, i64 0, i32 2 - %5 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %4, align 8, !tbaa !28 - %stackFrame.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - %6 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame.i, %struct.rb_iseq_struct** %6, align 8, !tbaa !31 - %7 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 4 - %8 = load i64*, i64** %7, align 8, !tbaa !33 - %9 = load i64, i64* %8, align 8, !tbaa !6 - %10 = and i64 %9, -33 - store i64 %10, i64* %8, align 8, !tbaa !6 - tail call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %3, %struct.rb_control_frame_struct* %5, %struct.rb_iseq_struct* %stackFrame.i) #7 - %11 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 0 - store i64* getelementptr inbounds ([6 x i64], [6 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %11, align 8, !dbg !34, !tbaa !16 - tail call void @llvm.experimental.noalias.scope.decl(metadata !35) #7, !dbg !38 - %12 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !38, !tbaa !16 - %13 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %12, i64 0, i32 5, !dbg !38 - %14 = load i32, i32* %13, align 8, !dbg !38, !tbaa !39 - %15 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %12, i64 0, i32 6, !dbg !38 - %16 = load i32, i32* %15, align 4, !dbg !38, !tbaa !40 - %17 = xor i32 %16, -1, !dbg !38 - %18 = and i32 %17, %14, !dbg !38 - %19 = icmp eq i32 %18, 0, !dbg !38 - br i1 %19, label %"func_.13.exit", label %20, !dbg !38, !prof !41 - -20: ; preds = %entry - %21 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %12, i64 0, i32 8, !dbg !38 - %22 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %21, align 8, !dbg !38, !tbaa !42 - %23 = tail call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %22, i32 noundef 0) #7, !dbg !38 - br label %"func_.13.exit", !dbg !38 - -"func_.13.exit": ; preds = %entry, %20 - %24 = tail call i64 @sorbet_vm_bang(%struct.FunctionInlineCache* noundef @"ic_!", %struct.rb_control_frame_struct* nonnull %5, i64 noundef 0) #7, !dbg !10 - %25 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !15 - %26 = load i64*, i64** %25, align 8, !dbg !15 - store i64 %2, i64* %26, align 8, !dbg !15, !tbaa !6 - %27 = getelementptr inbounds i64, i64* %26, i64 1, !dbg !15 - store i64 %24, i64* %27, align 8, !dbg !15, !tbaa !6 - %28 = getelementptr inbounds i64, i64* %27, i64 1, !dbg !15 - store i64* %28, i64** %25, align 8, !dbg !15 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts, i64 0), !dbg !15 - ret void -} - -attributes #0 = { "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #1 = { noreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #2 = { inaccessiblememonly nofree nosync nounwind willreturn } -attributes #3 = { nounwind ssp uwtable "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #4 = { ssp } -attributes #5 = { sspreq } -attributes #6 = { noreturn nounwind } -attributes #7 = { nounwind } - -!llvm.module.flags = !{!0, !1, !2} -!llvm.dbg.cu = !{!3} - -!0 = !{i32 2, !"Debug Info Version", i32 3} -!1 = !{i32 4, !"cf-protection-return", i32 1} -!2 = !{i32 4, !"cf-protection-branch", i32 1} -!3 = distinct !DICompileUnit(language: DW_LANG_C, file: !4, producer: "Sorbet LLVM", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !5) -!4 = !DIFile(filename: "test/testdata/compiler/boolean_ops.rb", directory: ".") -!5 = !{} -!6 = !{!7, !7, i64 0} -!7 = !{!"long", !8, i64 0} -!8 = !{!"omnipotent char", !9, i64 0} -!9 = !{!"Simple C/C++ TBAA"} -!10 = !DILocation(line: 5, column: 6, scope: !11) -!11 = distinct !DISubprogram(name: ".", linkageName: "func_.13", scope: null, file: !4, line: 5, type: !12, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!12 = !DISubroutineType(types: !13) -!13 = !{!14} -!14 = !DIBasicType(name: "VALUE", size: 64, encoding: DW_ATE_signed) -!15 = !DILocation(line: 5, column: 1, scope: !11) -!16 = !{!17, !17, i64 0} -!17 = !{!"any pointer", !8, i64 0} -!18 = !{!19, !7, i64 400} -!19 = !{!"rb_vm_struct", !7, i64 0, !20, i64 8, !17, i64 192, !17, i64 200, !17, i64 208, !24, i64 216, !8, i64 224, !21, i64 264, !21, i64 280, !21, i64 296, !21, i64 312, !7, i64 328, !23, i64 336, !23, i64 340, !23, i64 344, !23, i64 344, !23, i64 344, !23, i64 344, !23, i64 348, !7, i64 352, !8, i64 360, !7, i64 400, !7, i64 408, !7, i64 416, !7, i64 424, !7, i64 432, !7, i64 440, !7, i64 448, !17, i64 456, !17, i64 464, !25, i64 472, !26, i64 992, !17, i64 1016, !17, i64 1024, !23, i64 1032, !23, i64 1036, !21, i64 1040, !8, i64 1056, !7, i64 1096, !7, i64 1104, !7, i64 1112, !7, i64 1120, !7, i64 1128, !23, i64 1136, !17, i64 1144, !17, i64 1152, !17, i64 1160, !17, i64 1168, !17, i64 1176, !17, i64 1184, !23, i64 1192, !27, i64 1200, !8, i64 1232} -!20 = !{!"rb_global_vm_lock_struct", !17, i64 0, !8, i64 8, !21, i64 48, !17, i64 64, !23, i64 72, !8, i64 80, !8, i64 128, !23, i64 176, !23, i64 180} -!21 = !{!"list_head", !22, i64 0} -!22 = !{!"list_node", !17, i64 0, !17, i64 8} -!23 = !{!"int", !8, i64 0} -!24 = !{!"long long", !8, i64 0} -!25 = !{!"", !8, i64 0} -!26 = !{!"rb_hook_list_struct", !17, i64 0, !23, i64 8, !23, i64 12, !23, i64 16} -!27 = !{!"", !7, i64 0, !7, i64 8, !7, i64 16, !7, i64 24} -!28 = !{!29, !17, i64 16} -!29 = !{!"rb_execution_context_struct", !17, i64 0, !7, i64 8, !17, i64 16, !17, i64 24, !17, i64 32, !23, i64 40, !23, i64 44, !17, i64 48, !17, i64 56, !17, i64 64, !7, i64 72, !7, i64 80, !17, i64 88, !7, i64 96, !17, i64 104, !17, i64 112, !7, i64 120, !7, i64 128, !8, i64 136, !8, i64 137, !7, i64 144, !30, i64 152} -!30 = !{!"", !17, i64 0, !17, i64 8, !7, i64 16, !8, i64 24} -!31 = !{!32, !17, i64 16} -!32 = !{!"rb_control_frame_struct", !17, i64 0, !17, i64 8, !17, i64 16, !7, i64 24, !17, i64 32, !17, i64 40, !17, i64 48} -!33 = !{!32, !17, i64 32} -!34 = !DILocation(line: 0, scope: !11) -!35 = !{!36} -!36 = distinct !{!36, !37, !"sorbet_rb_int_gt: argument 0"} -!37 = distinct !{!37, !"sorbet_rb_int_gt"} -!38 = !DILocation(line: 5, column: 8, scope: !11) -!39 = !{!29, !23, i64 40} -!40 = !{!29, !23, i64 44} -!41 = !{!"branch_weights", i32 2000, i32 1} -!42 = !{!29, !17, i64 56} diff --git a/test/testdata/compiler/call_final.rb b/test/testdata/compiler/call_final.rb index 67fc5d118b..5f15977095 100644 --- a/test/testdata/compiler/call_final.rb +++ b/test/testdata/compiler/call_final.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL -# run_filecheck: OPT class A; T::Sig::WithoutRuntime.sig(:final) {params(n: Integer).returns(Integer)} @@ -20,10 +18,4 @@ def caller(a) # present in the un-optimized ir, and that it gets inlined in the optimized # version. -# INITIAL-LABEL: define internal i64 @"func_B#6caller" -# INITIAL: call i64 @direct_func_A.3foo(%struct.FunctionInlineCache* -# INITIAL{LITERAL}: } -# OPT-LABEL: define internal i64 @"func_B#6caller" -# OPT-NOT: call i64 @direct_func_A.3foo(%struct.FunctionInlineCache* -# OPT{LITERAL}: } diff --git a/test/testdata/compiler/casts.opt.ll.exp b/test/testdata/compiler/casts.opt.ll.exp deleted file mode 100644 index 052223c9b1..0000000000 --- a/test/testdata/compiler/casts.opt.ll.exp +++ /dev/null @@ -1,989 +0,0 @@ -; ModuleID = 'payload' -source_filename = "llvm-link" -target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } -%struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.__pthread_cond_s } -%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } -%union.anon = type { i64 } -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } -%struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } -%struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } -%struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } -%struct.anon.0 = type { i16, [2 x i8] } -%struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } -%struct.rb_iseq_location_struct = type { i64, i64, i64, i64, i32, %struct.rb_code_location_struct } -%struct.rb_code_location_struct = type { %struct.rb_code_position_struct, %struct.rb_code_position_struct } -%struct.rb_code_position_struct = type { i32, i32 } -%struct.iseq_insn_info = type { %struct.iseq_insn_info_entry*, i32*, i32, %struct.succ_index_table* } -%struct.iseq_insn_info_entry = type opaque -%struct.succ_index_table = type opaque -%struct.iseq_catch_table = type opaque -%union.iseq_inline_storage_entry = type { %struct.iseq_inline_cache_entry } -%struct.iseq_inline_cache_entry = type { i64, %struct.rb_cref_struct*, i64 } -%struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } -%struct.rb_scope_visi_struct = type { i8, [3 x i8] } -%struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } -%struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } -%union.anon.13 = type { %struct.rb_method_cfunc_struct } -%struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%union.anon.15 = type { i32 } -%struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.16 = type { i64, i64, i64, i64* } -%union.anon.17 = type { %struct.anon.18 } -%struct.anon.18 = type { i64, i32 } -%struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } -%struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } -%struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } -%struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } -%struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } -%struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } -%struct.__sigset_t = type { [16 x i64] } -%struct.rb_calling_info = type { i64, i64, i32, i32 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } -%union.anon.9 = type { %union.pthread_cond_t } -%struct.rb_unblock_callback = type { void (i8*)*, i8* } -%struct.rb_mutex_struct = type opaque -%struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.10 = type { %struct.anon.11 } -%struct.anon.11 = type { i64, i64, i32 } -%struct.rb_fiber_struct = type opaque -%struct.anon.5 = type { [65 x i64] } -%struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } -%struct.rb_event_hook_struct = type opaque -%struct.rb_postponed_job_struct = type opaque -%struct.list_head = type { %struct.list_node } -%union.pthread_mutex_t = type { %struct.__pthread_mutex_s } -%struct.__pthread_mutex_s = type { i32, i32, i32, i32, i32, i16, i16, %struct.__pthread_internal_list } -%struct.__pthread_internal_list = type { %struct.__pthread_internal_list*, %struct.__pthread_internal_list* } -%struct.rb_objspace = type opaque -%struct.rb_at_exit_list = type { void (%struct.rb_vm_struct*)*, %struct.rb_at_exit_list* } -%struct.st_table = type { i8, i8, i8, i32, %struct.st_hash_type*, i64, i64*, i64, i64, %struct.st_table_entry* } -%struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } -%struct.st_table_entry = type opaque -%struct.rb_builtin_function = type opaque -%struct.anon.6 = type { i64, i64, i64, i64 } -%struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } -%struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } -%struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } -%struct.rb_call_info_with_kwarg = type { %struct.rb_call_info, %struct.rb_call_info_kw_arg* } -%struct.rb_call_info_kw_arg = type { i32, [1 x i64] } -%struct.iseq_inline_iv_cache_entry = type { i64, i64 } - -@ruby_current_vm_ptr = external local_unnamed_addr global %struct.rb_vm_struct*, align 8 -@ruby_current_execution_context_ptr = external local_unnamed_addr global %struct.rb_execution_context_struct*, align 8 -@rb_eRuntimeError = external local_unnamed_addr global i64, align 8 -@.str.9 = private unnamed_addr constant [95 x i8] c"sorbet_getBuildSCMRevision: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@.str.10 = private unnamed_addr constant [93 x i8] c"sorbet_getIsReleaseBuild: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@"stackFramePrecomputed_func_Object#6fooAll" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@iseqEncodedArray = internal global [30 x i64] zeroinitializer -@fileLineNumberInfo = internal global %struct.SorbetLineNumberInfo zeroinitializer -@"stackFramePrecomputed_func_Object#7fooAny1" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"stackFramePrecomputed_func_Object#7fooAny2" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"stackFramePrecomputed_func_Object#6fooInt" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"stackFramePrecomputed_func_Object#8fooArray" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"stackFramePrecomputed_func_.13" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@ic_fooInt = internal global %struct.FunctionInlineCache zeroinitializer -@ic_puts = internal global %struct.FunctionInlineCache zeroinitializer -@ic_fooAny1 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_puts.1 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_fooAny2 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_puts.2 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_fooAll = internal global %struct.FunctionInlineCache zeroinitializer -@ic_puts.3 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_fooArray = internal global %struct.FunctionInlineCache zeroinitializer -@ic_puts.4 = internal global %struct.FunctionInlineCache zeroinitializer -@sorbet_moduleStringTable = internal unnamed_addr constant [218 x i8] c"fooAll\00test/testdata/compiler/casts.rb\00Kernel\00T.cast\00fooAny1\00T.any(Integer, Float)\00fooAny2\00T.any(Float, Integer)\00fooInt\00Integer\00+\00fooArray\00T::Array[Integer]\00\00normal\00Object\00arg\00puts\00\00master\00", align 1 -@sorbet_moduleIDTable = internal unnamed_addr global [11 x i64] zeroinitializer, align 8 -@sorbet_moduleIDDescriptors = internal unnamed_addr constant [11 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 6 }, %struct.rb_code_position_struct { i32 53, i32 7 }, %struct.rb_code_position_struct { i32 83, i32 7 }, %struct.rb_code_position_struct { i32 113, i32 6 }, %struct.rb_code_position_struct { i32 128, i32 1 }, %struct.rb_code_position_struct { i32 130, i32 8 }, %struct.rb_code_position_struct { i32 157, i32 16 }, %struct.rb_code_position_struct { i32 174, i32 6 }, %struct.rb_code_position_struct { i32 188, i32 3 }, %struct.rb_code_position_struct { i32 192, i32 4 }, %struct.rb_code_position_struct { i32 197, i32 13 }], align 8 -@sorbet_moduleRubyStringTable = internal unnamed_addr global [7 x i64] zeroinitializer, align 8 -@sorbet_moduleRubyStringDescriptors = internal unnamed_addr constant [7 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 6 }, %struct.rb_code_position_struct { i32 7, i32 31 }, %struct.rb_code_position_struct { i32 53, i32 7 }, %struct.rb_code_position_struct { i32 83, i32 7 }, %struct.rb_code_position_struct { i32 113, i32 6 }, %struct.rb_code_position_struct { i32 130, i32 8 }, %struct.rb_code_position_struct { i32 157, i32 16 }], align 8 -@rb_mKernel = external local_unnamed_addr constant i64 -@rb_cObject = external local_unnamed_addr constant i64 - -; Function Attrs: nounwind readnone willreturn -declare i64 @rb_obj_is_kind_of(i64, i64) local_unnamed_addr #0 - -; Function Attrs: cold noreturn -declare void @sorbet_cast_failure(i64, i8*, i8*) local_unnamed_addr #1 - -; Function Attrs: noreturn -declare void @sorbet_raiseArity(i32, i32, i32) local_unnamed_addr #2 - -declare %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64, i64, i64, i64, %struct.rb_iseq_struct*, i32, i32, %struct.SorbetLineNumberInfo*, i64*, i32, i32) local_unnamed_addr #3 - -declare void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo*, i64*, i32) local_unnamed_addr #3 - -declare i64 @sorbet_readRealpath() local_unnamed_addr #3 - -declare void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) local_unnamed_addr #3 - -declare i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache*, i64) local_unnamed_addr #3 - -declare void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_iseq_struct*) local_unnamed_addr #3 - -declare i64 @sorbet_rb_int_plus_slowpath(i64, i64) local_unnamed_addr #3 - -declare void @sorbet_vm_define_method(i64, i8*, i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)*, i8*, %struct.rb_iseq_struct*, i1 zeroext) local_unnamed_addr #3 - -declare void @sorbet_vm_intern_ids(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #3 - -declare void @sorbet_vm_init_string_table(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #3 - -declare i64 @rb_ary_new_from_values(i64, i64*) local_unnamed_addr #3 - -; Function Attrs: noreturn -declare void @rb_raise(i64, i8*, ...) local_unnamed_addr #2 - -declare i64 @rb_int2big(i64) local_unnamed_addr #3 - -; Function Attrs: nofree nosync nounwind readnone speculatable willreturn -declare { i64, i1 } @llvm.sadd.with.overflow.i64(i64, i64) #4 - -; Function Attrs: inaccessiblememonly nofree nosync nounwind willreturn -declare void @llvm.experimental.noalias.scope.decl(metadata) #5 - -declare i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct*, i32) local_unnamed_addr #3 - -; Function Attrs: allocsize(0,1) -declare noalias nonnull i8* @ruby_xcalloc(i64, i64) local_unnamed_addr #6 - -; Function Attrs: allocsize(0,1) -declare noalias nonnull i8* @ruby_xmalloc2(i64, i64) local_unnamed_addr #6 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) #7 - -; Function Attrs: nounwind ssp uwtable -define weak i32 @sorbet_getIsReleaseBuild() local_unnamed_addr #8 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([93 x i8], [93 x i8]* @.str.10, i64 0, i64 0)) #14 - unreachable -} - -; Function Attrs: nounwind ssp uwtable -define weak i8* @sorbet_getBuildSCMRevision() local_unnamed_addr #8 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([95 x i8], [95 x i8]* @.str.9, i64 0, i64 0)) #14 - unreachable -} - -define internal fastcc void @sorbet_globalConstructors(i64 %realpath) unnamed_addr { -allocRubyIds: - tail call void @sorbet_vm_intern_ids(i64* noundef getelementptr inbounds ([11 x i64], [11 x i64]* @sorbet_moduleIDTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([11 x %struct.rb_code_position_struct], [11 x %struct.rb_code_position_struct]* @sorbet_moduleIDDescriptors, i32 0, i32 0), i32 noundef 11, i8* noundef getelementptr inbounds ([218 x i8], [218 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_vm_init_string_table(i64* noundef getelementptr inbounds ([7 x i64], [7 x i64]* @sorbet_moduleRubyStringTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([7 x %struct.rb_code_position_struct], [7 x %struct.rb_code_position_struct]* @sorbet_moduleRubyStringDescriptors, i32 0, i32 0), i32 noundef 7, i8* noundef getelementptr inbounds ([218 x i8], [218 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef getelementptr inbounds ([30 x i64], [30 x i64]* @iseqEncodedArray, i32 0, i32 0), i32 noundef 30) - tail call fastcc void @"Constr_stackFramePrecomputed_func_Object#6fooAll"(i64 %realpath) - tail call fastcc void @"Constr_stackFramePrecomputed_func_Object#7fooAny1"(i64 %realpath) - tail call fastcc void @"Constr_stackFramePrecomputed_func_Object#7fooAny2"(i64 %realpath) - tail call fastcc void @"Constr_stackFramePrecomputed_func_Object#6fooInt"(i64 %realpath) - tail call fastcc void @"Constr_stackFramePrecomputed_func_Object#8fooArray"(i64 %realpath) - tail call fastcc void @"Constr_stackFramePrecomputed_func_.13"(i64 %realpath) - %rubyId_fooInt = load i64, i64* getelementptr inbounds ([11 x i64], [11 x i64]* @sorbet_moduleIDTable, i64 0, i64 3), align 8, !dbg !10, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_fooInt, i64 %rubyId_fooInt, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !10 - %rubyId_puts = load i64, i64* getelementptr inbounds ([11 x i64], [11 x i64]* @sorbet_moduleIDTable, i64 0, i64 9), align 8, !dbg !15, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts, i64 %rubyId_puts, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !15 - %rubyId_fooAny1 = load i64, i64* getelementptr inbounds ([11 x i64], [11 x i64]* @sorbet_moduleIDTable, i64 0, i64 1), align 8, !dbg !16, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_fooAny1, i64 %rubyId_fooAny1, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !16 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts.1, i64 %rubyId_puts, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !17 - %rubyId_fooAny2 = load i64, i64* getelementptr inbounds ([11 x i64], [11 x i64]* @sorbet_moduleIDTable, i64 0, i64 2), align 8, !dbg !18, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_fooAny2, i64 %rubyId_fooAny2, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !18 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts.2, i64 %rubyId_puts, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !19 - %rubyId_fooAll = load i64, i64* getelementptr inbounds ([11 x i64], [11 x i64]* @sorbet_moduleIDTable, i64 0, i64 0), align 8, !dbg !20, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_fooAll, i64 %rubyId_fooAll, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !20 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts.3, i64 %rubyId_puts, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !21 - %rubyId_fooArray = load i64, i64* getelementptr inbounds ([11 x i64], [11 x i64]* @sorbet_moduleIDTable, i64 0, i64 5), align 8, !dbg !22, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_fooArray, i64 %rubyId_fooArray, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !22 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts.4, i64 %rubyId_puts, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !23 - ret void -} - -; Function Attrs: nounwind sspreq uwtable -define internal i64 @"func_Object#6fooAll"(i32 %argc, i64* nocapture readonly %argArray, i64 %selfRaw, %struct.rb_control_frame_struct* nocapture nonnull writeonly align 8 dereferenceable(8) %cfp, i8* nocapture nofree readnone %calling, i8* nocapture nofree readnone %callData) #9 !dbg !24 { -functionEntryInitializers: - %0 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 0 - store i64* getelementptr inbounds ([30 x i64], [30 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %0, align 8, !tbaa !25 - %tooManyArgs = icmp ugt i32 %argc, 1, !dbg !27 - %tooFewArgs = icmp ult i32 %argc, 1, !dbg !27 - %or.cond = or i1 %tooManyArgs, %tooFewArgs, !dbg !27 - br i1 %or.cond, label %argCountFailBlock, label %fillRequiredArgs, !dbg !27, !prof !28 - -argCountFailBlock: ; preds = %functionEntryInitializers - tail call void @sorbet_raiseArity(i32 %argc, i32 noundef 1, i32 noundef 1) #15, !dbg !27 - unreachable, !dbg !27 - -fillRequiredArgs: ; preds = %functionEntryInitializers - %rawArg_arg = load i64, i64* %argArray, align 8, !dbg !27 - store i64* getelementptr inbounds ([30 x i64], [30 x i64]* @iseqEncodedArray, i64 0, i64 6), i64** %0, align 8, !dbg !29, !tbaa !25 - %1 = load i64, i64* @rb_mKernel, align 8, !dbg !30 - %2 = tail call i64 @rb_obj_is_kind_of(i64 %rawArg_arg, i64 %1), !dbg !30 - %3 = icmp eq i64 %2, 20, !dbg !30 - br i1 %3, label %typeTestSuccess, label %codeRepl, !dbg !30, !prof !31 - -typeTestSuccess: ; preds = %fillRequiredArgs - ret i64 %rawArg_arg - -codeRepl: ; preds = %fillRequiredArgs - tail call fastcc void @"func_Object#6fooAll.cold.1"(i64 %rawArg_arg) #16, !dbg !30 - unreachable -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_Object#6fooAll"(i64 %realpath) unnamed_addr #10 { -entryInitializers: - %rubyId_fooAll = load i64, i64* getelementptr inbounds ([11 x i64], [11 x i64]* @sorbet_moduleIDTable, i64 0, i64 0), align 8, !invariant.load !5 - %rubyStr_fooAll = load i64, i64* getelementptr inbounds ([7 x i64], [7 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 0), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/casts.rb" = load i64, i64* getelementptr inbounds ([7 x i64], [7 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %rubyStr_fooAll, i64 %rubyId_fooAll, i64 %"rubyStr_test/testdata/compiler/casts.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 1, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 0) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Object#6fooAll", align 8 - ret void -} - -; Function Attrs: nounwind sspreq uwtable -define internal i64 @"func_Object#7fooAny1"(i32 %argc, i64* nocapture readonly %argArray, i64 %selfRaw, %struct.rb_control_frame_struct* nocapture nonnull writeonly align 8 dereferenceable(8) %cfp, i8* nocapture nofree readnone %calling, i8* nocapture nofree readnone %callData) #9 !dbg !32 { -functionEntryInitializers: - %0 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 0 - store i64* getelementptr inbounds ([30 x i64], [30 x i64]* @iseqEncodedArray, i64 0, i64 9), i64** %0, align 8, !tbaa !25 - %tooManyArgs = icmp ugt i32 %argc, 1, !dbg !33 - %tooFewArgs = icmp ult i32 %argc, 1, !dbg !33 - %or.cond = or i1 %tooManyArgs, %tooFewArgs, !dbg !33 - br i1 %or.cond, label %argCountFailBlock, label %fillRequiredArgs, !dbg !33, !prof !28 - -argCountFailBlock: ; preds = %functionEntryInitializers - tail call void @sorbet_raiseArity(i32 %argc, i32 noundef 1, i32 noundef 1) #15, !dbg !33 - unreachable, !dbg !33 - -fillRequiredArgs: ; preds = %functionEntryInitializers - %rawArg_arg = load i64, i64* %argArray, align 8, !dbg !33 - store i64* getelementptr inbounds ([30 x i64], [30 x i64]* @iseqEncodedArray, i64 0, i64 10), i64** %0, align 8, !dbg !34, !tbaa !25 - %1 = and i64 %rawArg_arg, 1, !dbg !35 - %2 = icmp eq i64 %1, 0, !dbg !35 - br i1 %2, label %3, label %typeTestSuccess, !dbg !35, !prof !36 - -3: ; preds = %fillRequiredArgs - %4 = and i64 %rawArg_arg, 7, !dbg !35 - %5 = icmp ne i64 %4, 0, !dbg !35 - %6 = and i64 %rawArg_arg, -9, !dbg !35 - %7 = icmp eq i64 %6, 0, !dbg !35 - %8 = or i1 %5, %7, !dbg !35 - br i1 %8, label %orCase, label %sorbet_isa_Integer.exit, !dbg !35 - -sorbet_isa_Integer.exit: ; preds = %3 - %9 = inttoptr i64 %rawArg_arg to %struct.iseq_inline_iv_cache_entry*, !dbg !35 - %10 = getelementptr inbounds %struct.iseq_inline_iv_cache_entry, %struct.iseq_inline_iv_cache_entry* %9, i64 0, i32 0, !dbg !35 - %11 = load i64, i64* %10, align 8, !dbg !35, !tbaa !37 - %12 = and i64 %11, 31, !dbg !35 - %13 = icmp eq i64 %12, 10, !dbg !35 - br i1 %13, label %typeTestSuccess, label %orCase, !dbg !35 - -orCase: ; preds = %3, %sorbet_isa_Integer.exit - %14 = and i64 %rawArg_arg, 3, !dbg !35 - %15 = icmp eq i64 %14, 2, !dbg !35 - br i1 %15, label %typeTestSuccess, label %16, !dbg !35 - -16: ; preds = %orCase - %17 = and i64 %rawArg_arg, 7, !dbg !35 - %18 = icmp ne i64 %17, 0, !dbg !35 - %19 = and i64 %rawArg_arg, -9, !dbg !35 - %20 = icmp eq i64 %19, 0, !dbg !35 - %21 = or i1 %18, %20, !dbg !35 - br i1 %21, label %codeRepl, label %sorbet_isa_Float.exit, !dbg !35 - -sorbet_isa_Float.exit: ; preds = %16 - %22 = inttoptr i64 %rawArg_arg to %struct.iseq_inline_iv_cache_entry*, !dbg !35 - %23 = getelementptr inbounds %struct.iseq_inline_iv_cache_entry, %struct.iseq_inline_iv_cache_entry* %22, i64 0, i32 0, !dbg !35 - %24 = load i64, i64* %23, align 8, !dbg !35, !tbaa !37 - %25 = and i64 %24, 31, !dbg !35 - %26 = icmp eq i64 %25, 4, !dbg !35 - br i1 %26, label %typeTestSuccess, label %codeRepl, !dbg !35, !prof !31 - -typeTestSuccess: ; preds = %orCase, %fillRequiredArgs, %sorbet_isa_Integer.exit, %sorbet_isa_Float.exit - ret i64 %rawArg_arg - -codeRepl: ; preds = %16, %sorbet_isa_Float.exit - tail call fastcc void @"func_Object#7fooAny1.cold.1"(i64 %rawArg_arg) #16, !dbg !35 - unreachable -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_Object#7fooAny1"(i64 %realpath) unnamed_addr #10 { -entryInitializers: - %rubyId_fooAny1 = load i64, i64* getelementptr inbounds ([11 x i64], [11 x i64]* @sorbet_moduleIDTable, i64 0, i64 1), align 8, !invariant.load !5 - %rubyStr_fooAny1 = load i64, i64* getelementptr inbounds ([7 x i64], [7 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 2), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/casts.rb" = load i64, i64* getelementptr inbounds ([7 x i64], [7 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %rubyStr_fooAny1, i64 %rubyId_fooAny1, i64 %"rubyStr_test/testdata/compiler/casts.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 1, i32 noundef 9, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 0) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Object#7fooAny1", align 8 - ret void -} - -; Function Attrs: nounwind sspreq uwtable -define internal i64 @"func_Object#7fooAny2"(i32 %argc, i64* nocapture readonly %argArray, i64 %selfRaw, %struct.rb_control_frame_struct* nocapture nonnull writeonly align 8 dereferenceable(8) %cfp, i8* nocapture nofree readnone %calling, i8* nocapture nofree readnone %callData) #9 !dbg !39 { -functionEntryInitializers: - %0 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 0 - store i64* getelementptr inbounds ([30 x i64], [30 x i64]* @iseqEncodedArray, i64 0, i64 13), i64** %0, align 8, !tbaa !25 - %tooManyArgs = icmp ugt i32 %argc, 1, !dbg !40 - %tooFewArgs = icmp ult i32 %argc, 1, !dbg !40 - %or.cond = or i1 %tooManyArgs, %tooFewArgs, !dbg !40 - br i1 %or.cond, label %argCountFailBlock, label %fillRequiredArgs, !dbg !40, !prof !28 - -argCountFailBlock: ; preds = %functionEntryInitializers - tail call void @sorbet_raiseArity(i32 %argc, i32 noundef 1, i32 noundef 1) #15, !dbg !40 - unreachable, !dbg !40 - -fillRequiredArgs: ; preds = %functionEntryInitializers - %rawArg_arg = load i64, i64* %argArray, align 8, !dbg !40 - store i64* getelementptr inbounds ([30 x i64], [30 x i64]* @iseqEncodedArray, i64 0, i64 14), i64** %0, align 8, !dbg !41, !tbaa !25 - %1 = and i64 %rawArg_arg, 3, !dbg !42 - %2 = icmp eq i64 %1, 2, !dbg !42 - br i1 %2, label %typeTestSuccess, label %3, !dbg !42 - -3: ; preds = %fillRequiredArgs - %4 = and i64 %rawArg_arg, 7, !dbg !42 - %5 = icmp ne i64 %4, 0, !dbg !42 - %6 = and i64 %rawArg_arg, -9, !dbg !42 - %7 = icmp eq i64 %6, 0, !dbg !42 - %8 = or i1 %5, %7, !dbg !42 - br i1 %8, label %orCase, label %sorbet_isa_Float.exit, !dbg !42 - -sorbet_isa_Float.exit: ; preds = %3 - %9 = inttoptr i64 %rawArg_arg to %struct.iseq_inline_iv_cache_entry*, !dbg !42 - %10 = getelementptr inbounds %struct.iseq_inline_iv_cache_entry, %struct.iseq_inline_iv_cache_entry* %9, i64 0, i32 0, !dbg !42 - %11 = load i64, i64* %10, align 8, !dbg !42, !tbaa !37 - %12 = and i64 %11, 31, !dbg !42 - %13 = icmp eq i64 %12, 4, !dbg !42 - br i1 %13, label %typeTestSuccess, label %orCase, !dbg !42 - -orCase: ; preds = %3, %sorbet_isa_Float.exit - %14 = and i64 %rawArg_arg, 1, !dbg !42 - %15 = icmp eq i64 %14, 0, !dbg !42 - br i1 %15, label %16, label %typeTestSuccess, !dbg !42, !prof !36 - -16: ; preds = %orCase - %17 = and i64 %rawArg_arg, 7, !dbg !42 - %18 = icmp ne i64 %17, 0, !dbg !42 - %19 = and i64 %rawArg_arg, -9, !dbg !42 - %20 = icmp eq i64 %19, 0, !dbg !42 - %21 = or i1 %18, %20, !dbg !42 - br i1 %21, label %codeRepl, label %sorbet_isa_Integer.exit, !dbg !42 - -sorbet_isa_Integer.exit: ; preds = %16 - %22 = inttoptr i64 %rawArg_arg to %struct.iseq_inline_iv_cache_entry*, !dbg !42 - %23 = getelementptr inbounds %struct.iseq_inline_iv_cache_entry, %struct.iseq_inline_iv_cache_entry* %22, i64 0, i32 0, !dbg !42 - %24 = load i64, i64* %23, align 8, !dbg !42, !tbaa !37 - %25 = and i64 %24, 31, !dbg !42 - %26 = icmp eq i64 %25, 10, !dbg !42 - br i1 %26, label %typeTestSuccess, label %codeRepl, !dbg !42, !prof !31 - -typeTestSuccess: ; preds = %orCase, %fillRequiredArgs, %sorbet_isa_Float.exit, %sorbet_isa_Integer.exit - ret i64 %rawArg_arg - -codeRepl: ; preds = %16, %sorbet_isa_Integer.exit - tail call fastcc void @"func_Object#7fooAny2.cold.1"(i64 %rawArg_arg) #16, !dbg !42 - unreachable -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_Object#7fooAny2"(i64 %realpath) unnamed_addr #10 { -entryInitializers: - %rubyId_fooAny2 = load i64, i64* getelementptr inbounds ([11 x i64], [11 x i64]* @sorbet_moduleIDTable, i64 0, i64 2), align 8, !invariant.load !5 - %rubyStr_fooAny2 = load i64, i64* getelementptr inbounds ([7 x i64], [7 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 3), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/casts.rb" = load i64, i64* getelementptr inbounds ([7 x i64], [7 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %rubyStr_fooAny2, i64 %rubyId_fooAny2, i64 %"rubyStr_test/testdata/compiler/casts.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 1, i32 noundef 13, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 0) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Object#7fooAny2", align 8 - ret void -} - -; Function Attrs: nounwind sspreq uwtable -define internal i64 @"func_Object#6fooInt"(i32 %argc, i64* nocapture readonly %argArray, i64 %selfRaw, %struct.rb_control_frame_struct* nocapture nonnull writeonly align 8 dereferenceable(8) %cfp, i8* nocapture nofree readnone %calling, i8* nocapture nofree readnone %callData) #9 !dbg !43 { -functionEntryInitializers: - %0 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 0 - store i64* getelementptr inbounds ([30 x i64], [30 x i64]* @iseqEncodedArray, i64 0, i64 17), i64** %0, align 8, !tbaa !25 - %tooManyArgs = icmp ugt i32 %argc, 1, !dbg !44 - %tooFewArgs = icmp ult i32 %argc, 1, !dbg !44 - %or.cond = or i1 %tooManyArgs, %tooFewArgs, !dbg !44 - br i1 %or.cond, label %argCountFailBlock, label %fillRequiredArgs, !dbg !44, !prof !28 - -argCountFailBlock: ; preds = %functionEntryInitializers - tail call void @sorbet_raiseArity(i32 %argc, i32 noundef 1, i32 noundef 1) #15, !dbg !44 - unreachable, !dbg !44 - -fillRequiredArgs: ; preds = %functionEntryInitializers - %rawArg_arg = load i64, i64* %argArray, align 8, !dbg !44 - store i64* getelementptr inbounds ([30 x i64], [30 x i64]* @iseqEncodedArray, i64 0, i64 18), i64** %0, align 8, !dbg !45, !tbaa !25 - %1 = and i64 %rawArg_arg, 1, !dbg !46 - %2 = icmp eq i64 %1, 0, !dbg !46 - br i1 %2, label %3, label %"fastSymCallIntrinsic_Integer_+", !dbg !46, !prof !36 - -3: ; preds = %fillRequiredArgs - %4 = and i64 %rawArg_arg, 7, !dbg !46 - %5 = icmp ne i64 %4, 0, !dbg !46 - %6 = and i64 %rawArg_arg, -9, !dbg !46 - %7 = icmp eq i64 %6, 0, !dbg !46 - %8 = or i1 %5, %7, !dbg !46 - br i1 %8, label %codeRepl, label %sorbet_isa_Integer.exit, !dbg !46 - -sorbet_isa_Integer.exit: ; preds = %3 - %9 = inttoptr i64 %rawArg_arg to %struct.iseq_inline_iv_cache_entry*, !dbg !46 - %10 = getelementptr inbounds %struct.iseq_inline_iv_cache_entry, %struct.iseq_inline_iv_cache_entry* %9, i64 0, i32 0, !dbg !46 - %11 = load i64, i64* %10, align 8, !dbg !46, !tbaa !37 - %12 = and i64 %11, 31, !dbg !46 - %13 = icmp eq i64 %12, 10, !dbg !46 - br i1 %13, label %"fastSymCallIntrinsic_Integer_+", label %codeRepl, !dbg !46, !prof !31 - -codeRepl: ; preds = %3, %sorbet_isa_Integer.exit - tail call fastcc void @"func_Object#6fooInt.cold.1"(i64 %rawArg_arg) #16, !dbg !46 - unreachable - -"fastSymCallIntrinsic_Integer_+": ; preds = %fillRequiredArgs, %sorbet_isa_Integer.exit - tail call void @llvm.experimental.noalias.scope.decl(metadata !47), !dbg !46 - %14 = and i64 %rawArg_arg, 1, !dbg !46 - %15 = icmp eq i64 %14, 0, !dbg !46 - br i1 %15, label %25, label %16, !dbg !46, !prof !28 - -16: ; preds = %"fastSymCallIntrinsic_Integer_+" - %17 = add nsw i64 %rawArg_arg, -1, !dbg !46 - %18 = tail call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %rawArg_arg, i64 %17) #17, !dbg !46 - %19 = extractvalue { i64, i1 } %18, 1, !dbg !46 - %20 = extractvalue { i64, i1 } %18, 0, !dbg !46 - br i1 %19, label %21, label %sorbet_rb_int_plus.exit, !dbg !46 - -21: ; preds = %16 - %22 = ashr i64 %20, 1, !dbg !46 - %23 = xor i64 %22, -9223372036854775808, !dbg !46 - %24 = tail call i64 @rb_int2big(i64 %23) #18, !dbg !46, !noalias !47 - br label %sorbet_rb_int_plus.exit, !dbg !46 - -25: ; preds = %"fastSymCallIntrinsic_Integer_+" - %26 = tail call i64 @sorbet_rb_int_plus_slowpath(i64 %rawArg_arg, i64 %rawArg_arg) #18, !dbg !46, !noalias !47 - br label %sorbet_rb_int_plus.exit, !dbg !46 - -sorbet_rb_int_plus.exit: ; preds = %21, %16, %25 - %27 = phi i64 [ %26, %25 ], [ %24, %21 ], [ %20, %16 ], !dbg !46 - %28 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !46, !tbaa !25 - %29 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %28, i64 0, i32 5, !dbg !46 - %30 = load i32, i32* %29, align 8, !dbg !46, !tbaa !50 - %31 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %28, i64 0, i32 6, !dbg !46 - %32 = load i32, i32* %31, align 4, !dbg !46, !tbaa !54 - %33 = xor i32 %32, -1, !dbg !46 - %34 = and i32 %33, %30, !dbg !46 - %35 = icmp eq i32 %34, 0, !dbg !46 - br i1 %35, label %rb_vm_check_ints.exit, label %36, !dbg !46, !prof !31 - -36: ; preds = %sorbet_rb_int_plus.exit - %37 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %28, i64 0, i32 8, !dbg !46 - %38 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %37, align 8, !dbg !46, !tbaa !55 - %39 = tail call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %38, i32 noundef 0) #18, !dbg !46 - br label %rb_vm_check_ints.exit, !dbg !46 - -rb_vm_check_ints.exit: ; preds = %sorbet_rb_int_plus.exit, %36 - ret i64 %27 -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_Object#6fooInt"(i64 %realpath) unnamed_addr #10 { -entryInitializers: - %rubyId_fooInt = load i64, i64* getelementptr inbounds ([11 x i64], [11 x i64]* @sorbet_moduleIDTable, i64 0, i64 3), align 8, !invariant.load !5 - %rubyStr_fooInt = load i64, i64* getelementptr inbounds ([7 x i64], [7 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 4), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/casts.rb" = load i64, i64* getelementptr inbounds ([7 x i64], [7 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %rubyStr_fooInt, i64 %rubyId_fooInt, i64 %"rubyStr_test/testdata/compiler/casts.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 1, i32 noundef 17, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 2) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Object#6fooInt", align 8 - ret void -} - -; Function Attrs: nounwind sspreq uwtable -define internal i64 @"func_Object#8fooArray"(i32 %argc, i64* nocapture readonly %argArray, i64 %selfRaw, %struct.rb_control_frame_struct* nocapture nonnull writeonly align 8 dereferenceable(8) %cfp, i8* nocapture nofree readnone %calling, i8* nocapture nofree readnone %callData) #9 !dbg !56 { -functionEntryInitializers: - %0 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 0 - store i64* getelementptr inbounds ([30 x i64], [30 x i64]* @iseqEncodedArray, i64 0, i64 21), i64** %0, align 8, !tbaa !25 - %tooManyArgs = icmp ugt i32 %argc, 1, !dbg !57 - %tooFewArgs = icmp ult i32 %argc, 1, !dbg !57 - %or.cond = or i1 %tooManyArgs, %tooFewArgs, !dbg !57 - br i1 %or.cond, label %argCountFailBlock, label %fillRequiredArgs, !dbg !57, !prof !28 - -argCountFailBlock: ; preds = %functionEntryInitializers - tail call void @sorbet_raiseArity(i32 %argc, i32 noundef 1, i32 noundef 1) #15, !dbg !57 - unreachable, !dbg !57 - -fillRequiredArgs: ; preds = %functionEntryInitializers - %rawArg_arg = load i64, i64* %argArray, align 8, !dbg !57 - store i64* getelementptr inbounds ([30 x i64], [30 x i64]* @iseqEncodedArray, i64 0, i64 22), i64** %0, align 8, !dbg !58, !tbaa !25 - %1 = and i64 %rawArg_arg, 7, !dbg !59 - %2 = icmp ne i64 %1, 0, !dbg !59 - %3 = and i64 %rawArg_arg, -9, !dbg !59 - %4 = icmp eq i64 %3, 0, !dbg !59 - %5 = or i1 %2, %4, !dbg !59 - br i1 %5, label %codeRepl, label %sorbet_isa_Array.exit, !dbg !59 - -sorbet_isa_Array.exit: ; preds = %fillRequiredArgs - %6 = inttoptr i64 %rawArg_arg to %struct.iseq_inline_iv_cache_entry*, !dbg !59 - %7 = getelementptr inbounds %struct.iseq_inline_iv_cache_entry, %struct.iseq_inline_iv_cache_entry* %6, i64 0, i32 0, !dbg !59 - %8 = load i64, i64* %7, align 8, !dbg !59, !tbaa !37 - %9 = and i64 %8, 31, !dbg !59 - %10 = icmp eq i64 %9, 7, !dbg !59 - br i1 %10, label %typeTestSuccess, label %codeRepl, !dbg !59, !prof !31 - -typeTestSuccess: ; preds = %sorbet_isa_Array.exit - ret i64 %rawArg_arg - -codeRepl: ; preds = %fillRequiredArgs, %sorbet_isa_Array.exit - tail call fastcc void @"func_Object#8fooArray.cold.1"(i64 %rawArg_arg) #16, !dbg !59 - unreachable -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_Object#8fooArray"(i64 %realpath) unnamed_addr #10 { -entryInitializers: - %rubyId_fooArray = load i64, i64* getelementptr inbounds ([11 x i64], [11 x i64]* @sorbet_moduleIDTable, i64 0, i64 5), align 8, !invariant.load !5 - %rubyStr_fooArray = load i64, i64* getelementptr inbounds ([7 x i64], [7 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 5), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/casts.rb" = load i64, i64* getelementptr inbounds ([7 x i64], [7 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %rubyStr_fooArray, i64 %rubyId_fooArray, i64 %"rubyStr_test/testdata/compiler/casts.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 1, i32 noundef 21, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 0) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Object#8fooArray", align 8 - ret void -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_.13"(i64 %realpath) unnamed_addr #10 { -entryInitializers: - %"rubyId_" = load i64, i64* getelementptr inbounds ([11 x i64], [11 x i64]* @sorbet_moduleIDTable, i64 0, i64 6), align 8, !invariant.load !5 - %"rubyStr_" = load i64, i64* getelementptr inbounds ([7 x i64], [7 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 6), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/casts.rb" = load i64, i64* getelementptr inbounds ([7 x i64], [7 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_", i64 %"rubyId_", i64 %"rubyStr_test/testdata/compiler/casts.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 0, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 4) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - ret void -} - -; Function Attrs: sspreq -define void @Init_casts() local_unnamed_addr #11 { -entry: - %callArgs.i = alloca [4 x i64], align 8 - %positional_table.i = alloca i64, align 8, !dbg !60 - %positional_table84.i = alloca i64, align 8, !dbg !61 - %positional_table96.i = alloca i64, align 8, !dbg !62 - %positional_table108.i = alloca i64, align 8, !dbg !63 - %positional_table120.i = alloca i64, align 8, !dbg !64 - %realpath = tail call i64 @sorbet_readRealpath() - tail call fastcc void @sorbet_globalConstructors(i64 %realpath) - %0 = load %struct.rb_vm_struct*, %struct.rb_vm_struct** @ruby_current_vm_ptr, align 8, !tbaa !25 - %1 = getelementptr inbounds %struct.rb_vm_struct, %struct.rb_vm_struct* %0, i64 0, i32 18 - %2 = load i64, i64* %1, align 8, !tbaa !65 - %3 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !25 - %4 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %3, i64 0, i32 2 - %5 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %4, align 8, !tbaa !74 - %6 = bitcast [4 x i64]* %callArgs.i to i8* - call void @llvm.lifetime.start.p0i8(i64 32, i8* nonnull %6) - %7 = bitcast i64* %positional_table.i to i8* - call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %7) - %8 = bitcast i64* %positional_table84.i to i8* - call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %8) - %9 = bitcast i64* %positional_table96.i to i8* - call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %9) - %10 = bitcast i64* %positional_table108.i to i8* - call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %10) - %11 = bitcast i64* %positional_table120.i to i8* - call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %11) - %stackFrame.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - %12 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %4, align 8, !tbaa !74 - %13 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %12, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame.i, %struct.rb_iseq_struct** %13, align 8, !tbaa !75 - %14 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %12, i64 0, i32 4 - %15 = load i64*, i64** %14, align 8, !tbaa !77 - %16 = load i64, i64* %15, align 8, !tbaa !6 - %17 = and i64 %16, -33 - store i64 %17, i64* %15, align 8, !tbaa !6 - tail call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %3, %struct.rb_control_frame_struct* %12, %struct.rb_iseq_struct* %stackFrame.i) #18 - %18 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 0 - store i64* getelementptr inbounds ([30 x i64], [30 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %18, align 8, !dbg !78, !tbaa !25 - %19 = load i64, i64* @rb_cObject, align 8, !dbg !60 - %stackFrame73.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Object#6fooAll", align 8, !dbg !60 - %20 = tail call noalias nonnull i8* @ruby_xcalloc(i64 noundef 1, i64 noundef 64) #19, !dbg !60 - %21 = bitcast i8* %20 to i16*, !dbg !60 - %22 = load i16, i16* %21, align 8, !dbg !60 - %23 = and i16 %22, -384, !dbg !60 - %24 = or i16 %23, 1, !dbg !60 - store i16 %24, i16* %21, align 8, !dbg !60 - %25 = getelementptr inbounds i8, i8* %20, i64 8, !dbg !60 - %26 = bitcast i8* %25 to i32*, !dbg !60 - store i32 1, i32* %26, align 8, !dbg !60, !tbaa !79 - %27 = getelementptr inbounds i8, i8* %20, i64 12, !dbg !60 - %28 = getelementptr inbounds i8, i8* %20, i64 4, !dbg !60 - %29 = bitcast i8* %28 to i32*, !dbg !60 - tail call void @llvm.memset.p0i8.i64(i8* nonnull align 4 %27, i8 0, i64 20, i1 false) #18, !dbg !60 - store i32 1, i32* %29, align 4, !dbg !60, !tbaa !82 - %rubyId_arg.i = load i64, i64* getelementptr inbounds ([11 x i64], [11 x i64]* @sorbet_moduleIDTable, i64 0, i64 8), align 8, !dbg !60, !invariant.load !5 - store i64 %rubyId_arg.i, i64* %positional_table.i, align 8, !dbg !60 - %30 = tail call noalias nonnull i8* @ruby_xmalloc2(i64 noundef 1, i64 noundef 8) #19, !dbg !60 - call void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture nonnull writeonly align 1 %30, i8* nocapture noundef nonnull readonly align 8 dereferenceable(8) %7, i64 noundef 8, i1 noundef false) #18, !dbg !60 - %31 = getelementptr inbounds i8, i8* %20, i64 32, !dbg !60 - %32 = bitcast i8* %31 to i8**, !dbg !60 - store i8* %30, i8** %32, align 8, !dbg !60, !tbaa !83 - tail call void @sorbet_vm_define_method(i64 %19, i8* noundef getelementptr inbounds ([218 x i8], [218 x i8]* @sorbet_moduleStringTable, i64 0, i64 0), i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)* noundef @"func_Object#6fooAll", i8* nonnull %20, %struct.rb_iseq_struct* %stackFrame73.i, i1 noundef zeroext false) #18, !dbg !60 - store i64* getelementptr inbounds ([30 x i64], [30 x i64]* @iseqEncodedArray, i64 0, i64 9), i64** %18, align 8, !dbg !60, !tbaa !25 - %stackFrame82.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Object#7fooAny1", align 8, !dbg !61 - %33 = tail call noalias nonnull i8* @ruby_xcalloc(i64 noundef 1, i64 noundef 64) #19, !dbg !61 - %34 = bitcast i8* %33 to i16*, !dbg !61 - %35 = load i16, i16* %34, align 8, !dbg !61 - %36 = and i16 %35, -384, !dbg !61 - %37 = or i16 %36, 1, !dbg !61 - store i16 %37, i16* %34, align 8, !dbg !61 - %38 = getelementptr inbounds i8, i8* %33, i64 8, !dbg !61 - %39 = bitcast i8* %38 to i32*, !dbg !61 - store i32 1, i32* %39, align 8, !dbg !61, !tbaa !79 - %40 = getelementptr inbounds i8, i8* %33, i64 12, !dbg !61 - %41 = getelementptr inbounds i8, i8* %33, i64 4, !dbg !61 - %42 = bitcast i8* %41 to i32*, !dbg !61 - tail call void @llvm.memset.p0i8.i64(i8* nonnull align 4 %40, i8 0, i64 20, i1 false) #18, !dbg !61 - store i32 1, i32* %42, align 4, !dbg !61, !tbaa !82 - store i64 %rubyId_arg.i, i64* %positional_table84.i, align 8, !dbg !61 - %43 = tail call noalias nonnull i8* @ruby_xmalloc2(i64 noundef 1, i64 noundef 8) #19, !dbg !61 - call void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture nonnull writeonly align 1 %43, i8* nocapture noundef nonnull readonly align 8 dereferenceable(8) %8, i64 noundef 8, i1 noundef false) #18, !dbg !61 - %44 = getelementptr inbounds i8, i8* %33, i64 32, !dbg !61 - %45 = bitcast i8* %44 to i8**, !dbg !61 - store i8* %43, i8** %45, align 8, !dbg !61, !tbaa !83 - tail call void @sorbet_vm_define_method(i64 %19, i8* getelementptr inbounds ([218 x i8], [218 x i8]* @sorbet_moduleStringTable, i64 0, i64 53), i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)* noundef @"func_Object#7fooAny1", i8* nonnull %33, %struct.rb_iseq_struct* %stackFrame82.i, i1 noundef zeroext false) #18, !dbg !61 - store i64* getelementptr inbounds ([30 x i64], [30 x i64]* @iseqEncodedArray, i64 0, i64 13), i64** %18, align 8, !dbg !61, !tbaa !25 - %stackFrame94.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Object#7fooAny2", align 8, !dbg !62 - %46 = tail call noalias nonnull i8* @ruby_xcalloc(i64 noundef 1, i64 noundef 64) #19, !dbg !62 - %47 = bitcast i8* %46 to i16*, !dbg !62 - %48 = load i16, i16* %47, align 8, !dbg !62 - %49 = and i16 %48, -384, !dbg !62 - %50 = or i16 %49, 1, !dbg !62 - store i16 %50, i16* %47, align 8, !dbg !62 - %51 = getelementptr inbounds i8, i8* %46, i64 8, !dbg !62 - %52 = bitcast i8* %51 to i32*, !dbg !62 - store i32 1, i32* %52, align 8, !dbg !62, !tbaa !79 - %53 = getelementptr inbounds i8, i8* %46, i64 12, !dbg !62 - %54 = getelementptr inbounds i8, i8* %46, i64 4, !dbg !62 - %55 = bitcast i8* %54 to i32*, !dbg !62 - tail call void @llvm.memset.p0i8.i64(i8* nonnull align 4 %53, i8 0, i64 20, i1 false) #18, !dbg !62 - store i32 1, i32* %55, align 4, !dbg !62, !tbaa !82 - store i64 %rubyId_arg.i, i64* %positional_table96.i, align 8, !dbg !62 - %56 = tail call noalias nonnull i8* @ruby_xmalloc2(i64 noundef 1, i64 noundef 8) #19, !dbg !62 - call void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture nonnull writeonly align 1 %56, i8* nocapture noundef nonnull readonly align 8 dereferenceable(8) %9, i64 noundef 8, i1 noundef false) #18, !dbg !62 - %57 = getelementptr inbounds i8, i8* %46, i64 32, !dbg !62 - %58 = bitcast i8* %57 to i8**, !dbg !62 - store i8* %56, i8** %58, align 8, !dbg !62, !tbaa !83 - tail call void @sorbet_vm_define_method(i64 %19, i8* getelementptr inbounds ([218 x i8], [218 x i8]* @sorbet_moduleStringTable, i64 0, i64 83), i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)* noundef @"func_Object#7fooAny2", i8* nonnull %46, %struct.rb_iseq_struct* %stackFrame94.i, i1 noundef zeroext false) #18, !dbg !62 - store i64* getelementptr inbounds ([30 x i64], [30 x i64]* @iseqEncodedArray, i64 0, i64 17), i64** %18, align 8, !dbg !62, !tbaa !25 - %stackFrame106.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Object#6fooInt", align 8, !dbg !63 - %59 = tail call noalias nonnull i8* @ruby_xcalloc(i64 noundef 1, i64 noundef 64) #19, !dbg !63 - %60 = bitcast i8* %59 to i16*, !dbg !63 - %61 = load i16, i16* %60, align 8, !dbg !63 - %62 = and i16 %61, -384, !dbg !63 - %63 = or i16 %62, 1, !dbg !63 - store i16 %63, i16* %60, align 8, !dbg !63 - %64 = getelementptr inbounds i8, i8* %59, i64 8, !dbg !63 - %65 = bitcast i8* %64 to i32*, !dbg !63 - store i32 1, i32* %65, align 8, !dbg !63, !tbaa !79 - %66 = getelementptr inbounds i8, i8* %59, i64 12, !dbg !63 - %67 = getelementptr inbounds i8, i8* %59, i64 4, !dbg !63 - %68 = bitcast i8* %67 to i32*, !dbg !63 - tail call void @llvm.memset.p0i8.i64(i8* nonnull align 4 %66, i8 0, i64 20, i1 false) #18, !dbg !63 - store i32 1, i32* %68, align 4, !dbg !63, !tbaa !82 - store i64 %rubyId_arg.i, i64* %positional_table108.i, align 8, !dbg !63 - %69 = tail call noalias nonnull i8* @ruby_xmalloc2(i64 noundef 1, i64 noundef 8) #19, !dbg !63 - call void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture nonnull writeonly align 1 %69, i8* nocapture noundef nonnull readonly align 8 dereferenceable(8) %10, i64 noundef 8, i1 noundef false) #18, !dbg !63 - %70 = getelementptr inbounds i8, i8* %59, i64 32, !dbg !63 - %71 = bitcast i8* %70 to i8**, !dbg !63 - store i8* %69, i8** %71, align 8, !dbg !63, !tbaa !83 - tail call void @sorbet_vm_define_method(i64 %19, i8* getelementptr inbounds ([218 x i8], [218 x i8]* @sorbet_moduleStringTable, i64 0, i64 113), i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)* noundef @"func_Object#6fooInt", i8* nonnull %59, %struct.rb_iseq_struct* %stackFrame106.i, i1 noundef zeroext false) #18, !dbg !63 - store i64* getelementptr inbounds ([30 x i64], [30 x i64]* @iseqEncodedArray, i64 0, i64 21), i64** %18, align 8, !dbg !63, !tbaa !25 - %stackFrame118.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Object#8fooArray", align 8, !dbg !64 - %72 = tail call noalias nonnull i8* @ruby_xcalloc(i64 noundef 1, i64 noundef 64) #19, !dbg !64 - %73 = bitcast i8* %72 to i16*, !dbg !64 - %74 = load i16, i16* %73, align 8, !dbg !64 - %75 = and i16 %74, -384, !dbg !64 - %76 = or i16 %75, 1, !dbg !64 - store i16 %76, i16* %73, align 8, !dbg !64 - %77 = getelementptr inbounds i8, i8* %72, i64 8, !dbg !64 - %78 = bitcast i8* %77 to i32*, !dbg !64 - store i32 1, i32* %78, align 8, !dbg !64, !tbaa !79 - %79 = getelementptr inbounds i8, i8* %72, i64 12, !dbg !64 - %80 = getelementptr inbounds i8, i8* %72, i64 4, !dbg !64 - %81 = bitcast i8* %80 to i32*, !dbg !64 - tail call void @llvm.memset.p0i8.i64(i8* nonnull align 4 %79, i8 0, i64 20, i1 false) #18, !dbg !64 - store i32 1, i32* %81, align 4, !dbg !64, !tbaa !82 - store i64 %rubyId_arg.i, i64* %positional_table120.i, align 8, !dbg !64 - %82 = tail call noalias nonnull i8* @ruby_xmalloc2(i64 noundef 1, i64 noundef 8) #19, !dbg !64 - call void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture nonnull writeonly align 1 %82, i8* nocapture noundef nonnull readonly align 8 dereferenceable(8) %11, i64 noundef 8, i1 noundef false) #18, !dbg !64 - %83 = getelementptr inbounds i8, i8* %72, i64 32, !dbg !64 - %84 = bitcast i8* %83 to i8**, !dbg !64 - store i8* %82, i8** %84, align 8, !dbg !64, !tbaa !83 - tail call void @sorbet_vm_define_method(i64 %19, i8* getelementptr inbounds ([218 x i8], [218 x i8]* @sorbet_moduleStringTable, i64 0, i64 130), i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)* noundef @"func_Object#8fooArray", i8* nonnull %72, %struct.rb_iseq_struct* %stackFrame118.i, i1 noundef zeroext false) #18, !dbg !64 - store i64* getelementptr inbounds ([30 x i64], [30 x i64]* @iseqEncodedArray, i64 0, i64 25), i64** %18, align 8, !dbg !64, !tbaa !25 - %85 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !10 - %86 = load i64*, i64** %85, align 8, !dbg !10 - store i64 %2, i64* %86, align 8, !dbg !10, !tbaa !6 - %87 = getelementptr inbounds i64, i64* %86, i64 1, !dbg !10 - store i64 5, i64* %87, align 8, !dbg !10, !tbaa !6 - %88 = getelementptr inbounds i64, i64* %87, i64 1, !dbg !10 - store i64* %88, i64** %85, align 8, !dbg !10 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_fooInt, i64 0), !dbg !10 - %89 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !15 - %90 = load i64*, i64** %89, align 8, !dbg !15 - store i64 %2, i64* %90, align 8, !dbg !15, !tbaa !6 - %91 = getelementptr inbounds i64, i64* %90, i64 1, !dbg !15 - store i64 %send, i64* %91, align 8, !dbg !15, !tbaa !6 - %92 = getelementptr inbounds i64, i64* %91, i64 1, !dbg !15 - store i64* %92, i64** %89, align 8, !dbg !15 - %send2 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts, i64 0), !dbg !15 - store i64* getelementptr inbounds ([30 x i64], [30 x i64]* @iseqEncodedArray, i64 0, i64 26), i64** %18, align 8, !dbg !15, !tbaa !25 - %93 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !16 - %94 = load i64*, i64** %93, align 8, !dbg !16 - store i64 %2, i64* %94, align 8, !dbg !16, !tbaa !6 - %95 = getelementptr inbounds i64, i64* %94, i64 1, !dbg !16 - store i64 5, i64* %95, align 8, !dbg !16, !tbaa !6 - %96 = getelementptr inbounds i64, i64* %95, i64 1, !dbg !16 - store i64* %96, i64** %93, align 8, !dbg !16 - %send4 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_fooAny1, i64 0), !dbg !16 - %97 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !17 - %98 = load i64*, i64** %97, align 8, !dbg !17 - store i64 %2, i64* %98, align 8, !dbg !17, !tbaa !6 - %99 = getelementptr inbounds i64, i64* %98, i64 1, !dbg !17 - store i64 %send4, i64* %99, align 8, !dbg !17, !tbaa !6 - %100 = getelementptr inbounds i64, i64* %99, i64 1, !dbg !17 - store i64* %100, i64** %97, align 8, !dbg !17 - %send6 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts.1, i64 0), !dbg !17 - store i64* getelementptr inbounds ([30 x i64], [30 x i64]* @iseqEncodedArray, i64 0, i64 27), i64** %18, align 8, !dbg !17, !tbaa !25 - %101 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !18 - %102 = load i64*, i64** %101, align 8, !dbg !18 - store i64 %2, i64* %102, align 8, !dbg !18, !tbaa !6 - %103 = getelementptr inbounds i64, i64* %102, i64 1, !dbg !18 - store i64 5, i64* %103, align 8, !dbg !18, !tbaa !6 - %104 = getelementptr inbounds i64, i64* %103, i64 1, !dbg !18 - store i64* %104, i64** %101, align 8, !dbg !18 - %send8 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_fooAny2, i64 0), !dbg !18 - %105 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !19 - %106 = load i64*, i64** %105, align 8, !dbg !19 - store i64 %2, i64* %106, align 8, !dbg !19, !tbaa !6 - %107 = getelementptr inbounds i64, i64* %106, i64 1, !dbg !19 - store i64 %send8, i64* %107, align 8, !dbg !19, !tbaa !6 - %108 = getelementptr inbounds i64, i64* %107, i64 1, !dbg !19 - store i64* %108, i64** %105, align 8, !dbg !19 - %send10 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts.2, i64 0), !dbg !19 - store i64* getelementptr inbounds ([30 x i64], [30 x i64]* @iseqEncodedArray, i64 0, i64 28), i64** %18, align 8, !dbg !19, !tbaa !25 - %109 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !20 - %110 = load i64*, i64** %109, align 8, !dbg !20 - store i64 %2, i64* %110, align 8, !dbg !20, !tbaa !6 - %111 = getelementptr inbounds i64, i64* %110, i64 1, !dbg !20 - store i64 5, i64* %111, align 8, !dbg !20, !tbaa !6 - %112 = getelementptr inbounds i64, i64* %111, i64 1, !dbg !20 - store i64* %112, i64** %109, align 8, !dbg !20 - %send12 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_fooAll, i64 0), !dbg !20 - %113 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !21 - %114 = load i64*, i64** %113, align 8, !dbg !21 - store i64 %2, i64* %114, align 8, !dbg !21, !tbaa !6 - %115 = getelementptr inbounds i64, i64* %114, i64 1, !dbg !21 - store i64 %send12, i64* %115, align 8, !dbg !21, !tbaa !6 - %116 = getelementptr inbounds i64, i64* %115, i64 1, !dbg !21 - store i64* %116, i64** %113, align 8, !dbg !21 - %send14 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts.3, i64 0), !dbg !21 - store i64* getelementptr inbounds ([30 x i64], [30 x i64]* @iseqEncodedArray, i64 0, i64 29), i64** %18, align 8, !dbg !21, !tbaa !25 - %callArgs0Addr.i = getelementptr [4 x i64], [4 x i64]* %callArgs.i, i64 0, i64 0, !dbg !84 - store i64 5, i64* %callArgs0Addr.i, align 8, !dbg !84 - tail call void @llvm.experimental.noalias.scope.decl(metadata !85) #18, !dbg !84 - %117 = call i64 @rb_ary_new_from_values(i64 noundef 1, i64* noundef nonnull align 8 %callArgs0Addr.i) #18, !dbg !84 - %118 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !22 - %119 = load i64*, i64** %118, align 8, !dbg !22 - store i64 %2, i64* %119, align 8, !dbg !22, !tbaa !6 - %120 = getelementptr inbounds i64, i64* %119, i64 1, !dbg !22 - store i64 %117, i64* %120, align 8, !dbg !22, !tbaa !6 - %121 = getelementptr inbounds i64, i64* %120, i64 1, !dbg !22 - store i64* %121, i64** %118, align 8, !dbg !22 - %send16 = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_fooArray, i64 0), !dbg !22 - %122 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !23 - %123 = load i64*, i64** %122, align 8, !dbg !23 - store i64 %2, i64* %123, align 8, !dbg !23, !tbaa !6 - %124 = getelementptr inbounds i64, i64* %123, i64 1, !dbg !23 - store i64 %send16, i64* %124, align 8, !dbg !23, !tbaa !6 - %125 = getelementptr inbounds i64, i64* %124, i64 1, !dbg !23 - store i64* %125, i64** %122, align 8, !dbg !23 - %send18 = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts.4, i64 0), !dbg !23 - call void @llvm.lifetime.end.p0i8(i64 32, i8* nonnull %6) - call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %7) - call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %8) - call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %9) - call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %10) - call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %11) - ret void -} - -; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly -declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #12 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #7 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #7 - -; Function Attrs: cold minsize noreturn nounwind sspreq uwtable -define internal fastcc void @"func_Object#6fooAll.cold.1"(i64 %rawArg_arg) unnamed_addr #13 !dbg !88 { -newFuncRoot: - tail call void @sorbet_cast_failure(i64 %rawArg_arg, i8* getelementptr inbounds ([218 x i8], [218 x i8]* @sorbet_moduleStringTable, i64 0, i64 46), i8* getelementptr inbounds ([218 x i8], [218 x i8]* @sorbet_moduleStringTable, i64 0, i64 39)) #15, !dbg !90 - unreachable, !dbg !90 -} - -; Function Attrs: cold minsize noreturn nounwind sspreq uwtable -define internal fastcc void @"func_Object#7fooAny1.cold.1"(i64 %rawArg_arg) unnamed_addr #13 !dbg !91 { -newFuncRoot: - tail call void @sorbet_cast_failure(i64 %rawArg_arg, i8* getelementptr inbounds ([218 x i8], [218 x i8]* @sorbet_moduleStringTable, i64 0, i64 46), i8* getelementptr inbounds ([218 x i8], [218 x i8]* @sorbet_moduleStringTable, i64 0, i64 61)) #15, !dbg !92 - unreachable, !dbg !92 -} - -; Function Attrs: cold minsize noreturn nounwind sspreq uwtable -define internal fastcc void @"func_Object#7fooAny2.cold.1"(i64 %rawArg_arg) unnamed_addr #13 !dbg !93 { -newFuncRoot: - tail call void @sorbet_cast_failure(i64 %rawArg_arg, i8* getelementptr inbounds ([218 x i8], [218 x i8]* @sorbet_moduleStringTable, i64 0, i64 46), i8* getelementptr inbounds ([218 x i8], [218 x i8]* @sorbet_moduleStringTable, i64 0, i64 91)) #15, !dbg !94 - unreachable, !dbg !94 -} - -; Function Attrs: cold minsize noreturn nounwind sspreq uwtable -define internal fastcc void @"func_Object#6fooInt.cold.1"(i64 %rawArg_arg) unnamed_addr #13 !dbg !95 { -newFuncRoot: - tail call void @sorbet_cast_failure(i64 %rawArg_arg, i8* getelementptr inbounds ([218 x i8], [218 x i8]* @sorbet_moduleStringTable, i64 0, i64 46), i8* getelementptr inbounds ([218 x i8], [218 x i8]* @sorbet_moduleStringTable, i64 0, i64 120)) #15, !dbg !96 - unreachable, !dbg !96 -} - -; Function Attrs: cold minsize noreturn nounwind sspreq uwtable -define internal fastcc void @"func_Object#8fooArray.cold.1"(i64 %rawArg_arg) unnamed_addr #13 !dbg !97 { -newFuncRoot: - tail call void @sorbet_cast_failure(i64 %rawArg_arg, i8* getelementptr inbounds ([218 x i8], [218 x i8]* @sorbet_moduleStringTable, i64 0, i64 46), i8* getelementptr inbounds ([218 x i8], [218 x i8]* @sorbet_moduleStringTable, i64 0, i64 139)) #15, !dbg !98 - unreachable, !dbg !98 -} - -attributes #0 = { nounwind readnone willreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #1 = { cold noreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #2 = { noreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #3 = { "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #4 = { nofree nosync nounwind readnone speculatable willreturn } -attributes #5 = { inaccessiblememonly nofree nosync nounwind willreturn } -attributes #6 = { allocsize(0,1) "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #7 = { argmemonly nofree nosync nounwind willreturn } -attributes #8 = { nounwind ssp uwtable "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #9 = { nounwind sspreq uwtable } -attributes #10 = { ssp } -attributes #11 = { sspreq } -attributes #12 = { argmemonly nofree nosync nounwind willreturn writeonly } -attributes #13 = { cold minsize noreturn nounwind sspreq uwtable } -attributes #14 = { noreturn nounwind } -attributes #15 = { noreturn } -attributes #16 = { noinline } -attributes #17 = { nounwind willreturn } -attributes #18 = { nounwind } -attributes #19 = { nounwind allocsize(0,1) } - -!llvm.module.flags = !{!0, !1, !2} -!llvm.dbg.cu = !{!3} - -!0 = !{i32 2, !"Debug Info Version", i32 3} -!1 = !{i32 4, !"cf-protection-return", i32 1} -!2 = !{i32 4, !"cf-protection-branch", i32 1} -!3 = distinct !DICompileUnit(language: DW_LANG_C, file: !4, producer: "Sorbet LLVM", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !5) -!4 = !DIFile(filename: "test/testdata/compiler/casts.rb", directory: ".") -!5 = !{} -!6 = !{!7, !7, i64 0} -!7 = !{!"long", !8, i64 0} -!8 = !{!"omnipotent char", !9, i64 0} -!9 = !{!"Simple C/C++ TBAA"} -!10 = !DILocation(line: 25, column: 6, scope: !11) -!11 = distinct !DISubprogram(name: ".", linkageName: "func_.13", scope: null, file: !4, line: 5, type: !12, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!12 = !DISubroutineType(types: !13) -!13 = !{!14} -!14 = !DIBasicType(name: "VALUE", size: 64, encoding: DW_ATE_signed) -!15 = !DILocation(line: 25, column: 1, scope: !11) -!16 = !DILocation(line: 26, column: 6, scope: !11) -!17 = !DILocation(line: 26, column: 1, scope: !11) -!18 = !DILocation(line: 27, column: 6, scope: !11) -!19 = !DILocation(line: 27, column: 1, scope: !11) -!20 = !DILocation(line: 28, column: 6, scope: !11) -!21 = !DILocation(line: 28, column: 1, scope: !11) -!22 = !DILocation(line: 29, column: 6, scope: !11) -!23 = !DILocation(line: 29, column: 1, scope: !11) -!24 = distinct !DISubprogram(name: "Object#fooAll", linkageName: "func_Object#6fooAll", scope: null, file: !4, line: 5, type: !12, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!25 = !{!26, !26, i64 0} -!26 = !{!"any pointer", !8, i64 0} -!27 = !DILocation(line: 5, column: 1, scope: !24) -!28 = !{!"branch_weights", i32 4001, i32 4000000} -!29 = !DILocation(line: 5, column: 12, scope: !24) -!30 = !DILocation(line: 6, column: 3, scope: !24) -!31 = !{!"branch_weights", i32 2000, i32 1} -!32 = distinct !DISubprogram(name: "Object#fooAny1", linkageName: "func_Object#7fooAny1", scope: null, file: !4, line: 9, type: !12, scopeLine: 9, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!33 = !DILocation(line: 9, column: 1, scope: !32) -!34 = !DILocation(line: 9, column: 13, scope: !32) -!35 = !DILocation(line: 10, column: 3, scope: !32) -!36 = !{!"branch_weights", i32 1, i32 2000} -!37 = !{!38, !7, i64 0} -!38 = !{!"RBasic", !7, i64 0, !7, i64 8} -!39 = distinct !DISubprogram(name: "Object#fooAny2", linkageName: "func_Object#7fooAny2", scope: null, file: !4, line: 13, type: !12, scopeLine: 13, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!40 = !DILocation(line: 13, column: 1, scope: !39) -!41 = !DILocation(line: 13, column: 13, scope: !39) -!42 = !DILocation(line: 14, column: 3, scope: !39) -!43 = distinct !DISubprogram(name: "Object#fooInt", linkageName: "func_Object#6fooInt", scope: null, file: !4, line: 17, type: !12, scopeLine: 17, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!44 = !DILocation(line: 17, column: 1, scope: !43) -!45 = !DILocation(line: 17, column: 12, scope: !43) -!46 = !DILocation(line: 18, column: 3, scope: !43) -!47 = !{!48} -!48 = distinct !{!48, !49, !"sorbet_rb_int_plus: argument 0"} -!49 = distinct !{!49, !"sorbet_rb_int_plus"} -!50 = !{!51, !52, i64 40} -!51 = !{!"rb_execution_context_struct", !26, i64 0, !7, i64 8, !26, i64 16, !26, i64 24, !26, i64 32, !52, i64 40, !52, i64 44, !26, i64 48, !26, i64 56, !26, i64 64, !7, i64 72, !7, i64 80, !26, i64 88, !7, i64 96, !26, i64 104, !26, i64 112, !7, i64 120, !7, i64 128, !8, i64 136, !8, i64 137, !7, i64 144, !53, i64 152} -!52 = !{!"int", !8, i64 0} -!53 = !{!"", !26, i64 0, !26, i64 8, !7, i64 16, !8, i64 24} -!54 = !{!51, !52, i64 44} -!55 = !{!51, !26, i64 56} -!56 = distinct !DISubprogram(name: "Object#fooArray", linkageName: "func_Object#8fooArray", scope: null, file: !4, line: 21, type: !12, scopeLine: 21, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!57 = !DILocation(line: 21, column: 1, scope: !56) -!58 = !DILocation(line: 21, column: 14, scope: !56) -!59 = !DILocation(line: 22, column: 3, scope: !56) -!60 = !DILocation(line: 5, column: 1, scope: !11) -!61 = !DILocation(line: 9, column: 1, scope: !11) -!62 = !DILocation(line: 13, column: 1, scope: !11) -!63 = !DILocation(line: 17, column: 1, scope: !11) -!64 = !DILocation(line: 21, column: 1, scope: !11) -!65 = !{!66, !7, i64 400} -!66 = !{!"rb_vm_struct", !7, i64 0, !67, i64 8, !26, i64 192, !26, i64 200, !26, i64 208, !70, i64 216, !8, i64 224, !68, i64 264, !68, i64 280, !68, i64 296, !68, i64 312, !7, i64 328, !52, i64 336, !52, i64 340, !52, i64 344, !52, i64 344, !52, i64 344, !52, i64 344, !52, i64 348, !7, i64 352, !8, i64 360, !7, i64 400, !7, i64 408, !7, i64 416, !7, i64 424, !7, i64 432, !7, i64 440, !7, i64 448, !26, i64 456, !26, i64 464, !71, i64 472, !72, i64 992, !26, i64 1016, !26, i64 1024, !52, i64 1032, !52, i64 1036, !68, i64 1040, !8, i64 1056, !7, i64 1096, !7, i64 1104, !7, i64 1112, !7, i64 1120, !7, i64 1128, !52, i64 1136, !26, i64 1144, !26, i64 1152, !26, i64 1160, !26, i64 1168, !26, i64 1176, !26, i64 1184, !52, i64 1192, !73, i64 1200, !8, i64 1232} -!67 = !{!"rb_global_vm_lock_struct", !26, i64 0, !8, i64 8, !68, i64 48, !26, i64 64, !52, i64 72, !8, i64 80, !8, i64 128, !52, i64 176, !52, i64 180} -!68 = !{!"list_head", !69, i64 0} -!69 = !{!"list_node", !26, i64 0, !26, i64 8} -!70 = !{!"long long", !8, i64 0} -!71 = !{!"", !8, i64 0} -!72 = !{!"rb_hook_list_struct", !26, i64 0, !52, i64 8, !52, i64 12, !52, i64 16} -!73 = !{!"", !7, i64 0, !7, i64 8, !7, i64 16, !7, i64 24} -!74 = !{!51, !26, i64 16} -!75 = !{!76, !26, i64 16} -!76 = !{!"rb_control_frame_struct", !26, i64 0, !26, i64 8, !26, i64 16, !7, i64 24, !26, i64 32, !26, i64 40, !26, i64 48} -!77 = !{!76, !26, i64 32} -!78 = !DILocation(line: 0, scope: !11) -!79 = !{!80, !52, i64 8} -!80 = !{!"rb_sorbet_param_struct", !81, i64 0, !52, i64 4, !52, i64 8, !52, i64 12, !52, i64 16, !52, i64 20, !52, i64 24, !52, i64 28, !26, i64 32, !52, i64 40, !52, i64 44, !52, i64 48, !52, i64 52, !26, i64 56} -!81 = !{!"", !52, i64 0, !52, i64 0, !52, i64 0, !52, i64 0, !52, i64 0, !52, i64 0, !52, i64 0, !52, i64 0, !52, i64 1, !52, i64 1} -!82 = !{!80, !52, i64 4} -!83 = !{!80, !26, i64 32} -!84 = !DILocation(line: 29, column: 15, scope: !11) -!85 = !{!86} -!86 = distinct !{!86, !87, !"sorbet_buildArrayIntrinsic: argument 0"} -!87 = distinct !{!87, !"sorbet_buildArrayIntrinsic"} -!88 = distinct !DISubprogram(name: "func_Object#6fooAll.cold.1", linkageName: "func_Object#6fooAll.cold.1", scope: null, file: !4, type: !89, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !3, retainedNodes: !5) -!89 = !DISubroutineType(types: !5) -!90 = !DILocation(line: 6, column: 3, scope: !88) -!91 = distinct !DISubprogram(name: "func_Object#7fooAny1.cold.1", linkageName: "func_Object#7fooAny1.cold.1", scope: null, file: !4, type: !89, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !3, retainedNodes: !5) -!92 = !DILocation(line: 10, column: 3, scope: !91) -!93 = distinct !DISubprogram(name: "func_Object#7fooAny2.cold.1", linkageName: "func_Object#7fooAny2.cold.1", scope: null, file: !4, type: !89, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !3, retainedNodes: !5) -!94 = !DILocation(line: 14, column: 3, scope: !93) -!95 = distinct !DISubprogram(name: "func_Object#6fooInt.cold.1", linkageName: "func_Object#6fooInt.cold.1", scope: null, file: !4, type: !89, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !3, retainedNodes: !5) -!96 = !DILocation(line: 18, column: 3, scope: !95) -!97 = distinct !DISubprogram(name: "func_Object#8fooArray.cold.1", linkageName: "func_Object#8fooArray.cold.1", scope: null, file: !4, type: !89, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !3, retainedNodes: !5) -!98 = !DILocation(line: 22, column: 3, scope: !97) diff --git a/test/testdata/compiler/class.opt.ll.exp b/test/testdata/compiler/class.opt.ll.exp deleted file mode 100644 index f3afd47a12..0000000000 --- a/test/testdata/compiler/class.opt.ll.exp +++ /dev/null @@ -1,348 +0,0 @@ -; ModuleID = 'payload' -source_filename = "llvm-link" -target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } -%struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } -%struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } -%struct.anon.0 = type { i16, [2 x i8] } -%struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } -%struct.rb_iseq_location_struct = type { i64, i64, i64, i64, i32, %struct.rb_code_location_struct } -%struct.rb_code_location_struct = type { %struct.rb_code_position_struct, %struct.rb_code_position_struct } -%struct.rb_code_position_struct = type { i32, i32 } -%struct.iseq_insn_info = type { %struct.iseq_insn_info_entry*, i32*, i32, %struct.succ_index_table* } -%struct.iseq_insn_info_entry = type opaque -%struct.succ_index_table = type opaque -%struct.iseq_catch_table = type opaque -%union.iseq_inline_storage_entry = type { %struct.iseq_inline_cache_entry } -%struct.iseq_inline_cache_entry = type { i64, %struct.rb_cref_struct*, i64 } -%struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } -%struct.rb_scope_visi_struct = type { i8, [3 x i8] } -%struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } -%struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } -%union.anon.13 = type { %struct.rb_method_cfunc_struct } -%struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%struct.rb_calling_info = type { i64, i64, i32, i32 } -%union.anon.15 = type { i32 } -%struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.16 = type { i64, i64, i64, i64* } -%union.anon.17 = type { %struct.anon.18 } -%struct.anon.18 = type { i64, i32 } -%struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } -%struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } -%struct.rb_fiber_struct = type opaque -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } -%struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } -%struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.__pthread_cond_s } -%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } -%union.anon = type { i64 } -%struct.anon.5 = type { [65 x i64] } -%struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } -%struct.rb_event_hook_struct = type opaque -%struct.rb_postponed_job_struct = type opaque -%struct.list_head = type { %struct.list_node } -%struct.rb_objspace = type opaque -%struct.rb_at_exit_list = type { void (%struct.rb_vm_struct*)*, %struct.rb_at_exit_list* } -%struct.rb_builtin_function = type opaque -%struct.anon.6 = type { i64, i64, i64, i64 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } -%union.anon.9 = type { %union.pthread_cond_t } -%union.pthread_mutex_t = type { %struct.__pthread_mutex_s } -%struct.__pthread_mutex_s = type { i32, i32, i32, i32, i32, i16, i16, %struct.__pthread_internal_list } -%struct.__pthread_internal_list = type { %struct.__pthread_internal_list*, %struct.__pthread_internal_list* } -%struct.rb_unblock_callback = type { void (i8*)*, i8* } -%struct.rb_mutex_struct = type opaque -%struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.10 = type { %struct.anon.11 } -%struct.anon.11 = type { i64, i64, i32 } -%struct.st_table = type { i8, i8, i8, i32, %struct.st_hash_type*, i64, i64*, i64, i64, %struct.st_table_entry* } -%struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } -%struct.st_table_entry = type opaque -%struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } -%struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } -%struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } -%struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } -%struct.__sigset_t = type { [16 x i64] } -%struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } - -@ruby_current_execution_context_ptr = external local_unnamed_addr global %struct.rb_execution_context_struct*, align 8 -@ruby_vm_global_constant_state = external local_unnamed_addr global i64, align 8 -@rb_eRuntimeError = external local_unnamed_addr global i64, align 8 -@.str.9 = private unnamed_addr constant [95 x i8] c"sorbet_getBuildSCMRevision: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@.str.10 = private unnamed_addr constant [93 x i8] c"sorbet_getIsReleaseBuild: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@"stackFramePrecomputed_func_.13" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@iseqEncodedArray = internal global [10 x i64] zeroinitializer -@fileLineNumberInfo = internal global %struct.SorbetLineNumberInfo zeroinitializer -@"stackFramePrecomputed_func_Foo.13" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"stackFramePrecomputed_func_Foo::Bar.13" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"stackFramePrecomputed_func_Baz.13" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@sorbet_moduleStringTable = internal unnamed_addr constant [117 x i8] c"\00test/testdata/compiler/class.rb\00Foo\00Baz\00Object\00master\00\00Bar\00\00\00", align 1 -@sorbet_moduleIDTable = internal unnamed_addr global [4 x i64] zeroinitializer, align 8 -@sorbet_moduleIDDescriptors = internal unnamed_addr constant [4 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 16 }, %struct.rb_code_position_struct { i32 71, i32 12 }, %struct.rb_code_position_struct { i32 88, i32 16 }, %struct.rb_code_position_struct { i32 105, i32 11 }], align 8 -@sorbet_moduleRubyStringTable = internal unnamed_addr global [5 x i64] zeroinitializer, align 8 -@sorbet_moduleRubyStringDescriptors = internal unnamed_addr constant [5 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 16 }, %struct.rb_code_position_struct { i32 17, i32 31 }, %struct.rb_code_position_struct { i32 71, i32 12 }, %struct.rb_code_position_struct { i32 88, i32 16 }, %struct.rb_code_position_struct { i32 105, i32 11 }], align 8 -@guard_epoch_Foo = linkonce local_unnamed_addr global i64 0 -@guarded_const_Foo = linkonce local_unnamed_addr global i64 0 -@rb_cObject = external local_unnamed_addr constant i64 - -declare %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64, i64, i64, i64, %struct.rb_iseq_struct*, i32, i32, %struct.SorbetLineNumberInfo*, i64*, i32, i32) local_unnamed_addr #0 - -declare void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo*, i64*, i32) local_unnamed_addr #0 - -declare i64 @sorbet_getConstant(i8*, i64) local_unnamed_addr #0 - -declare i64 @sorbet_readRealpath() local_unnamed_addr #0 - -declare %struct.rb_control_frame_struct* @sorbet_pushStaticInitFrame(i64) local_unnamed_addr #0 - -declare void @sorbet_popFrame() local_unnamed_addr #0 - -declare void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_iseq_struct*) local_unnamed_addr #0 - -declare void @sorbet_vm_intern_ids(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #0 - -declare void @sorbet_vm_init_string_table(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #0 - -declare i64 @rb_define_module(i8*) local_unnamed_addr #0 - -declare i64 @rb_define_class(i8*, i64) local_unnamed_addr #0 - -declare i64 @rb_define_class_under(i64, i8*, i64) local_unnamed_addr #0 - -; Function Attrs: noreturn -declare void @rb_raise(i64, i8*, ...) local_unnamed_addr #1 - -; Function Attrs: nounwind ssp uwtable -define weak i32 @sorbet_getIsReleaseBuild() local_unnamed_addr #2 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([93 x i8], [93 x i8]* @.str.10, i64 0, i64 0)) #6 - unreachable -} - -; Function Attrs: nounwind ssp uwtable -define weak i8* @sorbet_getBuildSCMRevision() local_unnamed_addr #2 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([95 x i8], [95 x i8]* @.str.9, i64 0, i64 0)) #6 - unreachable -} - -define internal fastcc void @sorbet_globalConstructors(i64 %realpath) unnamed_addr { -allocRubyIds: - tail call void @sorbet_vm_intern_ids(i64* noundef getelementptr inbounds ([4 x i64], [4 x i64]* @sorbet_moduleIDTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([4 x %struct.rb_code_position_struct], [4 x %struct.rb_code_position_struct]* @sorbet_moduleIDDescriptors, i32 0, i32 0), i32 noundef 4, i8* noundef getelementptr inbounds ([117 x i8], [117 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_vm_init_string_table(i64* noundef getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleRubyStringTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([5 x %struct.rb_code_position_struct], [5 x %struct.rb_code_position_struct]* @sorbet_moduleRubyStringDescriptors, i32 0, i32 0), i32 noundef 5, i8* noundef getelementptr inbounds ([117 x i8], [117 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef getelementptr inbounds ([10 x i64], [10 x i64]* @iseqEncodedArray, i32 0, i32 0), i32 noundef 10) - tail call fastcc void @"Constr_stackFramePrecomputed_func_.13"(i64 %realpath) - tail call fastcc void @"Constr_stackFramePrecomputed_func_Foo.13"(i64 %realpath) - tail call fastcc void @"Constr_stackFramePrecomputed_func_Foo::Bar.13"(i64 %realpath) - tail call fastcc void @"Constr_stackFramePrecomputed_func_Baz.13"(i64 %realpath) - ret void -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_.13"(i64 %realpath) unnamed_addr #3 { -entryInitializers: - %"rubyId_" = load i64, i64* getelementptr inbounds ([4 x i64], [4 x i64]* @sorbet_moduleIDTable, i64 0, i64 0), align 8, !invariant.load !5 - %"rubyStr_" = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 0), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/class.rb" = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_", i64 %"rubyId_", i64 %"rubyStr_test/testdata/compiler/class.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 0, i32 noundef 4, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 2) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - ret void -} - -; Function Attrs: sspreq -define void @Init_class() local_unnamed_addr #4 { -entry: - %realpath = tail call i64 @sorbet_readRealpath() - tail call fastcc void @sorbet_globalConstructors(i64 %realpath) - %0 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !10 - %1 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %0, i64 0, i32 2 - %2 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %1, align 8, !tbaa !12 - %stackFrame.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - %3 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame.i, %struct.rb_iseq_struct** %3, align 8, !tbaa !16 - %4 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 4 - %5 = load i64*, i64** %4, align 8, !tbaa !18 - %6 = load i64, i64* %5, align 8, !tbaa !6 - %7 = and i64 %6, -33 - store i64 %7, i64* %5, align 8, !tbaa !6 - tail call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %0, %struct.rb_control_frame_struct* %2, %struct.rb_iseq_struct* %stackFrame.i) #7 - %8 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 0 - store i64* getelementptr inbounds ([10 x i64], [10 x i64]* @iseqEncodedArray, i64 0, i64 4), i64** %8, align 8, !dbg !19, !tbaa !10 - %9 = tail call i64 @rb_define_module(i8* getelementptr inbounds ([117 x i8], [117 x i8]* @sorbet_moduleStringTable, i64 0, i64 49)) #7, !dbg !24 - %10 = tail call %struct.rb_control_frame_struct* @sorbet_pushStaticInitFrame(i64 %9) #7, !dbg !24 - %stackFrame.i1.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Foo.13", align 8 - %11 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !10 - %12 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %11, i64 0, i32 2 - %13 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %12, align 8, !tbaa !12 - %14 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %13, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame.i1.i, %struct.rb_iseq_struct** %14, align 8, !tbaa !16 - %15 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %13, i64 0, i32 4 - %16 = load i64*, i64** %15, align 8, !tbaa !18 - %17 = load i64, i64* %16, align 8, !tbaa !6 - %18 = and i64 %17, -33 - store i64 %18, i64* %16, align 8, !tbaa !6 - tail call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %11, %struct.rb_control_frame_struct* %13, %struct.rb_iseq_struct* %stackFrame.i1.i) #7 - %19 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %10, i64 0, i32 0 - store i64* getelementptr inbounds ([10 x i64], [10 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %19, align 8, !dbg !25, !tbaa !10 - %20 = load i64, i64* @guard_epoch_Foo, align 8, !dbg !28 - %21 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !28, !tbaa !29 - %needTakeSlowPath = icmp ne i64 %20, %21, !dbg !28 - br i1 %needTakeSlowPath, label %22, label %23, !dbg !28, !prof !31 - -22: ; preds = %entry - tail call void @const_recompute_Foo(), !dbg !28 - br label %23, !dbg !28 - -23: ; preds = %entry, %22 - %24 = load i64, i64* @guarded_const_Foo, align 8, !dbg !28 - %25 = load i64, i64* @guard_epoch_Foo, align 8, !dbg !28 - %26 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !28, !tbaa !29 - %guardUpdated = icmp eq i64 %25, %26, !dbg !28 - tail call void @llvm.assume(i1 %guardUpdated), !dbg !28 - %27 = load i64, i64* @rb_cObject, align 8, !dbg !28 - %28 = tail call i64 @rb_define_class_under(i64 %24, i8* getelementptr inbounds ([117 x i8], [117 x i8]* @sorbet_moduleStringTable, i64 0, i64 84), i64 %27) #7, !dbg !28 - %29 = tail call %struct.rb_control_frame_struct* @sorbet_pushStaticInitFrame(i64 %28) #7, !dbg !28 - %stackFrame.i.i.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Foo::Bar.13", align 8 - %30 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !10 - %31 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %30, i64 0, i32 2 - %32 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %31, align 8, !tbaa !12 - %33 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %32, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame.i.i.i, %struct.rb_iseq_struct** %33, align 8, !tbaa !16 - %34 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %32, i64 0, i32 4 - %35 = load i64*, i64** %34, align 8, !tbaa !18 - %36 = load i64, i64* %35, align 8, !tbaa !6 - %37 = and i64 %36, -33 - store i64 %37, i64* %35, align 8, !tbaa !6 - tail call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %30, %struct.rb_control_frame_struct* %32, %struct.rb_iseq_struct* %stackFrame.i.i.i) #7 - %38 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %29, i64 0, i32 0 - store i64* getelementptr inbounds ([10 x i64], [10 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %38, align 8, !dbg !32, !tbaa !10 - tail call void @sorbet_popFrame() #7, !dbg !28 - tail call void @sorbet_popFrame() #7, !dbg !24 - store i64* getelementptr inbounds ([10 x i64], [10 x i64]* @iseqEncodedArray, i64 0, i64 8), i64** %8, align 8, !dbg !24, !tbaa !10 - %39 = tail call i64 @rb_define_class(i8* getelementptr inbounds ([117 x i8], [117 x i8]* @sorbet_moduleStringTable, i64 0, i64 53), i64 %27) #7, !dbg !35 - %40 = tail call %struct.rb_control_frame_struct* @sorbet_pushStaticInitFrame(i64 %39) #7, !dbg !35 - %stackFrame.i.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Baz.13", align 8 - %41 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !10 - %42 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %41, i64 0, i32 2 - %43 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %42, align 8, !tbaa !12 - %44 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %43, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame.i.i, %struct.rb_iseq_struct** %44, align 8, !tbaa !16 - %45 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %43, i64 0, i32 4 - %46 = load i64*, i64** %45, align 8, !tbaa !18 - %47 = load i64, i64* %46, align 8, !tbaa !6 - %48 = and i64 %47, -33 - store i64 %48, i64* %46, align 8, !tbaa !6 - tail call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %41, %struct.rb_control_frame_struct* %43, %struct.rb_iseq_struct* %stackFrame.i.i) #7 - %49 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %40, i64 0, i32 0 - store i64* getelementptr inbounds ([10 x i64], [10 x i64]* @iseqEncodedArray, i64 0, i64 8), i64** %49, align 8, !dbg !36, !tbaa !10 - tail call void @sorbet_popFrame() #7, !dbg !35 - ret void -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_Foo.13"(i64 %realpath) unnamed_addr #3 { -entryInitializers: - %"rubyId_" = load i64, i64* getelementptr inbounds ([4 x i64], [4 x i64]* @sorbet_moduleIDTable, i64 0, i64 1), align 8, !invariant.load !5 - %"rubyStr_" = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 2), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/class.rb" = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_", i64 %"rubyId_", i64 %"rubyStr_test/testdata/compiler/class.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 3, i32 noundef 4, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 2) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Foo.13", align 8 - ret void -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_Foo::Bar.13"(i64 %realpath) unnamed_addr #3 { -entryInitializers: - %"rubyId_" = load i64, i64* getelementptr inbounds ([4 x i64], [4 x i64]* @sorbet_moduleIDTable, i64 0, i64 2), align 8, !invariant.load !5 - %"rubyStr_" = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 3), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/class.rb" = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_", i64 %"rubyId_", i64 %"rubyStr_test/testdata/compiler/class.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 3, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 0) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Foo::Bar.13", align 8 - ret void -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_Baz.13"(i64 %realpath) unnamed_addr #3 { -entryInitializers: - %"rubyId_" = load i64, i64* getelementptr inbounds ([4 x i64], [4 x i64]* @sorbet_moduleIDTable, i64 0, i64 3), align 8, !invariant.load !5 - %"rubyStr_" = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 4), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/class.rb" = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_", i64 %"rubyId_", i64 %"rubyStr_test/testdata/compiler/class.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 3, i32 noundef 8, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 0) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Baz.13", align 8 - ret void -} - -; Function Attrs: nofree nosync nounwind willreturn -declare void @llvm.assume(i1 noundef) #5 - -; Function Attrs: ssp -define linkonce void @const_recompute_Foo() local_unnamed_addr #3 { - %1 = tail call i64 @sorbet_getConstant(i8* getelementptr inbounds ([117 x i8], [117 x i8]* @sorbet_moduleStringTable, i64 0, i64 49), i64 3) - store i64 %1, i64* @guarded_const_Foo, align 8 - %2 = load i64, i64* @ruby_vm_global_constant_state, align 8, !tbaa !29 - store i64 %2, i64* @guard_epoch_Foo, align 8 - ret void -} - -attributes #0 = { "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #1 = { noreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #2 = { nounwind ssp uwtable "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #3 = { ssp } -attributes #4 = { sspreq } -attributes #5 = { nofree nosync nounwind willreturn } -attributes #6 = { noreturn nounwind } -attributes #7 = { nounwind } - -!llvm.module.flags = !{!0, !1, !2} -!llvm.dbg.cu = !{!3} - -!0 = !{i32 2, !"Debug Info Version", i32 3} -!1 = !{i32 4, !"cf-protection-return", i32 1} -!2 = !{i32 4, !"cf-protection-branch", i32 1} -!3 = distinct !DICompileUnit(language: DW_LANG_C, file: !4, producer: "Sorbet LLVM", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !5) -!4 = !DIFile(filename: "test/testdata/compiler/class.rb", directory: ".") -!5 = !{} -!6 = !{!7, !7, i64 0} -!7 = !{!"long", !8, i64 0} -!8 = !{!"omnipotent char", !9, i64 0} -!9 = !{!"Simple C/C++ TBAA"} -!10 = !{!11, !11, i64 0} -!11 = !{!"any pointer", !8, i64 0} -!12 = !{!13, !11, i64 16} -!13 = !{!"rb_execution_context_struct", !11, i64 0, !7, i64 8, !11, i64 16, !11, i64 24, !11, i64 32, !14, i64 40, !14, i64 44, !11, i64 48, !11, i64 56, !11, i64 64, !7, i64 72, !7, i64 80, !11, i64 88, !7, i64 96, !11, i64 104, !11, i64 112, !7, i64 120, !7, i64 128, !8, i64 136, !8, i64 137, !7, i64 144, !15, i64 152} -!14 = !{!"int", !8, i64 0} -!15 = !{!"", !11, i64 0, !11, i64 8, !7, i64 16, !8, i64 24} -!16 = !{!17, !11, i64 16} -!17 = !{!"rb_control_frame_struct", !11, i64 0, !11, i64 8, !11, i64 16, !7, i64 24, !11, i64 32, !11, i64 40, !11, i64 48} -!18 = !{!17, !11, i64 32} -!19 = !DILocation(line: 0, scope: !20) -!20 = distinct !DISubprogram(name: ".", linkageName: "func_.13", scope: null, file: !4, line: 4, type: !21, scopeLine: 4, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!21 = !DISubroutineType(types: !22) -!22 = !{!23} -!23 = !DIBasicType(name: "VALUE", size: 64, encoding: DW_ATE_signed) -!24 = !DILocation(line: 4, column: 1, scope: !20) -!25 = !DILocation(line: 0, scope: !26, inlinedAt: !27) -!26 = distinct !DISubprogram(name: "Foo.", linkageName: "func_Foo.13L61", scope: null, file: !4, line: 4, type: !21, scopeLine: 4, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!27 = distinct !DILocation(line: 4, column: 1, scope: !20) -!28 = !DILocation(line: 5, column: 3, scope: !26, inlinedAt: !27) -!29 = !{!30, !30, i64 0} -!30 = !{!"long long", !8, i64 0} -!31 = !{!"branch_weights", i32 1, i32 10000} -!32 = !DILocation(line: 0, scope: !33, inlinedAt: !34) -!33 = distinct !DISubprogram(name: "Bar.", linkageName: "func_Foo::Bar.13L74", scope: null, file: !4, line: 5, type: !21, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!34 = distinct !DILocation(line: 5, column: 3, scope: !26, inlinedAt: !27) -!35 = !DILocation(line: 8, column: 1, scope: !20) -!36 = !DILocation(line: 0, scope: !37, inlinedAt: !38) -!37 = distinct !DISubprogram(name: "Baz.", linkageName: "func_Baz.13L94", scope: null, file: !4, line: 8, type: !21, scopeLine: 8, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!38 = distinct !DILocation(line: 8, column: 1, scope: !20) diff --git a/test/testdata/compiler/classfields.opt.ll.exp b/test/testdata/compiler/classfields.opt.ll.exp deleted file mode 100644 index df5f241b4d..0000000000 --- a/test/testdata/compiler/classfields.opt.ll.exp +++ /dev/null @@ -1,661 +0,0 @@ -; ModuleID = 'payload' -source_filename = "llvm-link" -target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } -%struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.__pthread_cond_s } -%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } -%union.anon = type { i64 } -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } -%struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } -%struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } -%struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } -%struct.anon.0 = type { i16, [2 x i8] } -%struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } -%struct.rb_iseq_location_struct = type { i64, i64, i64, i64, i32, %struct.rb_code_location_struct } -%struct.rb_code_location_struct = type { %struct.rb_code_position_struct, %struct.rb_code_position_struct } -%struct.rb_code_position_struct = type { i32, i32 } -%struct.iseq_insn_info = type { %struct.iseq_insn_info_entry*, i32*, i32, %struct.succ_index_table* } -%struct.iseq_insn_info_entry = type opaque -%struct.succ_index_table = type opaque -%struct.iseq_catch_table = type opaque -%union.iseq_inline_storage_entry = type { %struct.iseq_inline_cache_entry } -%struct.iseq_inline_cache_entry = type { i64, %struct.rb_cref_struct*, i64 } -%struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } -%struct.rb_scope_visi_struct = type { i8, [3 x i8] } -%struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } -%struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } -%union.anon.13 = type { %struct.rb_method_cfunc_struct } -%struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%union.anon.15 = type { i32 } -%struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.16 = type { i64, i64, i64, i64* } -%union.anon.17 = type { %struct.anon.18 } -%struct.anon.18 = type { i64, i32 } -%struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } -%struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } -%struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } -%struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } -%struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } -%struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } -%struct.__sigset_t = type { [16 x i64] } -%struct.rb_calling_info = type { i64, i64, i32, i32 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } -%union.anon.9 = type { %union.pthread_cond_t } -%struct.rb_unblock_callback = type { void (i8*)*, i8* } -%struct.rb_mutex_struct = type opaque -%struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.10 = type { %struct.anon.11 } -%struct.anon.11 = type { i64, i64, i32 } -%struct.rb_fiber_struct = type opaque -%struct.anon.5 = type { [65 x i64] } -%struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } -%struct.rb_event_hook_struct = type opaque -%struct.rb_postponed_job_struct = type opaque -%struct.list_head = type { %struct.list_node } -%union.pthread_mutex_t = type { %struct.__pthread_mutex_s } -%struct.__pthread_mutex_s = type { i32, i32, i32, i32, i32, i16, i16, %struct.__pthread_internal_list } -%struct.__pthread_internal_list = type { %struct.__pthread_internal_list*, %struct.__pthread_internal_list* } -%struct.rb_objspace = type opaque -%struct.rb_at_exit_list = type { void (%struct.rb_vm_struct*)*, %struct.rb_at_exit_list* } -%struct.st_table = type { i8, i8, i8, i32, %struct.st_hash_type*, i64, i64*, i64, i64, %struct.st_table_entry* } -%struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } -%struct.st_table_entry = type opaque -%struct.rb_builtin_function = type opaque -%struct.anon.6 = type { i64, i64, i64, i64 } -%struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } -%struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } -%struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } -%struct.rb_call_info_with_kwarg = type { %struct.rb_call_info, %struct.rb_call_info_kw_arg* } -%struct.rb_call_info_kw_arg = type { i32, [1 x i64] } - -@ruby_current_vm_ptr = external local_unnamed_addr global %struct.rb_vm_struct*, align 8 -@ruby_current_execution_context_ptr = external local_unnamed_addr global %struct.rb_execution_context_struct*, align 8 -@ruby_vm_global_constant_state = external local_unnamed_addr global i64, align 8 -@rb_eRuntimeError = external local_unnamed_addr global i64, align 8 -@.str.9 = private unnamed_addr constant [95 x i8] c"sorbet_getBuildSCMRevision: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@.str.10 = private unnamed_addr constant [93 x i8] c"sorbet_getIsReleaseBuild: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@"stackFramePrecomputed_func_.13" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@iseqEncodedArray = internal global [29 x i64] zeroinitializer -@fileLineNumberInfo = internal global %struct.SorbetLineNumberInfo zeroinitializer -@ic_new = internal global %struct.FunctionInlineCache zeroinitializer -@ic_initialize = internal global %struct.FunctionInlineCache zeroinitializer -@ic_write = internal global %struct.FunctionInlineCache zeroinitializer -@ic_read = internal global %struct.FunctionInlineCache zeroinitializer -@ic_puts = internal global %struct.FunctionInlineCache zeroinitializer -@ic_new.1 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_initialize.2 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_read.3 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_puts.4 = internal global %struct.FunctionInlineCache zeroinitializer -@"stackFramePrecomputed_func_B#5write" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"stackFramePrecomputed_func_B#4read" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@stackFramePrecomputed_func_B.4read = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"stackFramePrecomputed_func_B.13" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@sorbet_moduleStringTable = internal unnamed_addr constant [125 x i8] c"\00test/testdata/compiler/classfields.rb\00B\00Object\00new\00initialize\00write\00read\00puts\00master\00@@f\00\00normal\00a\00", align 1 -@sorbet_moduleIDTable = internal unnamed_addr global [10 x i64] zeroinitializer, align 8 -@sorbet_moduleIDDescriptors = internal unnamed_addr constant [10 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 16 }, %struct.rb_code_position_struct { i32 64, i32 3 }, %struct.rb_code_position_struct { i32 68, i32 10 }, %struct.rb_code_position_struct { i32 79, i32 5 }, %struct.rb_code_position_struct { i32 85, i32 4 }, %struct.rb_code_position_struct { i32 90, i32 4 }, %struct.rb_code_position_struct { i32 102, i32 3 }, %struct.rb_code_position_struct { i32 106, i32 9 }, %struct.rb_code_position_struct { i32 116, i32 6 }, %struct.rb_code_position_struct { i32 123, i32 1 }], align 8 -@sorbet_moduleRubyStringTable = internal unnamed_addr global [5 x i64] zeroinitializer, align 8 -@sorbet_moduleRubyStringDescriptors = internal unnamed_addr constant [5 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 16 }, %struct.rb_code_position_struct { i32 17, i32 37 }, %struct.rb_code_position_struct { i32 79, i32 5 }, %struct.rb_code_position_struct { i32 85, i32 4 }, %struct.rb_code_position_struct { i32 106, i32 9 }], align 8 -@rb_cObject = external local_unnamed_addr constant i64 -@guard_epoch_B = linkonce local_unnamed_addr global i64 0 -@guarded_const_B = linkonce local_unnamed_addr global i64 0 - -; Function Attrs: noreturn -declare void @sorbet_raiseArity(i32, i32, i32) local_unnamed_addr #0 - -declare %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64, i64, i64, i64, %struct.rb_iseq_struct*, i32, i32, %struct.SorbetLineNumberInfo*, i64*, i32, i32) local_unnamed_addr #1 - -declare void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo*, i64*, i32) local_unnamed_addr #1 - -declare i64 @sorbet_getConstant(i8*, i64) local_unnamed_addr #1 - -declare i64 @sorbet_readRealpath() local_unnamed_addr #1 - -declare %struct.rb_control_frame_struct* @sorbet_pushStaticInitFrame(i64) local_unnamed_addr #1 - -declare void @sorbet_popFrame() local_unnamed_addr #1 - -declare void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) local_unnamed_addr #1 - -declare i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache*, i64) local_unnamed_addr #1 - -declare void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_iseq_struct*) local_unnamed_addr #1 - -declare void @sorbet_vm_define_method(i64, i8*, i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)*, i8*, %struct.rb_iseq_struct*, i1 zeroext) local_unnamed_addr #1 - -declare void @sorbet_vm_intern_ids(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #1 - -declare void @sorbet_vm_init_string_table(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #1 - -declare i64 @sorbet_maybeAllocateObjectFastPath(i64, %struct.FunctionInlineCache*) local_unnamed_addr #1 - -declare i64 @rb_define_class(i8*, i64) local_unnamed_addr #1 - -declare i64 @rb_cvar_get(i64, i64) local_unnamed_addr #1 - -declare void @rb_cvar_set(i64, i64, i64) local_unnamed_addr #1 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #2 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #2 - -; Function Attrs: noreturn -declare void @rb_raise(i64, i8*, ...) local_unnamed_addr #0 - -; Function Attrs: allocsize(0,1) -declare noalias nonnull i8* @ruby_xcalloc(i64, i64) local_unnamed_addr #3 - -; Function Attrs: allocsize(0,1) -declare noalias nonnull i8* @ruby_xmalloc2(i64, i64) local_unnamed_addr #3 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) #2 - -; Function Attrs: nounwind ssp uwtable -define weak i32 @sorbet_getIsReleaseBuild() local_unnamed_addr #4 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([93 x i8], [93 x i8]* @.str.10, i64 0, i64 0)) #10 - unreachable -} - -; Function Attrs: nounwind ssp uwtable -define weak i8* @sorbet_getBuildSCMRevision() local_unnamed_addr #4 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([95 x i8], [95 x i8]* @.str.9, i64 0, i64 0)) #10 - unreachable -} - -define internal fastcc void @sorbet_globalConstructors(i64 %realpath) unnamed_addr { -allocRubyIds: - tail call void @sorbet_vm_intern_ids(i64* noundef getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([10 x %struct.rb_code_position_struct], [10 x %struct.rb_code_position_struct]* @sorbet_moduleIDDescriptors, i32 0, i32 0), i32 noundef 10, i8* noundef getelementptr inbounds ([125 x i8], [125 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_vm_init_string_table(i64* noundef getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleRubyStringTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([5 x %struct.rb_code_position_struct], [5 x %struct.rb_code_position_struct]* @sorbet_moduleRubyStringDescriptors, i32 0, i32 0), i32 noundef 5, i8* noundef getelementptr inbounds ([125 x i8], [125 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef getelementptr inbounds ([29 x i64], [29 x i64]* @iseqEncodedArray, i32 0, i32 0), i32 noundef 29) - tail call fastcc void @"Constr_stackFramePrecomputed_func_.13"(i64 %realpath) - %rubyId_new = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 1), align 8, !dbg !10, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_new, i64 %rubyId_new, i32 noundef 16, i32 noundef 0, i32 noundef 0), !dbg !10 - %rubyId_initialize = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 2), align 8, !dbg !10, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_initialize, i64 %rubyId_initialize, i32 noundef 20, i32 noundef 0, i32 noundef 0), !dbg !10 - %rubyId_write = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 3), align 8, !dbg !10, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_write, i64 %rubyId_write, i32 noundef 16, i32 noundef 1, i32 noundef 0), !dbg !10 - %rubyId_read = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 4), align 8, !dbg !15, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_read, i64 %rubyId_read, i32 noundef 16, i32 noundef 0, i32 noundef 0), !dbg !15 - %rubyId_puts = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 5), align 8, !dbg !16, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts, i64 %rubyId_puts, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !16 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_new.1, i64 %rubyId_new, i32 noundef 16, i32 noundef 0, i32 noundef 0), !dbg !17 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_initialize.2, i64 %rubyId_initialize, i32 noundef 20, i32 noundef 0, i32 noundef 0), !dbg !17 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_read.3, i64 %rubyId_read, i32 noundef 16, i32 noundef 0, i32 noundef 0), !dbg !17 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts.4, i64 %rubyId_puts, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !18 - tail call fastcc void @"Constr_stackFramePrecomputed_func_B#5write"(i64 %realpath) - tail call fastcc void @"Constr_stackFramePrecomputed_func_B#4read"(i64 %realpath) - tail call fastcc void @Constr_stackFramePrecomputed_func_B.4read(i64 %realpath) - tail call fastcc void @"Constr_stackFramePrecomputed_func_B.13"(i64 %realpath) - ret void -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_.13"(i64 %realpath) unnamed_addr #5 { -entryInitializers: - %"rubyId_" = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 0), align 8, !invariant.load !5 - %"rubyStr_" = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 0), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/classfields.rb" = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_", i64 %"rubyId_", i64 %"rubyStr_test/testdata/compiler/classfields.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 0, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 2) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - ret void -} - -; Function Attrs: sspreq -define void @Init_classfields() local_unnamed_addr #6 { -entry: - %positional_table.i.i = alloca i64, align 8, !dbg !19 - %realpath = tail call i64 @sorbet_readRealpath() - tail call fastcc void @sorbet_globalConstructors(i64 %realpath) - %0 = load %struct.rb_vm_struct*, %struct.rb_vm_struct** @ruby_current_vm_ptr, align 8, !tbaa !22 - %1 = getelementptr inbounds %struct.rb_vm_struct, %struct.rb_vm_struct* %0, i64 0, i32 18 - %2 = load i64, i64* %1, align 8, !tbaa !24 - %3 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !22 - %4 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %3, i64 0, i32 2 - %5 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %4, align 8, !tbaa !34 - %stackFrame.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - %6 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame.i, %struct.rb_iseq_struct** %6, align 8, !tbaa !37 - %7 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 4 - %8 = load i64*, i64** %7, align 8, !tbaa !39 - %9 = load i64, i64* %8, align 8, !tbaa !6 - %10 = and i64 %9, -33 - store i64 %10, i64* %8, align 8, !tbaa !6 - tail call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %3, %struct.rb_control_frame_struct* %5, %struct.rb_iseq_struct* %stackFrame.i) #11 - %11 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 0 - store i64* getelementptr inbounds ([29 x i64], [29 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %11, align 8, !dbg !40, !tbaa !22 - %12 = load i64, i64* @rb_cObject, align 8, !dbg !41 - %13 = tail call i64 @rb_define_class(i8* getelementptr inbounds ([125 x i8], [125 x i8]* @sorbet_moduleStringTable, i64 0, i64 55), i64 %12) #11, !dbg !41 - %14 = tail call %struct.rb_control_frame_struct* @sorbet_pushStaticInitFrame(i64 %13) #11, !dbg !41 - %15 = bitcast i64* %positional_table.i.i to i8* - call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %15) #11 - %stackFrame.i.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_B.13", align 8 - %16 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !22 - %17 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %16, i64 0, i32 2 - %18 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %17, align 8, !tbaa !34 - %19 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %18, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame.i.i, %struct.rb_iseq_struct** %19, align 8, !tbaa !37 - %20 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %18, i64 0, i32 4 - %21 = load i64*, i64** %20, align 8, !tbaa !39 - %22 = load i64, i64* %21, align 8, !tbaa !6 - %23 = and i64 %22, -33 - store i64 %23, i64* %21, align 8, !tbaa !6 - tail call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %16, %struct.rb_control_frame_struct* %18, %struct.rb_iseq_struct* %stackFrame.i.i) #11 - %24 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %14, i64 0, i32 0 - store i64* getelementptr inbounds ([29 x i64], [29 x i64]* @iseqEncodedArray, i64 0, i64 7), i64** %24, align 8, !dbg !42, !tbaa !22 - %25 = load i64, i64* @guard_epoch_B, align 8, !dbg !19 - %26 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !19, !tbaa !43 - %needTakeSlowPath = icmp ne i64 %25, %26, !dbg !19 - br i1 %needTakeSlowPath, label %27, label %28, !dbg !19, !prof !44 - -27: ; preds = %entry - tail call void @const_recompute_B(), !dbg !19 - br label %28, !dbg !19 - -28: ; preds = %entry, %27 - %29 = load i64, i64* @guarded_const_B, align 8, !dbg !19 - %30 = load i64, i64* @guard_epoch_B, align 8, !dbg !19 - %31 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !19, !tbaa !43 - %guardUpdated = icmp eq i64 %30, %31, !dbg !19 - tail call void @llvm.assume(i1 %guardUpdated), !dbg !19 - %stackFrame25.i.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_B#5write", align 8, !dbg !19 - %32 = tail call noalias nonnull i8* @ruby_xcalloc(i64 noundef 1, i64 noundef 64) #12, !dbg !19 - %33 = bitcast i8* %32 to i16*, !dbg !19 - %34 = load i16, i16* %33, align 8, !dbg !19 - %35 = and i16 %34, -384, !dbg !19 - %36 = or i16 %35, 1, !dbg !19 - store i16 %36, i16* %33, align 8, !dbg !19 - %37 = getelementptr inbounds i8, i8* %32, i64 8, !dbg !19 - %38 = bitcast i8* %37 to i32*, !dbg !19 - store i32 1, i32* %38, align 8, !dbg !19, !tbaa !45 - %39 = getelementptr inbounds i8, i8* %32, i64 12, !dbg !19 - %40 = getelementptr inbounds i8, i8* %32, i64 4, !dbg !19 - %41 = bitcast i8* %40 to i32*, !dbg !19 - tail call void @llvm.memset.p0i8.i64(i8* nonnull align 4 %39, i8 0, i64 20, i1 false) #11, !dbg !19 - store i32 1, i32* %41, align 4, !dbg !19, !tbaa !48 - %rubyId_a.i.i = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 9), align 8, !dbg !19, !invariant.load !5 - store i64 %rubyId_a.i.i, i64* %positional_table.i.i, align 8, !dbg !19 - %42 = tail call noalias nonnull i8* @ruby_xmalloc2(i64 noundef 1, i64 noundef 8) #12, !dbg !19 - call void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture nonnull writeonly align 1 %42, i8* nocapture noundef nonnull readonly align 8 dereferenceable(8) %15, i64 noundef 8, i1 noundef false) #11, !dbg !19 - %43 = getelementptr inbounds i8, i8* %32, i64 32, !dbg !19 - %44 = bitcast i8* %43 to i8**, !dbg !19 - store i8* %42, i8** %44, align 8, !dbg !19, !tbaa !49 - tail call void @sorbet_vm_define_method(i64 %29, i8* getelementptr inbounds ([125 x i8], [125 x i8]* @sorbet_moduleStringTable, i64 0, i64 79), i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)* noundef @"func_B#5write", i8* nonnull %32, %struct.rb_iseq_struct* %stackFrame25.i.i, i1 noundef zeroext false) #11, !dbg !19 - store i64* getelementptr inbounds ([29 x i64], [29 x i64]* @iseqEncodedArray, i64 0, i64 16), i64** %24, align 8, !dbg !19, !tbaa !22 - %stackFrame34.i.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_B#4read", align 8, !dbg !50 - %45 = tail call noalias nonnull i8* @ruby_xcalloc(i64 noundef 1, i64 noundef 64) #12, !dbg !50 - %46 = bitcast i8* %45 to i16*, !dbg !50 - %47 = load i16, i16* %46, align 8, !dbg !50 - %48 = and i16 %47, -384, !dbg !50 - store i16 %48, i16* %46, align 8, !dbg !50 - %49 = getelementptr inbounds i8, i8* %45, i64 4, !dbg !50 - tail call void @llvm.memset.p0i8.i64(i8* nonnull align 4 %49, i8 0, i64 28, i1 false) #11, !dbg !50 - tail call void @sorbet_vm_define_method(i64 %29, i8* getelementptr inbounds ([125 x i8], [125 x i8]* @sorbet_moduleStringTable, i64 0, i64 85), i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)* noundef @"func_B#4read", i8* nonnull %45, %struct.rb_iseq_struct* %stackFrame34.i.i, i1 noundef zeroext false) #11, !dbg !50 - store i64* getelementptr inbounds ([29 x i64], [29 x i64]* @iseqEncodedArray, i64 0, i64 19), i64** %24, align 8, !dbg !50, !tbaa !22 - %stackFrame44.i.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @stackFramePrecomputed_func_B.4read, align 8, !dbg !51 - %50 = tail call noalias nonnull i8* @ruby_xcalloc(i64 noundef 1, i64 noundef 64) #12, !dbg !51 - %51 = bitcast i8* %50 to i16*, !dbg !51 - %52 = load i16, i16* %51, align 8, !dbg !51 - %53 = and i16 %52, -384, !dbg !51 - store i16 %53, i16* %51, align 8, !dbg !51 - %54 = getelementptr inbounds i8, i8* %50, i64 4, !dbg !51 - tail call void @llvm.memset.p0i8.i64(i8* nonnull align 4 %54, i8 0, i64 28, i1 false) #11, !dbg !51 - tail call void @sorbet_vm_define_method(i64 %29, i8* getelementptr inbounds ([125 x i8], [125 x i8]* @sorbet_moduleStringTable, i64 0, i64 85), i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)* noundef @func_B.4read, i8* nonnull %50, %struct.rb_iseq_struct* %stackFrame44.i.i, i1 noundef zeroext true) #11, !dbg !51 - call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %15) #11 - tail call void @sorbet_popFrame() #11, !dbg !41 - store i64* getelementptr inbounds ([29 x i64], [29 x i64]* @iseqEncodedArray, i64 0, i64 25), i64** %11, align 8, !dbg !41, !tbaa !22 - %55 = tail call i64 @sorbet_maybeAllocateObjectFastPath(i64 %29, %struct.FunctionInlineCache* noundef @ic_new) #11, !dbg !10 - %56 = icmp eq i64 %55, 52, !dbg !10 - br i1 %56, label %slowNew.i, label %fastNew.i, !dbg !10 - -slowNew.i: ; preds = %28 - %57 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !10 - %58 = load i64*, i64** %57, align 8, !dbg !10 - store i64 %29, i64* %58, align 8, !dbg !10, !tbaa !6 - %59 = getelementptr inbounds i64, i64* %58, i64 1, !dbg !10 - store i64* %59, i64** %57, align 8, !dbg !10 - %60 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* noundef @ic_new, i64 noundef 0) #11, !dbg !10 - br label %afterNew.i, !dbg !10 - -fastNew.i: ; preds = %28 - %61 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !10 - %62 = load i64*, i64** %61, align 8, !dbg !10 - store i64 %55, i64* %62, align 8, !dbg !10, !tbaa !6 - %63 = getelementptr inbounds i64, i64* %62, i64 1, !dbg !10 - store i64* %63, i64** %61, align 8, !dbg !10 - %64 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* noundef @ic_initialize, i64 noundef 0) #11, !dbg !10 - br label %afterNew.i, !dbg !10 - -afterNew.i: ; preds = %fastNew.i, %slowNew.i - %initializedObject.i = phi i64 [ %60, %slowNew.i ], [ %55, %fastNew.i ], !dbg !10 - %65 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !10 - %66 = load i64*, i64** %65, align 8, !dbg !10 - store i64 %initializedObject.i, i64* %66, align 8, !dbg !10, !tbaa !6 - %67 = getelementptr inbounds i64, i64* %66, i64 1, !dbg !10 - store i64 3, i64* %67, align 8, !dbg !10, !tbaa !6 - %68 = getelementptr inbounds i64, i64* %67, i64 1, !dbg !10 - store i64* %68, i64** %65, align 8, !dbg !10 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_write, i64 0), !dbg !10 - store i64* getelementptr inbounds ([29 x i64], [29 x i64]* @iseqEncodedArray, i64 0, i64 27), i64** %11, align 8, !dbg !10, !tbaa !22 - %69 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !15 - %70 = load i64*, i64** %69, align 8, !dbg !15 - store i64 %29, i64* %70, align 8, !dbg !15, !tbaa !6 - %71 = getelementptr inbounds i64, i64* %70, i64 1, !dbg !15 - store i64* %71, i64** %69, align 8, !dbg !15 - %send5 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_read, i64 0), !dbg !15 - %72 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !16 - %73 = load i64*, i64** %72, align 8, !dbg !16 - store i64 %2, i64* %73, align 8, !dbg !16, !tbaa !6 - %74 = getelementptr inbounds i64, i64* %73, i64 1, !dbg !16 - store i64 %send5, i64* %74, align 8, !dbg !16, !tbaa !6 - %75 = getelementptr inbounds i64, i64* %74, i64 1, !dbg !16 - store i64* %75, i64** %72, align 8, !dbg !16 - %send7 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts, i64 0), !dbg !16 - store i64* getelementptr inbounds ([29 x i64], [29 x i64]* @iseqEncodedArray, i64 0, i64 28), i64** %11, align 8, !dbg !16, !tbaa !22 - %76 = tail call i64 @sorbet_maybeAllocateObjectFastPath(i64 %29, %struct.FunctionInlineCache* noundef @ic_new.1) #11, !dbg !17 - %77 = icmp eq i64 %76, 52, !dbg !17 - br i1 %77, label %slowNew50.i, label %fastNew51.i, !dbg !17 - -slowNew50.i: ; preds = %afterNew.i - %78 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !17 - %79 = load i64*, i64** %78, align 8, !dbg !17 - store i64 %29, i64* %79, align 8, !dbg !17, !tbaa !6 - %80 = getelementptr inbounds i64, i64* %79, i64 1, !dbg !17 - store i64* %80, i64** %78, align 8, !dbg !17 - %81 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* noundef @ic_new.1, i64 noundef 0) #11, !dbg !17 - br label %"func_.13.exit", !dbg !17 - -fastNew51.i: ; preds = %afterNew.i - %82 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !17 - %83 = load i64*, i64** %82, align 8, !dbg !17 - store i64 %76, i64* %83, align 8, !dbg !17, !tbaa !6 - %84 = getelementptr inbounds i64, i64* %83, i64 1, !dbg !17 - store i64* %84, i64** %82, align 8, !dbg !17 - %85 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* noundef @ic_initialize.2, i64 noundef 0) #11, !dbg !17 - br label %"func_.13.exit", !dbg !17 - -"func_.13.exit": ; preds = %slowNew50.i, %fastNew51.i - %initializedObject56.i = phi i64 [ %81, %slowNew50.i ], [ %76, %fastNew51.i ], !dbg !17 - %86 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !17 - %87 = load i64*, i64** %86, align 8, !dbg !17 - store i64 %initializedObject56.i, i64* %87, align 8, !dbg !17, !tbaa !6 - %88 = getelementptr inbounds i64, i64* %87, i64 1, !dbg !17 - store i64* %88, i64** %86, align 8, !dbg !17 - %send9 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_read.3, i64 0), !dbg !17 - %89 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !18 - %90 = load i64*, i64** %89, align 8, !dbg !18 - store i64 %2, i64* %90, align 8, !dbg !18, !tbaa !6 - %91 = getelementptr inbounds i64, i64* %90, i64 1, !dbg !18 - store i64 %send9, i64* %91, align 8, !dbg !18, !tbaa !6 - %92 = getelementptr inbounds i64, i64* %91, i64 1, !dbg !18 - store i64* %92, i64** %89, align 8, !dbg !18 - %send11 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts.4, i64 0), !dbg !18 - ret void -} - -; Function Attrs: nounwind sspreq uwtable -define internal i64 @"func_B#5write"(i32 %argc, i64* nocapture readonly %argArray, i64 %selfRaw, %struct.rb_control_frame_struct* nocapture nonnull writeonly align 8 dereferenceable(8) %cfp, i8* nocapture nofree readnone %calling, i8* nocapture nofree readnone %callData) #7 !dbg !52 { -functionEntryInitializers: - %0 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 0 - store i64* getelementptr inbounds ([29 x i64], [29 x i64]* @iseqEncodedArray, i64 0, i64 7), i64** %0, align 8, !tbaa !22 - %tooManyArgs = icmp ugt i32 %argc, 1, !dbg !53 - %tooFewArgs = icmp ult i32 %argc, 1, !dbg !53 - %or.cond = or i1 %tooManyArgs, %tooFewArgs, !dbg !53 - br i1 %or.cond, label %argCountFailBlock, label %fillRequiredArgs, !dbg !53, !prof !54 - -argCountFailBlock: ; preds = %functionEntryInitializers - tail call void @sorbet_raiseArity(i32 %argc, i32 noundef 1, i32 noundef 1) #13, !dbg !53 - unreachable, !dbg !53 - -fillRequiredArgs: ; preds = %functionEntryInitializers - %rawArg_a = load i64, i64* %argArray, align 8, !dbg !53 - store i64* getelementptr inbounds ([29 x i64], [29 x i64]* @iseqEncodedArray, i64 0, i64 8), i64** %0, align 8, !dbg !55, !tbaa !22 - %1 = load i64, i64* @guard_epoch_B, align 8, !dbg !56 - %2 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !56, !tbaa !43 - %needTakeSlowPath = icmp ne i64 %1, %2, !dbg !56 - br i1 %needTakeSlowPath, label %3, label %4, !dbg !56, !prof !44 - -3: ; preds = %fillRequiredArgs - tail call void @const_recompute_B(), !dbg !56 - br label %4, !dbg !56 - -4: ; preds = %fillRequiredArgs, %3 - %5 = load i64, i64* @guarded_const_B, align 8, !dbg !56 - %6 = load i64, i64* @guard_epoch_B, align 8, !dbg !56 - %7 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !56, !tbaa !43 - %guardUpdated = icmp eq i64 %6, %7, !dbg !56 - tail call void @llvm.assume(i1 %guardUpdated), !dbg !56 - %"rubyId_@@f" = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 6), align 8, !dbg !56, !invariant.load !5 - tail call void @rb_cvar_set(i64 %5, i64 %"rubyId_@@f", i64 %rawArg_a) #11, !dbg !56 - %8 = tail call i64 @rb_cvar_get(i64 %5, i64 %"rubyId_@@f") #11, !dbg !57 - ret i64 %8 -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_B#5write"(i64 %realpath) unnamed_addr #5 { -entryInitializers: - %rubyId_write = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 3), align 8, !invariant.load !5 - %rubyStr_write = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 2), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/classfields.rb" = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %rubyStr_write, i64 %rubyId_write, i64 %"rubyStr_test/testdata/compiler/classfields.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 1, i32 noundef 7, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 0) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_B#5write", align 8 - ret void -} - -; Function Attrs: nounwind sspreq uwtable -define internal i64 @"func_B#4read"(i32 %argc, i64* nocapture nofree readnone %argArray, i64 %selfRaw, %struct.rb_control_frame_struct* nocapture nonnull writeonly align 8 dereferenceable(8) %cfp, i8* nocapture nofree readnone %calling, i8* nocapture nofree readnone %callData) #7 !dbg !58 { -functionEntryInitializers: - %0 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 0 - store i64* getelementptr inbounds ([29 x i64], [29 x i64]* @iseqEncodedArray, i64 0, i64 16), i64** %0, align 8, !tbaa !22 - %tooManyArgs = icmp ugt i32 %argc, 0, !dbg !59 - br i1 %tooManyArgs, label %argCountFailBlock, label %fillRequiredArgs, !dbg !59, !prof !60 - -argCountFailBlock: ; preds = %functionEntryInitializers - tail call void @sorbet_raiseArity(i32 %argc, i32 noundef 0, i32 noundef 0) #13, !dbg !59 - unreachable, !dbg !59 - -fillRequiredArgs: ; preds = %functionEntryInitializers - store i64* getelementptr inbounds ([29 x i64], [29 x i64]* @iseqEncodedArray, i64 0, i64 17), i64** %0, align 8, !dbg !61, !tbaa !22 - %1 = load i64, i64* @guard_epoch_B, align 8, !dbg !62 - %2 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !62, !tbaa !43 - %needTakeSlowPath = icmp ne i64 %1, %2, !dbg !62 - br i1 %needTakeSlowPath, label %3, label %4, !dbg !62, !prof !44 - -3: ; preds = %fillRequiredArgs - tail call void @const_recompute_B(), !dbg !62 - br label %4, !dbg !62 - -4: ; preds = %fillRequiredArgs, %3 - %5 = load i64, i64* @guarded_const_B, align 8, !dbg !62 - %6 = load i64, i64* @guard_epoch_B, align 8, !dbg !62 - %7 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !62, !tbaa !43 - %guardUpdated = icmp eq i64 %6, %7, !dbg !62 - tail call void @llvm.assume(i1 %guardUpdated), !dbg !62 - %"rubyId_@@f" = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 6), align 8, !dbg !62, !invariant.load !5 - %8 = tail call i64 @rb_cvar_get(i64 %5, i64 %"rubyId_@@f") #11, !dbg !62 - ret i64 %8 -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_B#4read"(i64 %realpath) unnamed_addr #5 { -entryInitializers: - %rubyId_read = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 4), align 8, !invariant.load !5 - %rubyStr_read = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 3), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/classfields.rb" = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %rubyStr_read, i64 %rubyId_read, i64 %"rubyStr_test/testdata/compiler/classfields.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 1, i32 noundef 16, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 0) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_B#4read", align 8 - ret void -} - -; Function Attrs: nounwind sspreq uwtable -define internal i64 @func_B.4read(i32 %argc, i64* nocapture nofree readnone %argArray, i64 %selfRaw, %struct.rb_control_frame_struct* nocapture nonnull writeonly align 8 dereferenceable(8) %cfp, i8* nocapture nofree readnone %calling, i8* nocapture nofree readnone %callData) #7 !dbg !63 { -functionEntryInitializers: - %0 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 0 - store i64* getelementptr inbounds ([29 x i64], [29 x i64]* @iseqEncodedArray, i64 0, i64 19), i64** %0, align 8, !tbaa !22 - %tooManyArgs = icmp ugt i32 %argc, 0, !dbg !64 - br i1 %tooManyArgs, label %argCountFailBlock, label %fillRequiredArgs, !dbg !64, !prof !60 - -argCountFailBlock: ; preds = %functionEntryInitializers - tail call void @sorbet_raiseArity(i32 %argc, i32 noundef 0, i32 noundef 0) #13, !dbg !64 - unreachable, !dbg !64 - -fillRequiredArgs: ; preds = %functionEntryInitializers - store i64* getelementptr inbounds ([29 x i64], [29 x i64]* @iseqEncodedArray, i64 0, i64 20), i64** %0, align 8, !dbg !65, !tbaa !22 - %1 = load i64, i64* @guard_epoch_B, align 8, !dbg !66 - %2 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !66, !tbaa !43 - %needTakeSlowPath = icmp ne i64 %1, %2, !dbg !66 - br i1 %needTakeSlowPath, label %3, label %4, !dbg !66, !prof !44 - -3: ; preds = %fillRequiredArgs - tail call void @const_recompute_B(), !dbg !66 - br label %4, !dbg !66 - -4: ; preds = %fillRequiredArgs, %3 - %5 = load i64, i64* @guarded_const_B, align 8, !dbg !66 - %6 = load i64, i64* @guard_epoch_B, align 8, !dbg !66 - %7 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !66, !tbaa !43 - %guardUpdated = icmp eq i64 %6, %7, !dbg !66 - tail call void @llvm.assume(i1 %guardUpdated), !dbg !66 - %"rubyId_@@f" = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 6), align 8, !dbg !66, !invariant.load !5 - %8 = tail call i64 @rb_cvar_get(i64 %5, i64 %"rubyId_@@f") #11, !dbg !66 - ret i64 %8 -} - -; Function Attrs: ssp -define internal fastcc void @Constr_stackFramePrecomputed_func_B.4read(i64 %realpath) unnamed_addr #5 { -entryInitializers: - %rubyId_read = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 4), align 8, !invariant.load !5 - %rubyStr_read = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 3), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/classfields.rb" = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %rubyStr_read, i64 %rubyId_read, i64 %"rubyStr_test/testdata/compiler/classfields.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 1, i32 noundef 19, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 0) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @stackFramePrecomputed_func_B.4read, align 8 - ret void -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_B.13"(i64 %realpath) unnamed_addr #5 { -entryInitializers: - %"rubyId_" = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 7), align 8, !invariant.load !5 - %"rubyStr_" = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 4), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/classfields.rb" = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_", i64 %"rubyId_", i64 %"rubyStr_test/testdata/compiler/classfields.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 3, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 4) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_B.13", align 8 - ret void -} - -; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly -declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #8 - -; Function Attrs: nofree nosync nounwind willreturn -declare void @llvm.assume(i1 noundef) #9 - -; Function Attrs: ssp -define linkonce void @const_recompute_B() local_unnamed_addr #5 { - %1 = tail call i64 @sorbet_getConstant(i8* getelementptr inbounds ([125 x i8], [125 x i8]* @sorbet_moduleStringTable, i64 0, i64 55), i64 1) - store i64 %1, i64* @guarded_const_B, align 8 - %2 = load i64, i64* @ruby_vm_global_constant_state, align 8, !tbaa !43 - store i64 %2, i64* @guard_epoch_B, align 8 - ret void -} - -attributes #0 = { noreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #1 = { "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #2 = { argmemonly nofree nosync nounwind willreturn } -attributes #3 = { allocsize(0,1) "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #4 = { nounwind ssp uwtable "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #5 = { ssp } -attributes #6 = { sspreq } -attributes #7 = { nounwind sspreq uwtable } -attributes #8 = { argmemonly nofree nosync nounwind willreturn writeonly } -attributes #9 = { nofree nosync nounwind willreturn } -attributes #10 = { noreturn nounwind } -attributes #11 = { nounwind } -attributes #12 = { nounwind allocsize(0,1) } -attributes #13 = { noreturn } - -!llvm.module.flags = !{!0, !1, !2} -!llvm.dbg.cu = !{!3} - -!0 = !{i32 2, !"Debug Info Version", i32 3} -!1 = !{i32 4, !"cf-protection-return", i32 1} -!2 = !{i32 4, !"cf-protection-branch", i32 1} -!3 = distinct !DICompileUnit(language: DW_LANG_C, file: !4, producer: "Sorbet LLVM", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !5) -!4 = !DIFile(filename: "test/testdata/compiler/classfields.rb", directory: ".") -!5 = !{} -!6 = !{!7, !7, i64 0} -!7 = !{!"long", !8, i64 0} -!8 = !{!"omnipotent char", !9, i64 0} -!9 = !{!"Simple C/C++ TBAA"} -!10 = !DILocation(line: 25, column: 1, scope: !11) -!11 = distinct !DISubprogram(name: ".", linkageName: "func_.13", scope: null, file: !4, line: 5, type: !12, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!12 = !DISubroutineType(types: !13) -!13 = !{!14} -!14 = !DIBasicType(name: "VALUE", size: 64, encoding: DW_ATE_signed) -!15 = !DILocation(line: 27, column: 6, scope: !11) -!16 = !DILocation(line: 27, column: 1, scope: !11) -!17 = !DILocation(line: 28, column: 6, scope: !11) -!18 = !DILocation(line: 28, column: 1, scope: !11) -!19 = !DILocation(line: 7, column: 3, scope: !20, inlinedAt: !21) -!20 = distinct !DISubprogram(name: "B.", linkageName: "func_B.13L62", scope: null, file: !4, line: 5, type: !12, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!21 = distinct !DILocation(line: 5, column: 1, scope: !11) -!22 = !{!23, !23, i64 0} -!23 = !{!"any pointer", !8, i64 0} -!24 = !{!25, !7, i64 400} -!25 = !{!"rb_vm_struct", !7, i64 0, !26, i64 8, !23, i64 192, !23, i64 200, !23, i64 208, !30, i64 216, !8, i64 224, !27, i64 264, !27, i64 280, !27, i64 296, !27, i64 312, !7, i64 328, !29, i64 336, !29, i64 340, !29, i64 344, !29, i64 344, !29, i64 344, !29, i64 344, !29, i64 348, !7, i64 352, !8, i64 360, !7, i64 400, !7, i64 408, !7, i64 416, !7, i64 424, !7, i64 432, !7, i64 440, !7, i64 448, !23, i64 456, !23, i64 464, !31, i64 472, !32, i64 992, !23, i64 1016, !23, i64 1024, !29, i64 1032, !29, i64 1036, !27, i64 1040, !8, i64 1056, !7, i64 1096, !7, i64 1104, !7, i64 1112, !7, i64 1120, !7, i64 1128, !29, i64 1136, !23, i64 1144, !23, i64 1152, !23, i64 1160, !23, i64 1168, !23, i64 1176, !23, i64 1184, !29, i64 1192, !33, i64 1200, !8, i64 1232} -!26 = !{!"rb_global_vm_lock_struct", !23, i64 0, !8, i64 8, !27, i64 48, !23, i64 64, !29, i64 72, !8, i64 80, !8, i64 128, !29, i64 176, !29, i64 180} -!27 = !{!"list_head", !28, i64 0} -!28 = !{!"list_node", !23, i64 0, !23, i64 8} -!29 = !{!"int", !8, i64 0} -!30 = !{!"long long", !8, i64 0} -!31 = !{!"", !8, i64 0} -!32 = !{!"rb_hook_list_struct", !23, i64 0, !29, i64 8, !29, i64 12, !29, i64 16} -!33 = !{!"", !7, i64 0, !7, i64 8, !7, i64 16, !7, i64 24} -!34 = !{!35, !23, i64 16} -!35 = !{!"rb_execution_context_struct", !23, i64 0, !7, i64 8, !23, i64 16, !23, i64 24, !23, i64 32, !29, i64 40, !29, i64 44, !23, i64 48, !23, i64 56, !23, i64 64, !7, i64 72, !7, i64 80, !23, i64 88, !7, i64 96, !23, i64 104, !23, i64 112, !7, i64 120, !7, i64 128, !8, i64 136, !8, i64 137, !7, i64 144, !36, i64 152} -!36 = !{!"", !23, i64 0, !23, i64 8, !7, i64 16, !8, i64 24} -!37 = !{!38, !23, i64 16} -!38 = !{!"rb_control_frame_struct", !23, i64 0, !23, i64 8, !23, i64 16, !7, i64 24, !23, i64 32, !23, i64 40, !23, i64 48} -!39 = !{!38, !23, i64 32} -!40 = !DILocation(line: 0, scope: !11) -!41 = !DILocation(line: 5, column: 1, scope: !11) -!42 = !DILocation(line: 0, scope: !20, inlinedAt: !21) -!43 = !{!30, !30, i64 0} -!44 = !{!"branch_weights", i32 1, i32 10000} -!45 = !{!46, !29, i64 8} -!46 = !{!"rb_sorbet_param_struct", !47, i64 0, !29, i64 4, !29, i64 8, !29, i64 12, !29, i64 16, !29, i64 20, !29, i64 24, !29, i64 28, !23, i64 32, !29, i64 40, !29, i64 44, !29, i64 48, !29, i64 52, !23, i64 56} -!47 = !{!"", !29, i64 0, !29, i64 0, !29, i64 0, !29, i64 0, !29, i64 0, !29, i64 0, !29, i64 0, !29, i64 0, !29, i64 1, !29, i64 1} -!48 = !{!46, !29, i64 4} -!49 = !{!46, !23, i64 32} -!50 = !DILocation(line: 16, column: 3, scope: !20, inlinedAt: !21) -!51 = !DILocation(line: 19, column: 3, scope: !20, inlinedAt: !21) -!52 = distinct !DISubprogram(name: "B#write", linkageName: "func_B#5write", scope: null, file: !4, line: 7, type: !12, scopeLine: 7, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!53 = !DILocation(line: 7, column: 3, scope: !52) -!54 = !{!"branch_weights", i32 4001, i32 4000000} -!55 = !DILocation(line: 7, column: 13, scope: !52) -!56 = !DILocation(line: 8, column: 11, scope: !52) -!57 = !DILocation(line: 8, column: 5, scope: !52) -!58 = distinct !DISubprogram(name: "B#read", linkageName: "func_B#4read", scope: null, file: !4, line: 16, type: !12, scopeLine: 16, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!59 = !DILocation(line: 16, column: 3, scope: !58) -!60 = !{!"branch_weights", i32 1, i32 2000} -!61 = !DILocation(line: 0, scope: !58) -!62 = !DILocation(line: 17, column: 5, scope: !58) -!63 = distinct !DISubprogram(name: "B.read", linkageName: "func_B.4read", scope: null, file: !4, line: 19, type: !12, scopeLine: 19, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!64 = !DILocation(line: 19, column: 3, scope: !63) -!65 = !DILocation(line: 0, scope: !63) -!66 = !DILocation(line: 20, column: 5, scope: !63) diff --git a/test/testdata/compiler/classfields.rb b/test/testdata/compiler/classfields.rb index 17f5d222f5..67390e3ac8 100644 --- a/test/testdata/compiler/classfields.rb +++ b/test/testdata/compiler/classfields.rb @@ -21,7 +21,7 @@ def self.read end end -# TODO: we'll need more tests when we implement subclasses and fix self in static scope +# TODO, we'll need more tests when we implement subclasses and fix self in static scope B.new.write(1) puts B.read diff --git a/test/testdata/compiler/constant_cache.opt.ll.exp b/test/testdata/compiler/constant_cache.opt.ll.exp deleted file mode 100644 index 1a9c320708..0000000000 --- a/test/testdata/compiler/constant_cache.opt.ll.exp +++ /dev/null @@ -1,414 +0,0 @@ -; ModuleID = 'payload' -source_filename = "llvm-link" -target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } -%struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.__pthread_cond_s } -%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } -%union.anon = type { i64 } -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } -%struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } -%struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } -%struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } -%struct.anon.0 = type { i16, [2 x i8] } -%struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } -%struct.rb_iseq_location_struct = type { i64, i64, i64, i64, i32, %struct.rb_code_location_struct } -%struct.rb_code_location_struct = type { %struct.rb_code_position_struct, %struct.rb_code_position_struct } -%struct.rb_code_position_struct = type { i32, i32 } -%struct.iseq_insn_info = type { %struct.iseq_insn_info_entry*, i32*, i32, %struct.succ_index_table* } -%struct.iseq_insn_info_entry = type opaque -%struct.succ_index_table = type opaque -%struct.iseq_catch_table = type opaque -%union.iseq_inline_storage_entry = type { %struct.iseq_inline_cache_entry } -%struct.iseq_inline_cache_entry = type { i64, %struct.rb_cref_struct*, i64 } -%struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } -%struct.rb_scope_visi_struct = type { i8, [3 x i8] } -%struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } -%struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } -%union.anon.13 = type { %struct.rb_method_cfunc_struct } -%struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%union.anon.15 = type { i32 } -%struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.16 = type { i64, i64, i64, i64* } -%union.anon.17 = type { %struct.anon.18 } -%struct.anon.18 = type { i64, i32 } -%struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } -%struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } -%struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } -%struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } -%struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } -%struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } -%struct.__sigset_t = type { [16 x i64] } -%struct.rb_calling_info = type { i64, i64, i32, i32 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } -%union.anon.9 = type { %union.pthread_cond_t } -%struct.rb_unblock_callback = type { void (i8*)*, i8* } -%struct.rb_mutex_struct = type opaque -%struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.10 = type { %struct.anon.11 } -%struct.anon.11 = type { i64, i64, i32 } -%struct.rb_fiber_struct = type opaque -%struct.anon.5 = type { [65 x i64] } -%struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } -%struct.rb_event_hook_struct = type opaque -%struct.rb_postponed_job_struct = type opaque -%struct.list_head = type { %struct.list_node } -%union.pthread_mutex_t = type { %struct.__pthread_mutex_s } -%struct.__pthread_mutex_s = type { i32, i32, i32, i32, i32, i16, i16, %struct.__pthread_internal_list } -%struct.__pthread_internal_list = type { %struct.__pthread_internal_list*, %struct.__pthread_internal_list* } -%struct.rb_objspace = type opaque -%struct.rb_at_exit_list = type { void (%struct.rb_vm_struct*)*, %struct.rb_at_exit_list* } -%struct.st_table = type { i8, i8, i8, i32, %struct.st_hash_type*, i64, i64*, i64, i64, %struct.st_table_entry* } -%struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } -%struct.st_table_entry = type opaque -%struct.rb_builtin_function = type opaque -%struct.anon.6 = type { i64, i64, i64, i64 } -%struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } -%struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } -%struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } -%struct.rb_call_info_with_kwarg = type { %struct.rb_call_info, %struct.rb_call_info_kw_arg* } -%struct.rb_call_info_kw_arg = type { i32, [1 x i64] } - -@ruby_current_vm_ptr = external local_unnamed_addr global %struct.rb_vm_struct*, align 8 -@ruby_current_execution_context_ptr = external local_unnamed_addr global %struct.rb_execution_context_struct*, align 8 -@ruby_vm_global_constant_state = external local_unnamed_addr global i64, align 8 -@rb_eRuntimeError = external local_unnamed_addr global i64, align 8 -@.str.9 = private unnamed_addr constant [95 x i8] c"sorbet_getBuildSCMRevision: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@.str.10 = private unnamed_addr constant [93 x i8] c"sorbet_getIsReleaseBuild: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@"stackFramePrecomputed_func_Object#3foo" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@iseqEncodedArray = internal global [13 x i64] zeroinitializer -@fileLineNumberInfo = internal global %struct.SorbetLineNumberInfo zeroinitializer -@ic_puts = internal global %struct.FunctionInlineCache zeroinitializer -@ic_puts.1 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_puts.2 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_puts.3 = internal global %struct.FunctionInlineCache zeroinitializer -@"stackFramePrecomputed_func_.13" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@ic_foo = internal global %struct.FunctionInlineCache zeroinitializer -@"stackFramePrecomputed_func_A.13" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@sorbet_moduleStringTable = internal unnamed_addr constant [100 x i8] c"foo\00test/testdata/compiler/constant_cache.rb\00A\00puts\00\00Object\00normal\00master\00\00", align 1 -@sorbet_moduleIDTable = internal unnamed_addr global [5 x i64] zeroinitializer, align 8 -@sorbet_moduleIDDescriptors = internal unnamed_addr constant [5 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 3 }, %struct.rb_code_position_struct { i32 47, i32 4 }, %struct.rb_code_position_struct { i32 52, i32 16 }, %struct.rb_code_position_struct { i32 76, i32 6 }, %struct.rb_code_position_struct { i32 90, i32 9 }], align 8 -@sorbet_moduleRubyStringTable = internal unnamed_addr global [4 x i64] zeroinitializer, align 8 -@sorbet_moduleRubyStringDescriptors = internal unnamed_addr constant [4 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 3 }, %struct.rb_code_position_struct { i32 4, i32 40 }, %struct.rb_code_position_struct { i32 52, i32 16 }, %struct.rb_code_position_struct { i32 90, i32 9 }], align 8 -@guard_epoch_A = linkonce local_unnamed_addr global i64 0 -@guarded_const_A = linkonce local_unnamed_addr global i64 0 -@rb_cObject = external local_unnamed_addr constant i64 - -; Function Attrs: noreturn -declare void @sorbet_raiseArity(i32, i32, i32) local_unnamed_addr #0 - -declare %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64, i64, i64, i64, %struct.rb_iseq_struct*, i32, i32, %struct.SorbetLineNumberInfo*, i64*, i32, i32) local_unnamed_addr #1 - -declare void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo*, i64*, i32) local_unnamed_addr #1 - -declare i64 @sorbet_getConstant(i8*, i64) local_unnamed_addr #1 - -declare i64 @sorbet_readRealpath() local_unnamed_addr #1 - -declare %struct.rb_control_frame_struct* @sorbet_pushStaticInitFrame(i64) local_unnamed_addr #1 - -declare void @sorbet_popFrame() local_unnamed_addr #1 - -declare void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) local_unnamed_addr #1 - -declare i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache*, i64) local_unnamed_addr #1 - -declare void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_iseq_struct*) local_unnamed_addr #1 - -declare void @sorbet_vm_define_method(i64, i8*, i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)*, i8*, %struct.rb_iseq_struct*, i1 zeroext) local_unnamed_addr #1 - -declare void @sorbet_vm_intern_ids(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #1 - -declare void @sorbet_vm_init_string_table(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #1 - -declare i64 @rb_define_class(i8*, i64) local_unnamed_addr #1 - -; Function Attrs: noreturn -declare void @rb_raise(i64, i8*, ...) local_unnamed_addr #0 - -; Function Attrs: allocsize(0,1) -declare noalias nonnull i8* @ruby_xcalloc(i64, i64) local_unnamed_addr #2 - -; Function Attrs: nounwind ssp uwtable -define weak i32 @sorbet_getIsReleaseBuild() local_unnamed_addr #3 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([93 x i8], [93 x i8]* @.str.10, i64 0, i64 0)) #9 - unreachable -} - -; Function Attrs: nounwind ssp uwtable -define weak i8* @sorbet_getBuildSCMRevision() local_unnamed_addr #3 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([95 x i8], [95 x i8]* @.str.9, i64 0, i64 0)) #9 - unreachable -} - -define internal fastcc void @sorbet_globalConstructors(i64 %realpath) unnamed_addr { -allocRubyIds: - tail call void @sorbet_vm_intern_ids(i64* noundef getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleIDTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([5 x %struct.rb_code_position_struct], [5 x %struct.rb_code_position_struct]* @sorbet_moduleIDDescriptors, i32 0, i32 0), i32 noundef 5, i8* noundef getelementptr inbounds ([100 x i8], [100 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_vm_init_string_table(i64* noundef getelementptr inbounds ([4 x i64], [4 x i64]* @sorbet_moduleRubyStringTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([4 x %struct.rb_code_position_struct], [4 x %struct.rb_code_position_struct]* @sorbet_moduleRubyStringDescriptors, i32 0, i32 0), i32 noundef 4, i8* noundef getelementptr inbounds ([100 x i8], [100 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef getelementptr inbounds ([13 x i64], [13 x i64]* @iseqEncodedArray, i32 0, i32 0), i32 noundef 13) - tail call fastcc void @"Constr_stackFramePrecomputed_func_Object#3foo"(i64 %realpath) - %rubyId_puts = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleIDTable, i64 0, i64 1), align 8, !dbg !10, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts, i64 %rubyId_puts, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !10 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts.1, i64 %rubyId_puts, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !15 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts.2, i64 %rubyId_puts, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !16 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts.3, i64 %rubyId_puts, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !17 - tail call fastcc void @"Constr_stackFramePrecomputed_func_.13"(i64 %realpath) - %rubyId_foo = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleIDTable, i64 0, i64 0), align 8, !dbg !18, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_foo, i64 %rubyId_foo, i32 noundef 20, i32 noundef 0, i32 noundef 0), !dbg !18 - tail call fastcc void @"Constr_stackFramePrecomputed_func_A.13"(i64 %realpath) - ret void -} - -; Function Attrs: nounwind sspreq uwtable -define internal i64 @"func_Object#3foo"(i32 %argc, i64* nocapture nofree readnone %argArray, i64 %selfRaw, %struct.rb_control_frame_struct* nonnull align 8 dereferenceable(8) %cfp, i8* nocapture nofree readnone %calling, i8* nocapture nofree readnone %callData) #4 !dbg !11 { -functionEntryInitializers: - %0 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 0 - store i64* getelementptr inbounds ([13 x i64], [13 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %0, align 8, !tbaa !20 - %tooManyArgs = icmp ugt i32 %argc, 0, !dbg !22 - br i1 %tooManyArgs, label %argCountFailBlock, label %fillRequiredArgs, !dbg !22, !prof !23 - -argCountFailBlock: ; preds = %functionEntryInitializers - tail call void @sorbet_raiseArity(i32 %argc, i32 noundef 0, i32 noundef 0) #10, !dbg !22 - unreachable, !dbg !22 - -fillRequiredArgs: ; preds = %functionEntryInitializers - store i64* getelementptr inbounds ([13 x i64], [13 x i64]* @iseqEncodedArray, i64 0, i64 6), i64** %0, align 8, !dbg !24, !tbaa !20 - %1 = load i64, i64* @guard_epoch_A, align 8, !dbg !10 - %2 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !10, !tbaa !25 - %needTakeSlowPath = icmp ne i64 %1, %2, !dbg !10 - br i1 %needTakeSlowPath, label %3, label %4, !dbg !10, !prof !27 - -3: ; preds = %fillRequiredArgs - tail call void @const_recompute_A(), !dbg !10 - br label %4, !dbg !10 - -4: ; preds = %fillRequiredArgs, %3 - %5 = load i64, i64* @guarded_const_A, align 8, !dbg !10 - %6 = load i64, i64* @guard_epoch_A, align 8, !dbg !10 - %7 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !10, !tbaa !25 - %guardUpdated = icmp eq i64 %6, %7, !dbg !10 - tail call void @llvm.assume(i1 %guardUpdated), !dbg !10 - %8 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 1, !dbg !10 - %9 = load i64*, i64** %8, align 8, !dbg !10 - store i64 %selfRaw, i64* %9, align 8, !dbg !10, !tbaa !6 - %10 = getelementptr inbounds i64, i64* %9, i64 1, !dbg !10 - store i64 %5, i64* %10, align 8, !dbg !10, !tbaa !6 - %11 = getelementptr inbounds i64, i64* %10, i64 1, !dbg !10 - store i64* %11, i64** %8, align 8, !dbg !10 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts, i64 0), !dbg !10 - store i64* getelementptr inbounds ([13 x i64], [13 x i64]* @iseqEncodedArray, i64 0, i64 7), i64** %0, align 8, !dbg !10, !tbaa !20 - %12 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 1, !dbg !15 - %13 = load i64*, i64** %12, align 8, !dbg !15 - store i64 %selfRaw, i64* %13, align 8, !dbg !15, !tbaa !6 - %14 = getelementptr inbounds i64, i64* %13, i64 1, !dbg !15 - store i64 %5, i64* %14, align 8, !dbg !15, !tbaa !6 - %15 = getelementptr inbounds i64, i64* %14, i64 1, !dbg !15 - store i64* %15, i64** %12, align 8, !dbg !15 - %send41 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts.1, i64 0), !dbg !15 - store i64* getelementptr inbounds ([13 x i64], [13 x i64]* @iseqEncodedArray, i64 0, i64 8), i64** %0, align 8, !dbg !15, !tbaa !20 - %16 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 1, !dbg !16 - %17 = load i64*, i64** %16, align 8, !dbg !16 - store i64 %selfRaw, i64* %17, align 8, !dbg !16, !tbaa !6 - %18 = getelementptr inbounds i64, i64* %17, i64 1, !dbg !16 - store i64 %5, i64* %18, align 8, !dbg !16, !tbaa !6 - %19 = getelementptr inbounds i64, i64* %18, i64 1, !dbg !16 - store i64* %19, i64** %16, align 8, !dbg !16 - %send43 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts.2, i64 0), !dbg !16 - store i64* getelementptr inbounds ([13 x i64], [13 x i64]* @iseqEncodedArray, i64 0, i64 9), i64** %0, align 8, !dbg !16, !tbaa !20 - %20 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 1, !dbg !17 - %21 = load i64*, i64** %20, align 8, !dbg !17 - store i64 %selfRaw, i64* %21, align 8, !dbg !17, !tbaa !6 - %22 = getelementptr inbounds i64, i64* %21, i64 1, !dbg !17 - store i64 %5, i64* %22, align 8, !dbg !17, !tbaa !6 - %23 = getelementptr inbounds i64, i64* %22, i64 1, !dbg !17 - store i64* %23, i64** %20, align 8, !dbg !17 - %send45 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts.3, i64 0), !dbg !17 - ret i64 %send45 -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_Object#3foo"(i64 %realpath) unnamed_addr #5 { -entryInitializers: - %rubyId_foo = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleIDTable, i64 0, i64 0), align 8, !invariant.load !5 - %rubyStr_foo = load i64, i64* getelementptr inbounds ([4 x i64], [4 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 0), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/constant_cache.rb" = load i64, i64* getelementptr inbounds ([4 x i64], [4 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %rubyStr_foo, i64 %rubyId_foo, i64 %"rubyStr_test/testdata/compiler/constant_cache.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 1, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 2) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Object#3foo", align 8 - ret void -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_.13"(i64 %realpath) unnamed_addr #5 { -entryInitializers: - %"rubyId_" = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleIDTable, i64 0, i64 2), align 8, !invariant.load !5 - %"rubyStr_" = load i64, i64* getelementptr inbounds ([4 x i64], [4 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 2), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/constant_cache.rb" = load i64, i64* getelementptr inbounds ([4 x i64], [4 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_", i64 %"rubyId_", i64 %"rubyStr_test/testdata/compiler/constant_cache.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 0, i32 noundef 4, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 4) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - ret void -} - -; Function Attrs: sspreq -define void @Init_constant_cache() local_unnamed_addr #6 { -entry: - %realpath = tail call i64 @sorbet_readRealpath() - tail call fastcc void @sorbet_globalConstructors(i64 %realpath) - %0 = load %struct.rb_vm_struct*, %struct.rb_vm_struct** @ruby_current_vm_ptr, align 8, !tbaa !20 - %1 = getelementptr inbounds %struct.rb_vm_struct, %struct.rb_vm_struct* %0, i64 0, i32 18 - %2 = load i64, i64* %1, align 8, !tbaa !28 - %3 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !20 - %4 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %3, i64 0, i32 2 - %5 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %4, align 8, !tbaa !37 - %stackFrame.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - %6 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame.i, %struct.rb_iseq_struct** %6, align 8, !tbaa !40 - %7 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 4 - %8 = load i64*, i64** %7, align 8, !tbaa !42 - %9 = load i64, i64* %8, align 8, !tbaa !6 - %10 = and i64 %9, -33 - store i64 %10, i64* %8, align 8, !tbaa !6 - tail call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %3, %struct.rb_control_frame_struct* %5, %struct.rb_iseq_struct* %stackFrame.i) #11 - %11 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 0 - store i64* getelementptr inbounds ([13 x i64], [13 x i64]* @iseqEncodedArray, i64 0, i64 4), i64** %11, align 8, !dbg !43, !tbaa !20 - %12 = load i64, i64* @rb_cObject, align 8, !dbg !44 - %13 = tail call i64 @rb_define_class(i8* getelementptr inbounds ([100 x i8], [100 x i8]* @sorbet_moduleStringTable, i64 0, i64 45), i64 %12) #11, !dbg !44 - %14 = tail call %struct.rb_control_frame_struct* @sorbet_pushStaticInitFrame(i64 %13) #11, !dbg !44 - %stackFrame.i.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_A.13", align 8 - %15 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !20 - %16 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %15, i64 0, i32 2 - %17 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %16, align 8, !tbaa !37 - %18 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %17, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame.i.i, %struct.rb_iseq_struct** %18, align 8, !tbaa !40 - %19 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %17, i64 0, i32 4 - %20 = load i64*, i64** %19, align 8, !tbaa !42 - %21 = load i64, i64* %20, align 8, !tbaa !6 - %22 = and i64 %21, -33 - store i64 %22, i64* %20, align 8, !tbaa !6 - tail call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %15, %struct.rb_control_frame_struct* %17, %struct.rb_iseq_struct* %stackFrame.i.i) #11 - %23 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %14, i64 0, i32 0 - store i64* getelementptr inbounds ([13 x i64], [13 x i64]* @iseqEncodedArray, i64 0, i64 4), i64** %23, align 8, !dbg !45, !tbaa !20 - tail call void @sorbet_popFrame() #11, !dbg !44 - store i64* getelementptr inbounds ([13 x i64], [13 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %11, align 8, !dbg !44, !tbaa !20 - %stackFrame21.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Object#3foo", align 8, !dbg !48 - %24 = tail call noalias nonnull i8* @ruby_xcalloc(i64 noundef 1, i64 noundef 64) #12, !dbg !48 - %25 = bitcast i8* %24 to i16*, !dbg !48 - %26 = load i16, i16* %25, align 8, !dbg !48 - %27 = and i16 %26, -384, !dbg !48 - store i16 %27, i16* %25, align 8, !dbg !48 - %28 = getelementptr inbounds i8, i8* %24, i64 4, !dbg !48 - tail call void @llvm.memset.p0i8.i64(i8* nonnull align 4 %28, i8 0, i64 28, i1 false) #11, !dbg !48 - tail call void @sorbet_vm_define_method(i64 %12, i8* noundef getelementptr inbounds ([100 x i8], [100 x i8]* @sorbet_moduleStringTable, i64 0, i64 0), i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)* noundef @"func_Object#3foo", i8* nonnull %24, %struct.rb_iseq_struct* %stackFrame21.i, i1 noundef zeroext false) #11, !dbg !48 - store i64* getelementptr inbounds ([13 x i64], [13 x i64]* @iseqEncodedArray, i64 0, i64 12), i64** %11, align 8, !dbg !48, !tbaa !20 - %29 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !18 - %30 = load i64*, i64** %29, align 8, !dbg !18 - store i64 %2, i64* %30, align 8, !dbg !18, !tbaa !6 - %31 = getelementptr inbounds i64, i64* %30, i64 1, !dbg !18 - store i64* %31, i64** %29, align 8, !dbg !18 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_foo, i64 0), !dbg !18 - ret void -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_A.13"(i64 %realpath) unnamed_addr #5 { -entryInitializers: - %"rubyId_" = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleIDTable, i64 0, i64 4), align 8, !invariant.load !5 - %"rubyStr_" = load i64, i64* getelementptr inbounds ([4 x i64], [4 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 3), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/constant_cache.rb" = load i64, i64* getelementptr inbounds ([4 x i64], [4 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_", i64 %"rubyId_", i64 %"rubyStr_test/testdata/compiler/constant_cache.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 3, i32 noundef 4, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 0) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_A.13", align 8 - ret void -} - -; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly -declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #7 - -; Function Attrs: nofree nosync nounwind willreturn -declare void @llvm.assume(i1 noundef) #8 - -; Function Attrs: ssp -define linkonce void @const_recompute_A() local_unnamed_addr #5 { - %1 = tail call i64 @sorbet_getConstant(i8* getelementptr inbounds ([100 x i8], [100 x i8]* @sorbet_moduleStringTable, i64 0, i64 45), i64 1) - store i64 %1, i64* @guarded_const_A, align 8 - %2 = load i64, i64* @ruby_vm_global_constant_state, align 8, !tbaa !25 - store i64 %2, i64* @guard_epoch_A, align 8 - ret void -} - -attributes #0 = { noreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #1 = { "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #2 = { allocsize(0,1) "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #3 = { nounwind ssp uwtable "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #4 = { nounwind sspreq uwtable } -attributes #5 = { ssp } -attributes #6 = { sspreq } -attributes #7 = { argmemonly nofree nosync nounwind willreturn writeonly } -attributes #8 = { nofree nosync nounwind willreturn } -attributes #9 = { noreturn nounwind } -attributes #10 = { noreturn } -attributes #11 = { nounwind } -attributes #12 = { nounwind allocsize(0,1) } - -!llvm.module.flags = !{!0, !1, !2} -!llvm.dbg.cu = !{!3} - -!0 = !{i32 2, !"Debug Info Version", i32 3} -!1 = !{i32 4, !"cf-protection-return", i32 1} -!2 = !{i32 4, !"cf-protection-branch", i32 1} -!3 = distinct !DICompileUnit(language: DW_LANG_C, file: !4, producer: "Sorbet LLVM", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !5) -!4 = !DIFile(filename: "test/testdata/compiler/constant_cache.rb", directory: ".") -!5 = !{} -!6 = !{!7, !7, i64 0} -!7 = !{!"long", !8, i64 0} -!8 = !{!"omnipotent char", !9, i64 0} -!9 = !{!"Simple C/C++ TBAA"} -!10 = !DILocation(line: 6, column: 3, scope: !11) -!11 = distinct !DISubprogram(name: "Object#foo", linkageName: "func_Object#3foo", scope: null, file: !4, line: 5, type: !12, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!12 = !DISubroutineType(types: !13) -!13 = !{!14} -!14 = !DIBasicType(name: "VALUE", size: 64, encoding: DW_ATE_signed) -!15 = !DILocation(line: 7, column: 3, scope: !11) -!16 = !DILocation(line: 8, column: 3, scope: !11) -!17 = !DILocation(line: 9, column: 3, scope: !11) -!18 = !DILocation(line: 12, column: 1, scope: !19) -!19 = distinct !DISubprogram(name: ".", linkageName: "func_.13", scope: null, file: !4, line: 4, type: !12, scopeLine: 4, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!20 = !{!21, !21, i64 0} -!21 = !{!"any pointer", !8, i64 0} -!22 = !DILocation(line: 5, column: 1, scope: !11) -!23 = !{!"branch_weights", i32 1, i32 2000} -!24 = !DILocation(line: 0, scope: !11) -!25 = !{!26, !26, i64 0} -!26 = !{!"long long", !8, i64 0} -!27 = !{!"branch_weights", i32 1, i32 10000} -!28 = !{!29, !7, i64 400} -!29 = !{!"rb_vm_struct", !7, i64 0, !30, i64 8, !21, i64 192, !21, i64 200, !21, i64 208, !26, i64 216, !8, i64 224, !31, i64 264, !31, i64 280, !31, i64 296, !31, i64 312, !7, i64 328, !33, i64 336, !33, i64 340, !33, i64 344, !33, i64 344, !33, i64 344, !33, i64 344, !33, i64 348, !7, i64 352, !8, i64 360, !7, i64 400, !7, i64 408, !7, i64 416, !7, i64 424, !7, i64 432, !7, i64 440, !7, i64 448, !21, i64 456, !21, i64 464, !34, i64 472, !35, i64 992, !21, i64 1016, !21, i64 1024, !33, i64 1032, !33, i64 1036, !31, i64 1040, !8, i64 1056, !7, i64 1096, !7, i64 1104, !7, i64 1112, !7, i64 1120, !7, i64 1128, !33, i64 1136, !21, i64 1144, !21, i64 1152, !21, i64 1160, !21, i64 1168, !21, i64 1176, !21, i64 1184, !33, i64 1192, !36, i64 1200, !8, i64 1232} -!30 = !{!"rb_global_vm_lock_struct", !21, i64 0, !8, i64 8, !31, i64 48, !21, i64 64, !33, i64 72, !8, i64 80, !8, i64 128, !33, i64 176, !33, i64 180} -!31 = !{!"list_head", !32, i64 0} -!32 = !{!"list_node", !21, i64 0, !21, i64 8} -!33 = !{!"int", !8, i64 0} -!34 = !{!"", !8, i64 0} -!35 = !{!"rb_hook_list_struct", !21, i64 0, !33, i64 8, !33, i64 12, !33, i64 16} -!36 = !{!"", !7, i64 0, !7, i64 8, !7, i64 16, !7, i64 24} -!37 = !{!38, !21, i64 16} -!38 = !{!"rb_execution_context_struct", !21, i64 0, !7, i64 8, !21, i64 16, !21, i64 24, !21, i64 32, !33, i64 40, !33, i64 44, !21, i64 48, !21, i64 56, !21, i64 64, !7, i64 72, !7, i64 80, !21, i64 88, !7, i64 96, !21, i64 104, !21, i64 112, !7, i64 120, !7, i64 128, !8, i64 136, !8, i64 137, !7, i64 144, !39, i64 152} -!39 = !{!"", !21, i64 0, !21, i64 8, !7, i64 16, !8, i64 24} -!40 = !{!41, !21, i64 16} -!41 = !{!"rb_control_frame_struct", !21, i64 0, !21, i64 8, !21, i64 16, !7, i64 24, !21, i64 32, !21, i64 40, !21, i64 48} -!42 = !{!41, !21, i64 32} -!43 = !DILocation(line: 0, scope: !19) -!44 = !DILocation(line: 4, column: 1, scope: !19) -!45 = !DILocation(line: 0, scope: !46, inlinedAt: !47) -!46 = distinct !DISubprogram(name: "A.", linkageName: "func_A.13L61", scope: null, file: !4, line: 4, type: !12, scopeLine: 4, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!47 = distinct !DILocation(line: 4, column: 1, scope: !19) -!48 = !DILocation(line: 5, column: 1, scope: !19) diff --git a/test/testdata/compiler/custom_plus.opt.ll.exp b/test/testdata/compiler/custom_plus.opt.ll.exp deleted file mode 100644 index cd122dbb77..0000000000 --- a/test/testdata/compiler/custom_plus.opt.ll.exp +++ /dev/null @@ -1,550 +0,0 @@ -; ModuleID = 'payload' -source_filename = "llvm-link" -target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } -%struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.__pthread_cond_s } -%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } -%union.anon = type { i64 } -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } -%struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } -%struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } -%struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } -%struct.anon.0 = type { i16, [2 x i8] } -%struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } -%struct.rb_iseq_location_struct = type { i64, i64, i64, i64, i32, %struct.rb_code_location_struct } -%struct.rb_code_location_struct = type { %struct.rb_code_position_struct, %struct.rb_code_position_struct } -%struct.rb_code_position_struct = type { i32, i32 } -%struct.iseq_insn_info = type { %struct.iseq_insn_info_entry*, i32*, i32, %struct.succ_index_table* } -%struct.iseq_insn_info_entry = type opaque -%struct.succ_index_table = type opaque -%struct.iseq_catch_table = type opaque -%union.iseq_inline_storage_entry = type { %struct.iseq_inline_cache_entry } -%struct.iseq_inline_cache_entry = type { i64, %struct.rb_cref_struct*, i64 } -%struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } -%struct.rb_scope_visi_struct = type { i8, [3 x i8] } -%struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } -%struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } -%union.anon.13 = type { %struct.rb_method_cfunc_struct } -%struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%union.anon.15 = type { i32 } -%struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.16 = type { i64, i64, i64, i64* } -%union.anon.17 = type { %struct.anon.18 } -%struct.anon.18 = type { i64, i32 } -%struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } -%struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } -%struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } -%struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } -%struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } -%struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } -%struct.__sigset_t = type { [16 x i64] } -%struct.rb_calling_info = type { i64, i64, i32, i32 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } -%union.anon.9 = type { %union.pthread_cond_t } -%struct.rb_unblock_callback = type { void (i8*)*, i8* } -%struct.rb_mutex_struct = type opaque -%struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.10 = type { %struct.anon.11 } -%struct.anon.11 = type { i64, i64, i32 } -%struct.rb_fiber_struct = type opaque -%struct.anon.5 = type { [65 x i64] } -%struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } -%struct.rb_event_hook_struct = type opaque -%struct.rb_postponed_job_struct = type opaque -%struct.list_head = type { %struct.list_node } -%union.pthread_mutex_t = type { %struct.__pthread_mutex_s } -%struct.__pthread_mutex_s = type { i32, i32, i32, i32, i32, i16, i16, %struct.__pthread_internal_list } -%struct.__pthread_internal_list = type { %struct.__pthread_internal_list*, %struct.__pthread_internal_list* } -%struct.rb_objspace = type opaque -%struct.rb_at_exit_list = type { void (%struct.rb_vm_struct*)*, %struct.rb_at_exit_list* } -%struct.st_table = type { i8, i8, i8, i32, %struct.st_hash_type*, i64, i64*, i64, i64, %struct.st_table_entry* } -%struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } -%struct.st_table_entry = type opaque -%struct.rb_builtin_function = type opaque -%struct.anon.6 = type { i64, i64, i64, i64 } -%struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } -%struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } -%struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } -%struct.rb_call_info_with_kwarg = type { %struct.rb_call_info, %struct.rb_call_info_kw_arg* } -%struct.rb_call_info_kw_arg = type { i32, [1 x i64] } - -@ruby_current_vm_ptr = external local_unnamed_addr global %struct.rb_vm_struct*, align 8 -@ruby_current_execution_context_ptr = external local_unnamed_addr global %struct.rb_execution_context_struct*, align 8 -@ruby_vm_global_constant_state = external local_unnamed_addr global i64, align 8 -@rb_eRuntimeError = external local_unnamed_addr global i64, align 8 -@.str.9 = private unnamed_addr constant [95 x i8] c"sorbet_getBuildSCMRevision: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@.str.10 = private unnamed_addr constant [93 x i8] c"sorbet_getIsReleaseBuild: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@"stackFramePrecomputed_func_Object#8delegate" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@iseqEncodedArray = internal global [23 x i64] zeroinitializer -@fileLineNumberInfo = internal global %struct.SorbetLineNumberInfo zeroinitializer -@"ic_+" = internal global %struct.FunctionInlineCache zeroinitializer -@"ic_==" = internal global %struct.FunctionInlineCache zeroinitializer -@ic_puts = internal global %struct.FunctionInlineCache zeroinitializer -@ic_puts.1 = internal global %struct.FunctionInlineCache zeroinitializer -@"stackFramePrecomputed_func_.13" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@ic_new = internal global %struct.FunctionInlineCache zeroinitializer -@ic_initialize = internal global %struct.FunctionInlineCache zeroinitializer -@ic_delegate = internal global %struct.FunctionInlineCache zeroinitializer -@"stackFramePrecomputed_func_A#1+" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"stackFramePrecomputed_func_A.13" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@sorbet_moduleStringTable = internal unnamed_addr constant [134 x i8] c"delegate\00test/testdata/compiler/custom_plus.rb\00+\00==\00ok\00puts\00fail\00\00A\00Object\00new\00initialize\00normal\00a\00master\00\00b\00", align 1 -@sorbet_moduleIDTable = internal unnamed_addr global [11 x i64] zeroinitializer, align 8 -@sorbet_moduleIDDescriptors = internal unnamed_addr constant [11 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 8 }, %struct.rb_code_position_struct { i32 47, i32 1 }, %struct.rb_code_position_struct { i32 49, i32 2 }, %struct.rb_code_position_struct { i32 55, i32 4 }, %struct.rb_code_position_struct { i32 65, i32 16 }, %struct.rb_code_position_struct { i32 91, i32 3 }, %struct.rb_code_position_struct { i32 95, i32 10 }, %struct.rb_code_position_struct { i32 106, i32 6 }, %struct.rb_code_position_struct { i32 113, i32 1 }, %struct.rb_code_position_struct { i32 122, i32 9 }, %struct.rb_code_position_struct { i32 132, i32 1 }], align 8 -@sorbet_moduleRubyStringTable = internal unnamed_addr global [7 x i64] zeroinitializer, align 8 -@sorbet_moduleRubyStringDescriptors = internal unnamed_addr constant [7 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 8 }, %struct.rb_code_position_struct { i32 9, i32 37 }, %struct.rb_code_position_struct { i32 52, i32 2 }, %struct.rb_code_position_struct { i32 60, i32 4 }, %struct.rb_code_position_struct { i32 65, i32 16 }, %struct.rb_code_position_struct { i32 47, i32 1 }, %struct.rb_code_position_struct { i32 122, i32 9 }], align 8 -@rb_cObject = external local_unnamed_addr constant i64 -@guard_epoch_A = linkonce local_unnamed_addr global i64 0 -@guarded_const_A = linkonce local_unnamed_addr global i64 0 - -; Function Attrs: noreturn -declare void @sorbet_raiseArity(i32, i32, i32) local_unnamed_addr #0 - -declare %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64, i64, i64, i64, %struct.rb_iseq_struct*, i32, i32, %struct.SorbetLineNumberInfo*, i64*, i32, i32) local_unnamed_addr #1 - -declare void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo*, i64*, i32) local_unnamed_addr #1 - -declare i64 @sorbet_getConstant(i8*, i64) local_unnamed_addr #1 - -declare i64 @sorbet_readRealpath() local_unnamed_addr #1 - -declare %struct.rb_control_frame_struct* @sorbet_pushStaticInitFrame(i64) local_unnamed_addr #1 - -declare void @sorbet_popFrame() local_unnamed_addr #1 - -declare void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) local_unnamed_addr #1 - -declare i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache*, i64) local_unnamed_addr #1 - -declare void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_iseq_struct*) local_unnamed_addr #1 - -declare void @sorbet_vm_define_method(i64, i8*, i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)*, i8*, %struct.rb_iseq_struct*, i1 zeroext) local_unnamed_addr #1 - -declare void @sorbet_vm_intern_ids(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #1 - -declare void @sorbet_vm_init_string_table(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #1 - -declare i64 @sorbet_maybeAllocateObjectFastPath(i64, %struct.FunctionInlineCache*) local_unnamed_addr #1 - -declare i64 @sorbet_vm_plus(%struct.rb_control_frame_struct*, %struct.FunctionInlineCache*, i64, i64) local_unnamed_addr #1 - -declare i64 @sorbet_vm_eqeq(%struct.rb_control_frame_struct*, %struct.FunctionInlineCache*, i64, i64) local_unnamed_addr #1 - -declare i64 @rb_define_class(i8*, i64) local_unnamed_addr #1 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #2 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #2 - -; Function Attrs: noreturn -declare void @rb_raise(i64, i8*, ...) local_unnamed_addr #0 - -; Function Attrs: allocsize(0,1) -declare noalias nonnull i8* @ruby_xcalloc(i64, i64) local_unnamed_addr #3 - -; Function Attrs: allocsize(0,1) -declare noalias nonnull i8* @ruby_xmalloc2(i64, i64) local_unnamed_addr #3 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) #2 - -; Function Attrs: nounwind ssp uwtable -define weak i32 @sorbet_getIsReleaseBuild() local_unnamed_addr #4 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([93 x i8], [93 x i8]* @.str.10, i64 0, i64 0)) #9 - unreachable -} - -; Function Attrs: nounwind ssp uwtable -define weak i8* @sorbet_getBuildSCMRevision() local_unnamed_addr #4 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([95 x i8], [95 x i8]* @.str.9, i64 0, i64 0)) #9 - unreachable -} - -define internal fastcc void @sorbet_globalConstructors(i64 %realpath) unnamed_addr { -allocRubyIds: - tail call void @sorbet_vm_intern_ids(i64* noundef getelementptr inbounds ([11 x i64], [11 x i64]* @sorbet_moduleIDTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([11 x %struct.rb_code_position_struct], [11 x %struct.rb_code_position_struct]* @sorbet_moduleIDDescriptors, i32 0, i32 0), i32 noundef 11, i8* noundef getelementptr inbounds ([134 x i8], [134 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_vm_init_string_table(i64* noundef getelementptr inbounds ([7 x i64], [7 x i64]* @sorbet_moduleRubyStringTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([7 x %struct.rb_code_position_struct], [7 x %struct.rb_code_position_struct]* @sorbet_moduleRubyStringDescriptors, i32 0, i32 0), i32 noundef 7, i8* noundef getelementptr inbounds ([134 x i8], [134 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef getelementptr inbounds ([23 x i64], [23 x i64]* @iseqEncodedArray, i32 0, i32 0), i32 noundef 23) - tail call fastcc void @"Constr_stackFramePrecomputed_func_Object#8delegate"(i64 %realpath) - %"rubyId_+" = load i64, i64* getelementptr inbounds ([11 x i64], [11 x i64]* @sorbet_moduleIDTable, i64 0, i64 1), align 8, !dbg !10, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @"ic_+", i64 %"rubyId_+", i32 noundef 16, i32 noundef 1, i32 noundef 0), !dbg !10 - %"rubyId_==" = load i64, i64* getelementptr inbounds ([11 x i64], [11 x i64]* @sorbet_moduleIDTable, i64 0, i64 2), align 8, !dbg !10, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @"ic_==", i64 %"rubyId_==", i32 noundef 16, i32 noundef 1, i32 noundef 0), !dbg !10 - %rubyId_puts = load i64, i64* getelementptr inbounds ([11 x i64], [11 x i64]* @sorbet_moduleIDTable, i64 0, i64 3), align 8, !dbg !15, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts, i64 %rubyId_puts, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !15 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts.1, i64 %rubyId_puts, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !16 - tail call fastcc void @"Constr_stackFramePrecomputed_func_.13"(i64 %realpath) - %rubyId_new = load i64, i64* getelementptr inbounds ([11 x i64], [11 x i64]* @sorbet_moduleIDTable, i64 0, i64 5), align 8, !dbg !17, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_new, i64 %rubyId_new, i32 noundef 16, i32 noundef 0, i32 noundef 0), !dbg !17 - %rubyId_initialize = load i64, i64* getelementptr inbounds ([11 x i64], [11 x i64]* @sorbet_moduleIDTable, i64 0, i64 6), align 8, !dbg !17, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_initialize, i64 %rubyId_initialize, i32 noundef 20, i32 noundef 0, i32 noundef 0), !dbg !17 - %rubyId_delegate = load i64, i64* getelementptr inbounds ([11 x i64], [11 x i64]* @sorbet_moduleIDTable, i64 0, i64 0), align 8, !dbg !19, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_delegate, i64 %rubyId_delegate, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !19 - tail call fastcc void @"Constr_stackFramePrecomputed_func_A#1+"(i64 %realpath) - tail call fastcc void @"Constr_stackFramePrecomputed_func_A.13"(i64 %realpath) - ret void -} - -; Function Attrs: nounwind sspreq uwtable -define internal i64 @"func_Object#8delegate"(i32 %argc, i64* nocapture readonly %argArray, i64 %selfRaw, %struct.rb_control_frame_struct* nonnull align 8 dereferenceable(8) %cfp, i8* nocapture nofree readnone %calling, i8* nocapture nofree readnone %callData) #5 !dbg !11 { -functionEntryInitializers: - %0 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 0 - store i64* getelementptr inbounds ([23 x i64], [23 x i64]* @iseqEncodedArray, i64 0, i64 14), i64** %0, align 8, !tbaa !20 - %tooManyArgs = icmp ugt i32 %argc, 1, !dbg !22 - %tooFewArgs = icmp ult i32 %argc, 1, !dbg !22 - %or.cond = or i1 %tooManyArgs, %tooFewArgs, !dbg !22 - br i1 %or.cond, label %argCountFailBlock, label %fillRequiredArgs, !dbg !22, !prof !23 - -argCountFailBlock: ; preds = %functionEntryInitializers - tail call void @sorbet_raiseArity(i32 %argc, i32 noundef 1, i32 noundef 1) #10, !dbg !22 - unreachable, !dbg !22 - -fillRequiredArgs: ; preds = %functionEntryInitializers - %rawArg_a = load i64, i64* %argArray, align 8, !dbg !22 - store i64* getelementptr inbounds ([23 x i64], [23 x i64]* @iseqEncodedArray, i64 0, i64 15), i64** %0, align 8, !dbg !24, !tbaa !20 - %1 = tail call i64 @sorbet_vm_plus(%struct.rb_control_frame_struct* nonnull align 8 dereferenceable(8) %cfp, %struct.FunctionInlineCache* noundef @"ic_+", i64 %rawArg_a, i64 noundef 3), !dbg !10 - %2 = tail call i64 @sorbet_vm_eqeq(%struct.rb_control_frame_struct* nonnull %cfp, %struct.FunctionInlineCache* noundef @"ic_==", i64 %1, i64 %rawArg_a), !dbg !10 - %3 = and i64 %2, -9, !dbg !10 - %4 = icmp ne i64 %3, 0, !dbg !10 - %.sink42 = select i1 %4, i64* getelementptr inbounds ([23 x i64], [23 x i64]* @iseqEncodedArray, i64 0, i64 16), i64* getelementptr inbounds ([23 x i64], [23 x i64]* @iseqEncodedArray, i64 0, i64 18), !dbg !10 - %.sink = select i1 %4, i64* getelementptr inbounds ([7 x i64], [7 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 2), i64* getelementptr inbounds ([7 x i64], [7 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 3), !dbg !10 - %ic_puts.1.sink = select i1 %4, %struct.FunctionInlineCache* @ic_puts, %struct.FunctionInlineCache* @ic_puts.1, !dbg !10 - store i64* %.sink42, i64** %0, align 8, !tbaa !20 - %rubyStr_fail = load i64, i64* %.sink, align 8, !dbg !25, !invariant.load !5 - %5 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 1, !dbg !25 - %6 = load i64*, i64** %5, align 8, !dbg !25 - store i64 %selfRaw, i64* %6, align 8, !dbg !25, !tbaa !6 - %7 = getelementptr inbounds i64, i64* %6, i64 1, !dbg !25 - store i64 %rubyStr_fail, i64* %7, align 8, !dbg !25, !tbaa !6 - %8 = getelementptr inbounds i64, i64* %7, i64 1, !dbg !25 - store i64* %8, i64** %5, align 8, !dbg !25 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* %ic_puts.1.sink, i64 0), !dbg !25 - store i64* getelementptr inbounds ([23 x i64], [23 x i64]* @iseqEncodedArray, i64 0, i64 20), i64** %0, align 8, !tbaa !20 - ret i64 %send -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_Object#8delegate"(i64 %realpath) unnamed_addr #6 { -entryInitializers: - %rubyId_delegate = load i64, i64* getelementptr inbounds ([11 x i64], [11 x i64]* @sorbet_moduleIDTable, i64 0, i64 0), align 8, !invariant.load !5 - %rubyStr_delegate = load i64, i64* getelementptr inbounds ([7 x i64], [7 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 0), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/custom_plus.rb" = load i64, i64* getelementptr inbounds ([7 x i64], [7 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %rubyStr_delegate, i64 %rubyId_delegate, i64 %"rubyStr_test/testdata/compiler/custom_plus.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 1, i32 noundef 14, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 2) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Object#8delegate", align 8 - ret void -} - -; Function Attrs: nounwind sspreq uwtable -define internal fastcc void @"func_.13"(i64 %selfRaw, %struct.rb_control_frame_struct* %cfp) unnamed_addr #5 !dbg !18 { -functionEntryInitializers: - %positional_table.i = alloca i64, align 8, !dbg !26 - %stackFrame = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - %0 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !20 - %1 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %0, i64 0, i32 2 - %2 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %1, align 8, !tbaa !29 - %3 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame, %struct.rb_iseq_struct** %3, align 8, !tbaa !33 - %4 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 4 - %5 = load i64*, i64** %4, align 8, !tbaa !35 - %6 = load i64, i64* %5, align 8, !tbaa !6 - %7 = and i64 %6, -33 - store i64 %7, i64* %5, align 8, !tbaa !6 - tail call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %0, %struct.rb_control_frame_struct* %2, %struct.rb_iseq_struct* %stackFrame) #11 - %8 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 0 - store i64* getelementptr inbounds ([23 x i64], [23 x i64]* @iseqEncodedArray, i64 0, i64 6), i64** %8, align 8, !dbg !36, !tbaa !20 - %9 = load i64, i64* @rb_cObject, align 8, !dbg !37 - %10 = tail call i64 @rb_define_class(i8* getelementptr inbounds ([134 x i8], [134 x i8]* @sorbet_moduleStringTable, i64 0, i64 82), i64 %9) #11, !dbg !37 - %11 = tail call %struct.rb_control_frame_struct* @sorbet_pushStaticInitFrame(i64 %10) #11, !dbg !37 - %12 = bitcast i64* %positional_table.i to i8* - call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %12) - %stackFrame.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_A.13", align 8 - %13 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !20 - %14 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %13, i64 0, i32 2 - %15 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %14, align 8, !tbaa !29 - %16 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %15, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame.i, %struct.rb_iseq_struct** %16, align 8, !tbaa !33 - %17 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %15, i64 0, i32 4 - %18 = load i64*, i64** %17, align 8, !tbaa !35 - %19 = load i64, i64* %18, align 8, !tbaa !6 - %20 = and i64 %19, -33 - store i64 %20, i64* %18, align 8, !tbaa !6 - tail call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %13, %struct.rb_control_frame_struct* %15, %struct.rb_iseq_struct* %stackFrame.i) #11 - %21 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %11, i64 0, i32 0 - store i64* getelementptr inbounds ([23 x i64], [23 x i64]* @iseqEncodedArray, i64 0, i64 7), i64** %21, align 8, !dbg !38, !tbaa !20 - %22 = load i64, i64* @guard_epoch_A, align 8, !dbg !26 - %23 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !26, !tbaa !39 - %needTakeSlowPath = icmp ne i64 %22, %23, !dbg !26 - br i1 %needTakeSlowPath, label %24, label %25, !dbg !26, !prof !41 - -24: ; preds = %functionEntryInitializers - tail call void @const_recompute_A(), !dbg !26 - br label %25, !dbg !26 - -25: ; preds = %functionEntryInitializers, %24 - %26 = load i64, i64* @guarded_const_A, align 8, !dbg !26 - %27 = load i64, i64* @guard_epoch_A, align 8, !dbg !26 - %28 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !26, !tbaa !39 - %guardUpdated = icmp eq i64 %27, %28, !dbg !26 - tail call void @llvm.assume(i1 %guardUpdated), !dbg !26 - %stackFrame7.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_A#1+", align 8, !dbg !26 - %29 = tail call noalias nonnull i8* @ruby_xcalloc(i64 noundef 1, i64 noundef 64) #12, !dbg !26 - %30 = bitcast i8* %29 to i16*, !dbg !26 - %31 = load i16, i16* %30, align 8, !dbg !26 - %32 = and i16 %31, -384, !dbg !26 - %33 = or i16 %32, 1, !dbg !26 - store i16 %33, i16* %30, align 8, !dbg !26 - %34 = getelementptr inbounds i8, i8* %29, i64 8, !dbg !26 - %35 = bitcast i8* %34 to i32*, !dbg !26 - store i32 1, i32* %35, align 8, !dbg !26, !tbaa !42 - %36 = getelementptr inbounds i8, i8* %29, i64 12, !dbg !26 - %37 = getelementptr inbounds i8, i8* %29, i64 4, !dbg !26 - %38 = bitcast i8* %37 to i32*, !dbg !26 - tail call void @llvm.memset.p0i8.i64(i8* nonnull align 4 %36, i8 0, i64 20, i1 false) #11, !dbg !26 - store i32 1, i32* %38, align 4, !dbg !26, !tbaa !45 - %rubyId_b.i = load i64, i64* getelementptr inbounds ([11 x i64], [11 x i64]* @sorbet_moduleIDTable, i64 0, i64 10), align 8, !dbg !26, !invariant.load !5 - store i64 %rubyId_b.i, i64* %positional_table.i, align 8, !dbg !26 - %39 = tail call noalias nonnull i8* @ruby_xmalloc2(i64 noundef 1, i64 noundef 8) #12, !dbg !26 - call void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture nonnull writeonly align 1 %39, i8* nocapture noundef nonnull readonly align 8 dereferenceable(8) %12, i64 noundef 8, i1 noundef false) #11, !dbg !26 - %40 = getelementptr inbounds i8, i8* %29, i64 32, !dbg !26 - %41 = bitcast i8* %40 to i8**, !dbg !26 - store i8* %39, i8** %41, align 8, !dbg !26, !tbaa !46 - tail call void @sorbet_vm_define_method(i64 %26, i8* getelementptr inbounds ([134 x i8], [134 x i8]* @sorbet_moduleStringTable, i64 0, i64 47), i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)* noundef @"func_A#1+", i8* nonnull %29, %struct.rb_iseq_struct* %stackFrame7.i, i1 noundef zeroext false) #11, !dbg !26 - call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %12) - tail call void @sorbet_popFrame() #11, !dbg !37 - store i64* getelementptr inbounds ([23 x i64], [23 x i64]* @iseqEncodedArray, i64 0, i64 12), i64** %8, align 8, !dbg !37, !tbaa !20 - %42 = tail call i64 @sorbet_maybeAllocateObjectFastPath(i64 %26, %struct.FunctionInlineCache* noundef @ic_new), !dbg !17 - %43 = icmp eq i64 %42, 52, !dbg !17 - br i1 %43, label %slowNew, label %fastNew, !dbg !17 - -slowNew: ; preds = %25 - %44 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 1, !dbg !17 - %45 = load i64*, i64** %44, align 8, !dbg !17 - store i64 %26, i64* %45, align 8, !dbg !17, !tbaa !6 - %46 = getelementptr inbounds i64, i64* %45, i64 1, !dbg !17 - store i64* %46, i64** %44, align 8, !dbg !17 - %47 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* noundef @ic_new, i64 noundef 0), !dbg !17 - br label %afterNew, !dbg !17 - -fastNew: ; preds = %25 - %48 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 1, !dbg !17 - %49 = load i64*, i64** %48, align 8, !dbg !17 - store i64 %42, i64* %49, align 8, !dbg !17, !tbaa !6 - %50 = getelementptr inbounds i64, i64* %49, i64 1, !dbg !17 - store i64* %50, i64** %48, align 8, !dbg !17 - %51 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* noundef @ic_initialize, i64 noundef 0), !dbg !17 - br label %afterNew, !dbg !17 - -afterNew: ; preds = %fastNew, %slowNew - %initializedObject = phi i64 [ %47, %slowNew ], [ %42, %fastNew ], !dbg !17 - store i64* getelementptr inbounds ([23 x i64], [23 x i64]* @iseqEncodedArray, i64 0, i64 14), i64** %8, align 8, !dbg !17, !tbaa !20 - %stackFrame28 = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Object#8delegate", align 8, !dbg !47 - %52 = tail call noalias nonnull i8* @ruby_xcalloc(i64 noundef 1, i64 noundef 64) #12, !dbg !47 - %53 = bitcast i8* %52 to i16*, !dbg !47 - %54 = load i16, i16* %53, align 8, !dbg !47 - %55 = and i16 %54, -384, !dbg !47 - %56 = or i16 %55, 1, !dbg !47 - store i16 %56, i16* %53, align 8, !dbg !47 - %57 = getelementptr inbounds i8, i8* %52, i64 8, !dbg !47 - %58 = bitcast i8* %57 to i32*, !dbg !47 - store i32 1, i32* %58, align 8, !dbg !47, !tbaa !42 - %59 = getelementptr inbounds i8, i8* %52, i64 12, !dbg !47 - %60 = bitcast i8* %59 to i32*, !dbg !47 - %61 = getelementptr inbounds i8, i8* %52, i64 4, !dbg !47 - %62 = bitcast i8* %61 to i32*, !dbg !47 - tail call void @llvm.memset.p0i8.i64(i8* nonnull align 4 %59, i8 0, i64 20, i1 false), !dbg !47 - store i32 1, i32* %62, align 4, !dbg !47, !tbaa !45 - %positional_table = alloca i64, align 8, !dbg !47 - %rubyId_a = load i64, i64* getelementptr inbounds ([11 x i64], [11 x i64]* @sorbet_moduleIDTable, i64 0, i64 8), align 8, !dbg !47, !invariant.load !5 - store i64 %rubyId_a, i64* %positional_table, align 8, !dbg !47 - %63 = tail call noalias nonnull i8* @ruby_xmalloc2(i64 noundef 1, i64 noundef 8) #12, !dbg !47 - %64 = bitcast i64* %positional_table to i8*, !dbg !47 - call void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture nonnull writeonly align 1 %63, i8* nocapture noundef nonnull readonly align 8 dereferenceable(8) %64, i64 noundef 8, i1 noundef false) #11, !dbg !47 - %65 = getelementptr inbounds i8, i8* %52, i64 32, !dbg !47 - %66 = bitcast i8* %65 to i8**, !dbg !47 - store i8* %63, i8** %66, align 8, !dbg !47, !tbaa !46 - tail call void @sorbet_vm_define_method(i64 %9, i8* noundef getelementptr inbounds ([134 x i8], [134 x i8]* @sorbet_moduleStringTable, i64 0, i64 0), i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)* noundef @"func_Object#8delegate", i8* nonnull %52, %struct.rb_iseq_struct* %stackFrame28, i1 noundef zeroext false) #11, !dbg !47 - store i64* getelementptr inbounds ([23 x i64], [23 x i64]* @iseqEncodedArray, i64 0, i64 22), i64** %8, align 8, !dbg !47, !tbaa !20 - %67 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 1, !dbg !19 - %68 = load i64*, i64** %67, align 8, !dbg !19 - store i64 %selfRaw, i64* %68, align 8, !dbg !19, !tbaa !6 - %69 = getelementptr inbounds i64, i64* %68, i64 1, !dbg !19 - store i64 %initializedObject, i64* %69, align 8, !dbg !19, !tbaa !6 - %70 = getelementptr inbounds i64, i64* %69, i64 1, !dbg !19 - store i64* %70, i64** %67, align 8, !dbg !19 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_delegate, i64 0), !dbg !19 - ret void -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_.13"(i64 %realpath) unnamed_addr #6 { -entryInitializers: - %"rubyId_" = load i64, i64* getelementptr inbounds ([11 x i64], [11 x i64]* @sorbet_moduleIDTable, i64 0, i64 4), align 8, !invariant.load !5 - %"rubyStr_" = load i64, i64* getelementptr inbounds ([7 x i64], [7 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 4), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/custom_plus.rb" = load i64, i64* getelementptr inbounds ([7 x i64], [7 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_", i64 %"rubyId_", i64 %"rubyStr_test/testdata/compiler/custom_plus.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 0, i32 noundef 6, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 4) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - ret void -} - -; Function Attrs: ssp -define void @Init_custom_plus() local_unnamed_addr #6 { -entry: - %realpath = tail call i64 @sorbet_readRealpath() - tail call fastcc void @sorbet_globalConstructors(i64 %realpath) - %0 = load %struct.rb_vm_struct*, %struct.rb_vm_struct** @ruby_current_vm_ptr, align 8, !tbaa !20 - %1 = getelementptr inbounds %struct.rb_vm_struct, %struct.rb_vm_struct* %0, i64 0, i32 18 - %2 = load i64, i64* %1, align 8, !tbaa !48 - %3 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !20 - %4 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %3, i64 0, i32 2 - %5 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %4, align 8, !tbaa !29 - tail call fastcc void @"func_.13"(i64 %2, %struct.rb_control_frame_struct* %5) #11 - ret void -} - -; Function Attrs: nounwind sspreq uwtable -define internal i64 @"func_A#1+"(i32 %argc, i64* nocapture nofree readnone %argArray, i64 returned %selfRaw, %struct.rb_control_frame_struct* nocapture nonnull writeonly align 8 dereferenceable(8) %cfp, i8* nocapture nofree readnone %calling, i8* nocapture nofree readnone %callData) #5 !dbg !56 { -functionEntryInitializers: - %0 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 0 - store i64* getelementptr inbounds ([23 x i64], [23 x i64]* @iseqEncodedArray, i64 0, i64 7), i64** %0, align 8, !tbaa !20 - %tooManyArgs = icmp ugt i32 %argc, 1, !dbg !57 - %tooFewArgs = icmp ult i32 %argc, 1, !dbg !57 - %or.cond = or i1 %tooManyArgs, %tooFewArgs, !dbg !57 - br i1 %or.cond, label %argCountFailBlock, label %fillRequiredArgs, !dbg !57, !prof !23 - -argCountFailBlock: ; preds = %functionEntryInitializers - tail call void @sorbet_raiseArity(i32 %argc, i32 noundef 1, i32 noundef 1) #10, !dbg !57 - unreachable, !dbg !57 - -fillRequiredArgs: ; preds = %functionEntryInitializers - store i64* getelementptr inbounds ([23 x i64], [23 x i64]* @iseqEncodedArray, i64 0, i64 8), i64** %0, align 8, !dbg !58, !tbaa !20 - ret i64 %selfRaw -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_A#1+"(i64 %realpath) unnamed_addr #6 { -entryInitializers: - %"rubyId_+" = load i64, i64* getelementptr inbounds ([11 x i64], [11 x i64]* @sorbet_moduleIDTable, i64 0, i64 1), align 8, !invariant.load !5 - %"rubyStr_+" = load i64, i64* getelementptr inbounds ([7 x i64], [7 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 5), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/custom_plus.rb" = load i64, i64* getelementptr inbounds ([7 x i64], [7 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_+", i64 %"rubyId_+", i64 %"rubyStr_test/testdata/compiler/custom_plus.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 1, i32 noundef 7, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 0) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_A#1+", align 8 - ret void -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_A.13"(i64 %realpath) unnamed_addr #6 { -entryInitializers: - %"rubyId_" = load i64, i64* getelementptr inbounds ([11 x i64], [11 x i64]* @sorbet_moduleIDTable, i64 0, i64 9), align 8, !invariant.load !5 - %"rubyStr_" = load i64, i64* getelementptr inbounds ([7 x i64], [7 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 6), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/custom_plus.rb" = load i64, i64* getelementptr inbounds ([7 x i64], [7 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_", i64 %"rubyId_", i64 %"rubyStr_test/testdata/compiler/custom_plus.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 3, i32 noundef 6, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 4) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_A.13", align 8 - ret void -} - -; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly -declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #7 - -; Function Attrs: nofree nosync nounwind willreturn -declare void @llvm.assume(i1 noundef) #8 - -; Function Attrs: ssp -define linkonce void @const_recompute_A() local_unnamed_addr #6 { - %1 = tail call i64 @sorbet_getConstant(i8* getelementptr inbounds ([134 x i8], [134 x i8]* @sorbet_moduleStringTable, i64 0, i64 82), i64 1) - store i64 %1, i64* @guarded_const_A, align 8 - %2 = load i64, i64* @ruby_vm_global_constant_state, align 8, !tbaa !39 - store i64 %2, i64* @guard_epoch_A, align 8 - ret void -} - -attributes #0 = { noreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #1 = { "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #2 = { argmemonly nofree nosync nounwind willreturn } -attributes #3 = { allocsize(0,1) "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #4 = { nounwind ssp uwtable "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #5 = { nounwind sspreq uwtable } -attributes #6 = { ssp } -attributes #7 = { argmemonly nofree nosync nounwind willreturn writeonly } -attributes #8 = { nofree nosync nounwind willreturn } -attributes #9 = { noreturn nounwind } -attributes #10 = { noreturn } -attributes #11 = { nounwind } -attributes #12 = { nounwind allocsize(0,1) } - -!llvm.module.flags = !{!0, !1, !2} -!llvm.dbg.cu = !{!3} - -!0 = !{i32 2, !"Debug Info Version", i32 3} -!1 = !{i32 4, !"cf-protection-return", i32 1} -!2 = !{i32 4, !"cf-protection-branch", i32 1} -!3 = distinct !DICompileUnit(language: DW_LANG_C, file: !4, producer: "Sorbet LLVM", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !5) -!4 = !DIFile(filename: "test/testdata/compiler/custom_plus.rb", directory: ".") -!5 = !{} -!6 = !{!7, !7, i64 0} -!7 = !{!"long", !8, i64 0} -!8 = !{!"omnipotent char", !9, i64 0} -!9 = !{!"Simple C/C++ TBAA"} -!10 = !DILocation(line: 15, column: 6, scope: !11) -!11 = distinct !DISubprogram(name: "Object#delegate", linkageName: "func_Object#8delegate", scope: null, file: !4, line: 14, type: !12, scopeLine: 14, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!12 = !DISubroutineType(types: !13) -!13 = !{!14} -!14 = !DIBasicType(name: "VALUE", size: 64, encoding: DW_ATE_signed) -!15 = !DILocation(line: 16, column: 5, scope: !11) -!16 = !DILocation(line: 18, column: 5, scope: !11) -!17 = !DILocation(line: 12, column: 5, scope: !18) -!18 = distinct !DISubprogram(name: ".", linkageName: "func_.13", scope: null, file: !4, line: 6, type: !12, scopeLine: 6, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!19 = !DILocation(line: 22, column: 1, scope: !18) -!20 = !{!21, !21, i64 0} -!21 = !{!"any pointer", !8, i64 0} -!22 = !DILocation(line: 14, column: 1, scope: !11) -!23 = !{!"branch_weights", i32 4001, i32 4000000} -!24 = !DILocation(line: 14, column: 14, scope: !11) -!25 = !DILocation(line: 0, scope: !11) -!26 = !DILocation(line: 7, column: 3, scope: !27, inlinedAt: !28) -!27 = distinct !DISubprogram(name: "A.", linkageName: "func_A.13L79", scope: null, file: !4, line: 6, type: !12, scopeLine: 6, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!28 = distinct !DILocation(line: 6, column: 1, scope: !18) -!29 = !{!30, !21, i64 16} -!30 = !{!"rb_execution_context_struct", !21, i64 0, !7, i64 8, !21, i64 16, !21, i64 24, !21, i64 32, !31, i64 40, !31, i64 44, !21, i64 48, !21, i64 56, !21, i64 64, !7, i64 72, !7, i64 80, !21, i64 88, !7, i64 96, !21, i64 104, !21, i64 112, !7, i64 120, !7, i64 128, !8, i64 136, !8, i64 137, !7, i64 144, !32, i64 152} -!31 = !{!"int", !8, i64 0} -!32 = !{!"", !21, i64 0, !21, i64 8, !7, i64 16, !8, i64 24} -!33 = !{!34, !21, i64 16} -!34 = !{!"rb_control_frame_struct", !21, i64 0, !21, i64 8, !21, i64 16, !7, i64 24, !21, i64 32, !21, i64 40, !21, i64 48} -!35 = !{!34, !21, i64 32} -!36 = !DILocation(line: 0, scope: !18) -!37 = !DILocation(line: 6, column: 1, scope: !18) -!38 = !DILocation(line: 0, scope: !27, inlinedAt: !28) -!39 = !{!40, !40, i64 0} -!40 = !{!"long long", !8, i64 0} -!41 = !{!"branch_weights", i32 1, i32 10000} -!42 = !{!43, !31, i64 8} -!43 = !{!"rb_sorbet_param_struct", !44, i64 0, !31, i64 4, !31, i64 8, !31, i64 12, !31, i64 16, !31, i64 20, !31, i64 24, !31, i64 28, !21, i64 32, !31, i64 40, !31, i64 44, !31, i64 48, !31, i64 52, !21, i64 56} -!44 = !{!"", !31, i64 0, !31, i64 0, !31, i64 0, !31, i64 0, !31, i64 0, !31, i64 0, !31, i64 0, !31, i64 0, !31, i64 1, !31, i64 1} -!45 = !{!43, !31, i64 4} -!46 = !{!43, !21, i64 32} -!47 = !DILocation(line: 14, column: 1, scope: !18) -!48 = !{!49, !7, i64 400} -!49 = !{!"rb_vm_struct", !7, i64 0, !50, i64 8, !21, i64 192, !21, i64 200, !21, i64 208, !40, i64 216, !8, i64 224, !51, i64 264, !51, i64 280, !51, i64 296, !51, i64 312, !7, i64 328, !31, i64 336, !31, i64 340, !31, i64 344, !31, i64 344, !31, i64 344, !31, i64 344, !31, i64 348, !7, i64 352, !8, i64 360, !7, i64 400, !7, i64 408, !7, i64 416, !7, i64 424, !7, i64 432, !7, i64 440, !7, i64 448, !21, i64 456, !21, i64 464, !53, i64 472, !54, i64 992, !21, i64 1016, !21, i64 1024, !31, i64 1032, !31, i64 1036, !51, i64 1040, !8, i64 1056, !7, i64 1096, !7, i64 1104, !7, i64 1112, !7, i64 1120, !7, i64 1128, !31, i64 1136, !21, i64 1144, !21, i64 1152, !21, i64 1160, !21, i64 1168, !21, i64 1176, !21, i64 1184, !31, i64 1192, !55, i64 1200, !8, i64 1232} -!50 = !{!"rb_global_vm_lock_struct", !21, i64 0, !8, i64 8, !51, i64 48, !21, i64 64, !31, i64 72, !8, i64 80, !8, i64 128, !31, i64 176, !31, i64 180} -!51 = !{!"list_head", !52, i64 0} -!52 = !{!"list_node", !21, i64 0, !21, i64 8} -!53 = !{!"", !8, i64 0} -!54 = !{!"rb_hook_list_struct", !21, i64 0, !31, i64 8, !31, i64 12, !31, i64 16} -!55 = !{!"", !7, i64 0, !7, i64 8, !7, i64 16, !7, i64 24} -!56 = distinct !DISubprogram(name: "A#+", linkageName: "func_A#1+", scope: null, file: !4, line: 7, type: !12, scopeLine: 7, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!57 = !DILocation(line: 7, column: 3, scope: !56) -!58 = !DILocation(line: 0, scope: !56) diff --git a/test/testdata/compiler/default_args_effects.rb b/test/testdata/compiler/default_args_effects.rb index eb0f56d26f..3090a0f648 100644 --- a/test/testdata/compiler/default_args_effects.rb +++ b/test/testdata/compiler/default_args_effects.rb @@ -4,7 +4,7 @@ # The current implementation for default arguments doesn't consider `a` as being # captured by the defaultArgs function that's extracted to initialize `b`. The -# result is that the compiled version does not update the vaule of `a` when +# result is that the compiled version does not update the value of `a` when # providing the default value for `b`. def test(a, b=(a = 'test')) puts "a = #{a}" diff --git a/test/testdata/compiler/direct_call.opt.ll.exp b/test/testdata/compiler/direct_call.opt.ll.exp deleted file mode 100644 index dc1b8223b5..0000000000 --- a/test/testdata/compiler/direct_call.opt.ll.exp +++ /dev/null @@ -1,356 +0,0 @@ -; ModuleID = 'payload' -source_filename = "llvm-link" -target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } -%struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.__pthread_cond_s } -%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } -%union.anon = type { i64 } -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } -%struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } -%struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } -%struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } -%struct.anon.0 = type { i16, [2 x i8] } -%struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } -%struct.rb_iseq_location_struct = type { i64, i64, i64, i64, i32, %struct.rb_code_location_struct } -%struct.rb_code_location_struct = type { %struct.rb_code_position_struct, %struct.rb_code_position_struct } -%struct.rb_code_position_struct = type { i32, i32 } -%struct.iseq_insn_info = type { %struct.iseq_insn_info_entry*, i32*, i32, %struct.succ_index_table* } -%struct.iseq_insn_info_entry = type opaque -%struct.succ_index_table = type opaque -%struct.iseq_catch_table = type opaque -%union.iseq_inline_storage_entry = type { %struct.iseq_inline_cache_entry } -%struct.iseq_inline_cache_entry = type { i64, %struct.rb_cref_struct*, i64 } -%struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } -%struct.rb_scope_visi_struct = type { i8, [3 x i8] } -%struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } -%struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } -%union.anon.13 = type { %struct.rb_method_cfunc_struct } -%struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%union.anon.15 = type { i32 } -%struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.16 = type { i64, i64, i64, i64* } -%union.anon.17 = type { %struct.anon.18 } -%struct.anon.18 = type { i64, i32 } -%struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } -%struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } -%struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } -%struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } -%struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } -%struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } -%struct.__sigset_t = type { [16 x i64] } -%struct.rb_calling_info = type { i64, i64, i32, i32 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } -%union.anon.9 = type { %union.pthread_cond_t } -%struct.rb_unblock_callback = type { void (i8*)*, i8* } -%struct.rb_mutex_struct = type opaque -%struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.10 = type { %struct.anon.11 } -%struct.anon.11 = type { i64, i64, i32 } -%struct.rb_fiber_struct = type opaque -%struct.anon.5 = type { [65 x i64] } -%struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } -%struct.rb_event_hook_struct = type opaque -%struct.rb_postponed_job_struct = type opaque -%struct.list_head = type { %struct.list_node } -%union.pthread_mutex_t = type { %struct.__pthread_mutex_s } -%struct.__pthread_mutex_s = type { i32, i32, i32, i32, i32, i16, i16, %struct.__pthread_internal_list } -%struct.__pthread_internal_list = type { %struct.__pthread_internal_list*, %struct.__pthread_internal_list* } -%struct.rb_objspace = type opaque -%struct.rb_at_exit_list = type { void (%struct.rb_vm_struct*)*, %struct.rb_at_exit_list* } -%struct.st_table = type { i8, i8, i8, i32, %struct.st_hash_type*, i64, i64*, i64, i64, %struct.st_table_entry* } -%struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } -%struct.st_table_entry = type opaque -%struct.rb_builtin_function = type opaque -%struct.anon.6 = type { i64, i64, i64, i64 } -%struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } -%struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } -%struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } -%struct.rb_call_info_with_kwarg = type { %struct.rb_call_info, %struct.rb_call_info_kw_arg* } -%struct.rb_call_info_kw_arg = type { i32, [1 x i64] } - -@ruby_current_vm_ptr = external local_unnamed_addr global %struct.rb_vm_struct*, align 8 -@ruby_current_execution_context_ptr = external local_unnamed_addr global %struct.rb_execution_context_struct*, align 8 -@rb_eRuntimeError = external local_unnamed_addr global i64, align 8 -@.str.9 = private unnamed_addr constant [95 x i8] c"sorbet_getBuildSCMRevision: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@.str.10 = private unnamed_addr constant [93 x i8] c"sorbet_getIsReleaseBuild: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@"stackFramePrecomputed_func_Object#3foo" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@iseqEncodedArray = internal global [13 x i64] zeroinitializer -@fileLineNumberInfo = internal global %struct.SorbetLineNumberInfo zeroinitializer -@"stackFramePrecomputed_func_Object#3bar" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@ic_foo = internal global %struct.FunctionInlineCache zeroinitializer -@"stackFramePrecomputed_func_.13" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@ic_bar = internal global %struct.FunctionInlineCache zeroinitializer -@ic_puts = internal global %struct.FunctionInlineCache zeroinitializer -@sorbet_moduleStringTable = internal unnamed_addr constant [89 x i8] c"foo\00test/testdata/compiler/direct_call.rb\00bar\00\00normal\00Object\00puts\00master\00", align 1 -@sorbet_moduleIDTable = internal unnamed_addr global [5 x i64] zeroinitializer, align 8 -@sorbet_moduleIDDescriptors = internal unnamed_addr constant [5 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 3 }, %struct.rb_code_position_struct { i32 42, i32 3 }, %struct.rb_code_position_struct { i32 46, i32 16 }, %struct.rb_code_position_struct { i32 63, i32 6 }, %struct.rb_code_position_struct { i32 77, i32 4 }], align 8 -@sorbet_moduleRubyStringTable = internal unnamed_addr global [4 x i64] zeroinitializer, align 8 -@sorbet_moduleRubyStringDescriptors = internal unnamed_addr constant [4 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 3 }, %struct.rb_code_position_struct { i32 4, i32 37 }, %struct.rb_code_position_struct { i32 42, i32 3 }, %struct.rb_code_position_struct { i32 46, i32 16 }], align 8 -@rb_cObject = external local_unnamed_addr constant i64 - -; Function Attrs: noreturn -declare void @sorbet_raiseArity(i32, i32, i32) local_unnamed_addr #0 - -declare %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64, i64, i64, i64, %struct.rb_iseq_struct*, i32, i32, %struct.SorbetLineNumberInfo*, i64*, i32, i32) local_unnamed_addr #1 - -declare void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo*, i64*, i32) local_unnamed_addr #1 - -declare i64 @sorbet_readRealpath() local_unnamed_addr #1 - -declare void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) local_unnamed_addr #1 - -declare i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache*, i64) local_unnamed_addr #1 - -declare void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_iseq_struct*) local_unnamed_addr #1 - -declare void @sorbet_vm_define_method(i64, i8*, i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)*, i8*, %struct.rb_iseq_struct*, i1 zeroext) local_unnamed_addr #1 - -declare void @sorbet_vm_intern_ids(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #1 - -declare void @sorbet_vm_init_string_table(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #1 - -; Function Attrs: noreturn -declare void @rb_raise(i64, i8*, ...) local_unnamed_addr #0 - -; Function Attrs: allocsize(0,1) -declare noalias nonnull i8* @ruby_xcalloc(i64, i64) local_unnamed_addr #2 - -; Function Attrs: nounwind ssp uwtable -define weak i32 @sorbet_getIsReleaseBuild() local_unnamed_addr #3 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([93 x i8], [93 x i8]* @.str.10, i64 0, i64 0)) #8 - unreachable -} - -; Function Attrs: nounwind ssp uwtable -define weak i8* @sorbet_getBuildSCMRevision() local_unnamed_addr #3 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([95 x i8], [95 x i8]* @.str.9, i64 0, i64 0)) #8 - unreachable -} - -define internal fastcc void @sorbet_globalConstructors(i64 %realpath) unnamed_addr { -allocRubyIds: - tail call void @sorbet_vm_intern_ids(i64* noundef getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleIDTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([5 x %struct.rb_code_position_struct], [5 x %struct.rb_code_position_struct]* @sorbet_moduleIDDescriptors, i32 0, i32 0), i32 noundef 5, i8* noundef getelementptr inbounds ([89 x i8], [89 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_vm_init_string_table(i64* noundef getelementptr inbounds ([4 x i64], [4 x i64]* @sorbet_moduleRubyStringTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([4 x %struct.rb_code_position_struct], [4 x %struct.rb_code_position_struct]* @sorbet_moduleRubyStringDescriptors, i32 0, i32 0), i32 noundef 4, i8* noundef getelementptr inbounds ([89 x i8], [89 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef getelementptr inbounds ([13 x i64], [13 x i64]* @iseqEncodedArray, i32 0, i32 0), i32 noundef 13) - tail call fastcc void @"Constr_stackFramePrecomputed_func_Object#3foo"(i64 %realpath) - tail call fastcc void @"Constr_stackFramePrecomputed_func_Object#3bar"(i64 %realpath) - %rubyId_foo = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleIDTable, i64 0, i64 0), align 8, !dbg !10, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_foo, i64 %rubyId_foo, i32 noundef 20, i32 noundef 0, i32 noundef 0), !dbg !10 - tail call fastcc void @"Constr_stackFramePrecomputed_func_.13"(i64 %realpath) - %rubyId_bar = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleIDTable, i64 0, i64 1), align 8, !dbg !15, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_bar, i64 %rubyId_bar, i32 noundef 20, i32 noundef 0, i32 noundef 0), !dbg !15 - %rubyId_puts = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleIDTable, i64 0, i64 4), align 8, !dbg !17, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts, i64 %rubyId_puts, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !17 - ret void -} - -; Function Attrs: nounwind sspreq uwtable -define internal noundef i64 @"func_Object#3foo"(i32 %argc, i64* nocapture nofree readnone %argArray, i64 %selfRaw, %struct.rb_control_frame_struct* nocapture nonnull writeonly align 8 dereferenceable(8) %cfp, i8* nocapture nofree readnone %calling, i8* nocapture nofree readnone %callData) #4 !dbg !18 { -functionEntryInitializers: - %0 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 0 - store i64* getelementptr inbounds ([13 x i64], [13 x i64]* @iseqEncodedArray, i64 0, i64 4), i64** %0, align 8, !tbaa !19 - %tooManyArgs = icmp ugt i32 %argc, 0, !dbg !21 - br i1 %tooManyArgs, label %argCountFailBlock, label %fillRequiredArgs, !dbg !21, !prof !22 - -argCountFailBlock: ; preds = %functionEntryInitializers - tail call void @sorbet_raiseArity(i32 %argc, i32 noundef 0, i32 noundef 0) #9, !dbg !21 - unreachable, !dbg !21 - -fillRequiredArgs: ; preds = %functionEntryInitializers - store i64* getelementptr inbounds ([13 x i64], [13 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %0, align 8, !dbg !23, !tbaa !19 - ret i64 3 -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_Object#3foo"(i64 %realpath) unnamed_addr #5 { -entryInitializers: - %rubyId_foo = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleIDTable, i64 0, i64 0), align 8, !invariant.load !5 - %rubyStr_foo = load i64, i64* getelementptr inbounds ([4 x i64], [4 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 0), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/direct_call.rb" = load i64, i64* getelementptr inbounds ([4 x i64], [4 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %rubyStr_foo, i64 %rubyId_foo, i64 %"rubyStr_test/testdata/compiler/direct_call.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 1, i32 noundef 4, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 0) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Object#3foo", align 8 - ret void -} - -; Function Attrs: nounwind sspreq uwtable -define internal i64 @"func_Object#3bar"(i32 %argc, i64* nocapture nofree readnone %argArray, i64 %selfRaw, %struct.rb_control_frame_struct* nonnull align 8 dereferenceable(8) %cfp, i8* nocapture nofree readnone %calling, i8* nocapture nofree readnone %callData) #4 !dbg !11 { -functionEntryInitializers: - %0 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 0 - store i64* getelementptr inbounds ([13 x i64], [13 x i64]* @iseqEncodedArray, i64 0, i64 8), i64** %0, align 8, !tbaa !19 - %tooManyArgs = icmp ugt i32 %argc, 0, !dbg !24 - br i1 %tooManyArgs, label %argCountFailBlock, label %fillRequiredArgs, !dbg !24, !prof !22 - -argCountFailBlock: ; preds = %functionEntryInitializers - tail call void @sorbet_raiseArity(i32 %argc, i32 noundef 0, i32 noundef 0) #9, !dbg !24 - unreachable, !dbg !24 - -fillRequiredArgs: ; preds = %functionEntryInitializers - store i64* getelementptr inbounds ([13 x i64], [13 x i64]* @iseqEncodedArray, i64 0, i64 9), i64** %0, align 8, !dbg !25, !tbaa !19 - %1 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 1, !dbg !10 - %2 = load i64*, i64** %1, align 8, !dbg !10 - store i64 %selfRaw, i64* %2, align 8, !dbg !10, !tbaa !6 - %3 = getelementptr inbounds i64, i64* %2, i64 1, !dbg !10 - store i64* %3, i64** %1, align 8, !dbg !10 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_foo, i64 0), !dbg !10 - ret i64 %send -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_Object#3bar"(i64 %realpath) unnamed_addr #5 { -entryInitializers: - %rubyId_bar = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleIDTable, i64 0, i64 1), align 8, !invariant.load !5 - %rubyStr_bar = load i64, i64* getelementptr inbounds ([4 x i64], [4 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 2), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/direct_call.rb" = load i64, i64* getelementptr inbounds ([4 x i64], [4 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %rubyStr_bar, i64 %rubyId_bar, i64 %"rubyStr_test/testdata/compiler/direct_call.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 1, i32 noundef 8, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 1) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Object#3bar", align 8 - ret void -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_.13"(i64 %realpath) unnamed_addr #5 { -entryInitializers: - %"rubyId_" = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleIDTable, i64 0, i64 2), align 8, !invariant.load !5 - %"rubyStr_" = load i64, i64* getelementptr inbounds ([4 x i64], [4 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 3), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/direct_call.rb" = load i64, i64* getelementptr inbounds ([4 x i64], [4 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_", i64 %"rubyId_", i64 %"rubyStr_test/testdata/compiler/direct_call.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 0, i32 noundef 4, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 4) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - ret void -} - -; Function Attrs: sspreq -define void @Init_direct_call() local_unnamed_addr #6 { -entry: - %realpath = tail call i64 @sorbet_readRealpath() - tail call fastcc void @sorbet_globalConstructors(i64 %realpath) - %0 = load %struct.rb_vm_struct*, %struct.rb_vm_struct** @ruby_current_vm_ptr, align 8, !tbaa !19 - %1 = getelementptr inbounds %struct.rb_vm_struct, %struct.rb_vm_struct* %0, i64 0, i32 18 - %2 = load i64, i64* %1, align 8, !tbaa !26 - %3 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !19 - %4 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %3, i64 0, i32 2 - %5 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %4, align 8, !tbaa !36 - %stackFrame.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - %6 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame.i, %struct.rb_iseq_struct** %6, align 8, !tbaa !39 - %7 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 4 - %8 = load i64*, i64** %7, align 8, !tbaa !41 - %9 = load i64, i64* %8, align 8, !tbaa !6 - %10 = and i64 %9, -33 - store i64 %10, i64* %8, align 8, !tbaa !6 - tail call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %3, %struct.rb_control_frame_struct* %5, %struct.rb_iseq_struct* %stackFrame.i) #10 - %11 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 0 - store i64* getelementptr inbounds ([13 x i64], [13 x i64]* @iseqEncodedArray, i64 0, i64 4), i64** %11, align 8, !dbg !42, !tbaa !19 - %12 = load i64, i64* @rb_cObject, align 8, !dbg !43 - %stackFrame21.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Object#3foo", align 8, !dbg !43 - %13 = tail call noalias nonnull i8* @ruby_xcalloc(i64 noundef 1, i64 noundef 64) #11, !dbg !43 - %14 = bitcast i8* %13 to i16*, !dbg !43 - %15 = load i16, i16* %14, align 8, !dbg !43 - %16 = and i16 %15, -384, !dbg !43 - store i16 %16, i16* %14, align 8, !dbg !43 - %17 = getelementptr inbounds i8, i8* %13, i64 4, !dbg !43 - tail call void @llvm.memset.p0i8.i64(i8* nonnull align 4 %17, i8 0, i64 28, i1 false) #10, !dbg !43 - tail call void @sorbet_vm_define_method(i64 %12, i8* noundef getelementptr inbounds ([89 x i8], [89 x i8]* @sorbet_moduleStringTable, i64 0, i64 0), i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)* noundef @"func_Object#3foo", i8* nonnull %13, %struct.rb_iseq_struct* %stackFrame21.i, i1 noundef zeroext false) #10, !dbg !43 - store i64* getelementptr inbounds ([13 x i64], [13 x i64]* @iseqEncodedArray, i64 0, i64 8), i64** %11, align 8, !dbg !43, !tbaa !19 - %stackFrame30.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Object#3bar", align 8, !dbg !44 - %18 = tail call noalias nonnull i8* @ruby_xcalloc(i64 noundef 1, i64 noundef 64) #11, !dbg !44 - %19 = bitcast i8* %18 to i16*, !dbg !44 - %20 = load i16, i16* %19, align 8, !dbg !44 - %21 = and i16 %20, -384, !dbg !44 - store i16 %21, i16* %19, align 8, !dbg !44 - %22 = getelementptr inbounds i8, i8* %18, i64 4, !dbg !44 - tail call void @llvm.memset.p0i8.i64(i8* nonnull align 4 %22, i8 0, i64 28, i1 false) #10, !dbg !44 - tail call void @sorbet_vm_define_method(i64 %12, i8* getelementptr inbounds ([89 x i8], [89 x i8]* @sorbet_moduleStringTable, i64 0, i64 42), i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)* noundef @"func_Object#3bar", i8* nonnull %18, %struct.rb_iseq_struct* %stackFrame30.i, i1 noundef zeroext false) #10, !dbg !44 - store i64* getelementptr inbounds ([13 x i64], [13 x i64]* @iseqEncodedArray, i64 0, i64 12), i64** %11, align 8, !dbg !44, !tbaa !19 - %23 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !15 - %24 = load i64*, i64** %23, align 8, !dbg !15 - store i64 %2, i64* %24, align 8, !dbg !15, !tbaa !6 - %25 = getelementptr inbounds i64, i64* %24, i64 1, !dbg !15 - store i64* %25, i64** %23, align 8, !dbg !15 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_bar, i64 0), !dbg !15 - %26 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !17 - %27 = load i64*, i64** %26, align 8, !dbg !17 - store i64 %2, i64* %27, align 8, !dbg !17, !tbaa !6 - %28 = getelementptr inbounds i64, i64* %27, i64 1, !dbg !17 - store i64 %send, i64* %28, align 8, !dbg !17, !tbaa !6 - %29 = getelementptr inbounds i64, i64* %28, i64 1, !dbg !17 - store i64* %29, i64** %26, align 8, !dbg !17 - %send2 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts, i64 0), !dbg !17 - ret void -} - -; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly -declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #7 - -attributes #0 = { noreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #1 = { "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #2 = { allocsize(0,1) "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #3 = { nounwind ssp uwtable "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #4 = { nounwind sspreq uwtable } -attributes #5 = { ssp } -attributes #6 = { sspreq } -attributes #7 = { argmemonly nofree nosync nounwind willreturn writeonly } -attributes #8 = { noreturn nounwind } -attributes #9 = { noreturn } -attributes #10 = { nounwind } -attributes #11 = { nounwind allocsize(0,1) } - -!llvm.module.flags = !{!0, !1, !2} -!llvm.dbg.cu = !{!3} - -!0 = !{i32 2, !"Debug Info Version", i32 3} -!1 = !{i32 4, !"cf-protection-return", i32 1} -!2 = !{i32 4, !"cf-protection-branch", i32 1} -!3 = distinct !DICompileUnit(language: DW_LANG_C, file: !4, producer: "Sorbet LLVM", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !5) -!4 = !DIFile(filename: "test/testdata/compiler/direct_call.rb", directory: ".") -!5 = !{} -!6 = !{!7, !7, i64 0} -!7 = !{!"long", !8, i64 0} -!8 = !{!"omnipotent char", !9, i64 0} -!9 = !{!"Simple C/C++ TBAA"} -!10 = !DILocation(line: 9, column: 3, scope: !11) -!11 = distinct !DISubprogram(name: "Object#bar", linkageName: "func_Object#3bar", scope: null, file: !4, line: 8, type: !12, scopeLine: 8, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!12 = !DISubroutineType(types: !13) -!13 = !{!14} -!14 = !DIBasicType(name: "VALUE", size: 64, encoding: DW_ATE_signed) -!15 = !DILocation(line: 12, column: 6, scope: !16) -!16 = distinct !DISubprogram(name: ".", linkageName: "func_.13", scope: null, file: !4, line: 4, type: !12, scopeLine: 4, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!17 = !DILocation(line: 12, column: 1, scope: !16) -!18 = distinct !DISubprogram(name: "Object#foo", linkageName: "func_Object#3foo", scope: null, file: !4, line: 4, type: !12, scopeLine: 4, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!19 = !{!20, !20, i64 0} -!20 = !{!"any pointer", !8, i64 0} -!21 = !DILocation(line: 4, column: 1, scope: !18) -!22 = !{!"branch_weights", i32 1, i32 2000} -!23 = !DILocation(line: 0, scope: !18) -!24 = !DILocation(line: 8, column: 1, scope: !11) -!25 = !DILocation(line: 0, scope: !11) -!26 = !{!27, !7, i64 400} -!27 = !{!"rb_vm_struct", !7, i64 0, !28, i64 8, !20, i64 192, !20, i64 200, !20, i64 208, !32, i64 216, !8, i64 224, !29, i64 264, !29, i64 280, !29, i64 296, !29, i64 312, !7, i64 328, !31, i64 336, !31, i64 340, !31, i64 344, !31, i64 344, !31, i64 344, !31, i64 344, !31, i64 348, !7, i64 352, !8, i64 360, !7, i64 400, !7, i64 408, !7, i64 416, !7, i64 424, !7, i64 432, !7, i64 440, !7, i64 448, !20, i64 456, !20, i64 464, !33, i64 472, !34, i64 992, !20, i64 1016, !20, i64 1024, !31, i64 1032, !31, i64 1036, !29, i64 1040, !8, i64 1056, !7, i64 1096, !7, i64 1104, !7, i64 1112, !7, i64 1120, !7, i64 1128, !31, i64 1136, !20, i64 1144, !20, i64 1152, !20, i64 1160, !20, i64 1168, !20, i64 1176, !20, i64 1184, !31, i64 1192, !35, i64 1200, !8, i64 1232} -!28 = !{!"rb_global_vm_lock_struct", !20, i64 0, !8, i64 8, !29, i64 48, !20, i64 64, !31, i64 72, !8, i64 80, !8, i64 128, !31, i64 176, !31, i64 180} -!29 = !{!"list_head", !30, i64 0} -!30 = !{!"list_node", !20, i64 0, !20, i64 8} -!31 = !{!"int", !8, i64 0} -!32 = !{!"long long", !8, i64 0} -!33 = !{!"", !8, i64 0} -!34 = !{!"rb_hook_list_struct", !20, i64 0, !31, i64 8, !31, i64 12, !31, i64 16} -!35 = !{!"", !7, i64 0, !7, i64 8, !7, i64 16, !7, i64 24} -!36 = !{!37, !20, i64 16} -!37 = !{!"rb_execution_context_struct", !20, i64 0, !7, i64 8, !20, i64 16, !20, i64 24, !20, i64 32, !31, i64 40, !31, i64 44, !20, i64 48, !20, i64 56, !20, i64 64, !7, i64 72, !7, i64 80, !20, i64 88, !7, i64 96, !20, i64 104, !20, i64 112, !7, i64 120, !7, i64 128, !8, i64 136, !8, i64 137, !7, i64 144, !38, i64 152} -!38 = !{!"", !20, i64 0, !20, i64 8, !7, i64 16, !8, i64 24} -!39 = !{!40, !20, i64 16} -!40 = !{!"rb_control_frame_struct", !20, i64 0, !20, i64 8, !20, i64 16, !7, i64 24, !20, i64 32, !20, i64 40, !20, i64 48} -!41 = !{!40, !20, i64 32} -!42 = !DILocation(line: 0, scope: !16) -!43 = !DILocation(line: 4, column: 1, scope: !16) -!44 = !DILocation(line: 8, column: 1, scope: !16) diff --git a/test/testdata/compiler/disabled/array_each_override.rb b/test/testdata/compiler/disabled/array_each_override.rb deleted file mode 100644 index 17dcfa5032..0000000000 --- a/test/testdata/compiler/disabled/array_each_override.rb +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -class Arraylike < Array - def initialize - @called = T.let(false, T::Boolean) - super - end - - def each(&blk) - @called = true - super - end -end - -a = Arraylike["default"] - -a.each do |x| - p x -end - -p a.instance_variable_get(:@called) diff --git a/test/testdata/compiler/disabled/array_each_with_block_each_override.rb b/test/testdata/compiler/disabled/array_each_with_block_each_override.rb deleted file mode 100644 index c89c81ced0..0000000000 --- a/test/testdata/compiler/disabled/array_each_with_block_each_override.rb +++ /dev/null @@ -1,27 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -# Array#each_with_object is actually implemented with Array#each under the hood, -# and our intrinsics should respect that. - -class Arraylike < Array - def initialize - @called = T.let(false, T::Boolean) - super - end - - def each(&blk) - @called = true - super - end -end - -a = Arraylike["default"] - -a.each_with_object(1) do |x, obj| - p x - p obj -end - -p a.instance_variable_get(:@called) diff --git a/test/testdata/compiler/disabled/array_each_with_block_override.rb b/test/testdata/compiler/disabled/array_each_with_block_override.rb deleted file mode 100644 index 214ec32618..0000000000 --- a/test/testdata/compiler/disabled/array_each_with_block_override.rb +++ /dev/null @@ -1,27 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -# Array#each_with_object is actually implemented with Array#each under the hood, -# and our intrinsics should respect that. - -class Arraylike < Array - def initialize - @called = T.let(false, T::Boolean) - super - end - - def each_with_object(obj, &blk) - @called = true - super - end -end - -a = Arraylike["default"] - -a.each_with_object(1) do |x, obj| - p x - p obj -end - -p a.instance_variable_get(:@called) diff --git a/test/testdata/compiler/disabled/array_subtype.rb b/test/testdata/compiler/disabled/array_subtype.rb deleted file mode 100644 index 5ac09fc103..0000000000 --- a/test/testdata/compiler/disabled/array_subtype.rb +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -extend T::Sig - -# [] is not the only method that we need to be careful about, but it's a -# reasonable starting place. - -class Arraylike < Array - def [](key) - "overridden" - end -end - -sig {params(array: T::Array[T.untyped]).returns(T.untyped)} -def ref(array) - array[0] -end - -a = Arraylike["default"] - -p ref(a) diff --git a/test/testdata/compiler/disabled/assignment_order.rb b/test/testdata/compiler/disabled/assignment_order.rb deleted file mode 100644 index 349881241f..0000000000 --- a/test/testdata/compiler/disabled/assignment_order.rb +++ /dev/null @@ -1,16 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -module Space - def self.[](key) - :frob - end - - def self.[]=(key, value) - :blob - end -end - -p Space[:foo] -x = Space[:bar] = 2 -p x diff --git a/test/testdata/compiler/disabled/cast_fail_message.rb b/test/testdata/compiler/disabled/cast_fail_message.rb deleted file mode 100644 index 959b1b1562..0000000000 --- a/test/testdata/compiler/disabled/cast_fail_message.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -T.cast(nil, T::Array[Integer]) - -puts 'No cast failed!' diff --git a/test/testdata/compiler/disabled/const_set_special_constant.rb b/test/testdata/compiler/disabled/const_set_special_constant.rb deleted file mode 100644 index 83b082f8b6..0000000000 --- a/test/testdata/compiler/disabled/const_set_special_constant.rb +++ /dev/null @@ -1,22 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -# When a Ruby constant is available via a global symbol, the compiler replaces -# the VM constant search with a read from that constant. -# -# That means that some const_set calls can overwrite special symbols, but the -# compiler will still behave as if they had not been overwritten. -# -# We consider this ~fine, and if we were to change something it would be to -# raise loudly at the `const_set` line, not to make the const_get resilient to -# being rewritten (specifically for these "known symbols"). - -verbose = $VERBOSE -$VERBOSE = nil -Object.const_set(:Integer, nil) -Object.const_set(:Module, nil) -$VERBOSE = verbose - -p Integer -p Module diff --git a/test/testdata/compiler/disabled/define_singleton_method.rb b/test/testdata/compiler/disabled/define_singleton_method.rb deleted file mode 100644 index 88189fb85c..0000000000 --- a/test/testdata/compiler/disabled/define_singleton_method.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -class Currency - def self.supported - ['usd', 'jpy', 'cad'] - end - - supported.each do |currency| - define_singleton_method(currency) {currency} - end -end - -p T.unsafe(Currency).usd -p T.unsafe(Currency).jpy -p T.unsafe(Currency).cad diff --git a/test/testdata/compiler/disabled/defines_behavior__1.rb b/test/testdata/compiler/disabled/defines_behavior__1.rb deleted file mode 100644 index 087e5fd282..0000000000 --- a/test/testdata/compiler/disabled/defines_behavior__1.rb +++ /dev/null @@ -1,8 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -class A - def foo - end -end -require_relative './defines_behavior__2' diff --git a/test/testdata/compiler/disabled/defines_behavior__2.rb b/test/testdata/compiler/disabled/defines_behavior__2.rb deleted file mode 100644 index 2876bd7d6f..0000000000 --- a/test/testdata/compiler/disabled/defines_behavior__2.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -class A - def bar - end -end diff --git a/test/testdata/compiler/disabled/deprecated_enum_in_sig.rb b/test/testdata/compiler/disabled/deprecated_enum_in_sig.rb deleted file mode 100644 index d3239034bd..0000000000 --- a/test/testdata/compiler/disabled/deprecated_enum_in_sig.rb +++ /dev/null @@ -1,20 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -extend T::Sig - -ENUM_VALUES = ["value1", "value2"] - -sig {params(e: T.deprecated_enum(ENUM_VALUES)).void} -def f(e) - p e -end - -begin - f("bad value") -rescue => e - p e.class -else - p "should have gotten an error" -end diff --git a/test/testdata/compiler/disabled/forward_to_attr_reader.rb b/test/testdata/compiler/disabled/forward_to_attr_reader.rb deleted file mode 100644 index b58249cad6..0000000000 --- a/test/testdata/compiler/disabled/forward_to_attr_reader.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -# This is a test case designed to test some subtleties around argument handling -# and VM_METHOD_TYPE_IVAR methods. -# -# It fails (at time of writing) for reasons unrelated to the change to -# introduce a special case for VM_METHOD_TYPE_IVAR methods, so it must be -# disabled. - -class A - attr_reader :foo - - def wrap_foo(...) - this = T.unsafe(self) - this.foo(...) - end - - def initialize - @foo = 541 - end -end - -puts A.new.wrap_foo diff --git a/test/testdata/compiler/disabled/hash_each_with_block_each_override.rb b/test/testdata/compiler/disabled/hash_each_with_block_each_override.rb deleted file mode 100644 index ddc1dd75b5..0000000000 --- a/test/testdata/compiler/disabled/hash_each_with_block_each_override.rb +++ /dev/null @@ -1,30 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -# Hash#each_with_object is actually implemented via #each, so we should either -# use the overridden version in our intrinsic or we should disable use of our -# intrinsic entirely. Both options would make this test pass. - -extend T::Sig - -class Hashlike < Hash - def initialize - @called = T.let(false, T::Boolean) - super - end - - def each(&blk) - @called = true - super - end -end - -h = T.unsafe(Hashlike)["key", "default"] - -result = h.each_with_block([]) do |kv, array| - array << kv -end - -p result -p h.instance_variable_get(:@called) diff --git a/test/testdata/compiler/disabled/hash_each_with_block_override.rb b/test/testdata/compiler/disabled/hash_each_with_block_override.rb deleted file mode 100644 index ec4cd6806f..0000000000 --- a/test/testdata/compiler/disabled/hash_each_with_block_override.rb +++ /dev/null @@ -1,28 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -# Our intrinsic for Hash#each_with_block ought to take into account possible overrides. - -extend T::Sig - -class Hashlike < Hash - def initialize - @called = T.let(false, T::Boolean) - super - end - - def each_with_block(obj, &blk) - @called = true - super - end -end - -h = T.unsafe(Hashlike)["key", "default"] - -result = h.each_with_block([]) do |kv, array| - array << kv -end - -p result -p h.instance_variable_get(:@called) diff --git a/test/testdata/compiler/disabled/hash_subtype.rb b/test/testdata/compiler/disabled/hash_subtype.rb deleted file mode 100644 index 318b2fef26..0000000000 --- a/test/testdata/compiler/disabled/hash_subtype.rb +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -extend T::Sig - -# [] is not the only method that we need to be careful about, but it's a -# reasonable starting place. - -class Hashlike < Hash - def [](key) - "overridden" - end -end - -sig {params(hash: T::Hash[T.untyped, T.untyped]).returns(T.untyped)} -def ref(hash) - hash["key"] -end - -h = T.unsafe(Hashlike)["key", "default"] - -p ref(h) diff --git a/test/testdata/compiler/disabled/instance_eval_def.rb b/test/testdata/compiler/disabled/instance_eval_def.rb deleted file mode 100644 index bb93d8ec14..0000000000 --- a/test/testdata/compiler/disabled/instance_eval_def.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -module M - include Kernel - - def self.included(klass) - puts "hello from M.included" - - klass.instance_eval do - puts "hello from the instance_eval block" - puts "self is: #{self}" - puts "let's def f" - - def f - puts "hello from f" - end - - puts "done deffing f" - end - end -end - -module Z - include M -end - -T.unsafe(Z).f diff --git a/test/testdata/compiler/disabled/internal_method_defintions.rb b/test/testdata/compiler/disabled/internal_method_defintions.rb deleted file mode 100644 index cdd46adddd..0000000000 --- a/test/testdata/compiler/disabled/internal_method_defintions.rb +++ /dev/null @@ -1,15 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -def internal - p "outside" -end - -def wrapper - def internal - p "inside" - end -end - -p internal diff --git a/test/testdata/compiler/disabled/kwnil_parameters.rb b/test/testdata/compiler/disabled/kwnil_parameters.rb deleted file mode 100644 index f0df6b0daf..0000000000 --- a/test/testdata/compiler/disabled/kwnil_parameters.rb +++ /dev/null @@ -1,8 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -def foo(a, **nil) -end - -puts method(:foo).parameters diff --git a/test/testdata/compiler/disabled/kwsplat_argument_capture.rb b/test/testdata/compiler/disabled/kwsplat_argument_capture.rb deleted file mode 100644 index 0ea86d66b3..0000000000 --- a/test/testdata/compiler/disabled/kwsplat_argument_capture.rb +++ /dev/null @@ -1,57 +0,0 @@ -# compiled: true -# typed: true -# frozen_string_literal: true - -# When parsing keyword arguments that have a kwsplat present, the code that we -# emit will iteratively remove parsed arguments from the kwsplat hash to -# determine what's left over. The effect of this is that if something else had a -# reference to the hash that was used for the keyword args, they will see that -# hash as having fewer elements after the compiled function was called. - -class Util - extend T::Sig - - sig {params(x: Integer, args: T.untyped).void} - def self.main(x:, **args) - puts x - puts args - end -end - -class Inspector - - def self.setup - return unless @old.nil? - - @old = Util.method(:main) - old = @old - - Util.define_singleton_method(:main) do |args| - Inspector.instance_variable_set(:@args, args) - old.call(args) - end - end - - def self.teardown - # In the compiled version, this puts will differ as the `@args` hash has - # been mutated by the compiled function. - puts "args = #{@args}" - Util.define_singleton_method(:main, @old) - end - -end - -class Test - def self.test_stub - args = {msg: 'hi', x: 20} - Util.main(x: 10, **args) - end -end - -# Matches ruby -Test.test_stub - -# Doesn't match ruby, as the `@args` hash has been mutated -Inspector.setup -Test.test_stub -Inspector.teardown diff --git a/test/testdata/compiler/disabled/mangle.rb b/test/testdata/compiler/disabled/mangle.rb deleted file mode 100644 index 69d5c6853e..0000000000 --- a/test/testdata/compiler/disabled/mangle.rb +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -def foo - puts "foo" -end -foo - -def foo(a) - puts "foo2" -end -foo(2) diff --git a/test/testdata/compiler/disabled/module_function.rb b/test/testdata/compiler/disabled/module_function.rb deleted file mode 100644 index f82f6d4b3b..0000000000 --- a/test/testdata/compiler/disabled/module_function.rb +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -module Helpers - extend T::Sig - - sig {returns(Float).checked(:never)} - module_function def sample_rate - 0.01 - end -end - -p Helpers.sample_rate diff --git a/test/testdata/compiler/disabled/multiple_definitions.rb b/test/testdata/compiler/disabled/multiple_definitions.rb deleted file mode 100644 index 797bbc2ebd..0000000000 --- a/test/testdata/compiler/disabled/multiple_definitions.rb +++ /dev/null @@ -1,16 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -def multiple - 1 -end - -# Make sure we actually do something with the first definition. -p multiple - -def multiple - 2 -end - -p multiple diff --git a/test/testdata/compiler/disabled/nil_p_no_basic_object.rb b/test/testdata/compiler/disabled/nil_p_no_basic_object.rb deleted file mode 100644 index 5492274b53..0000000000 --- a/test/testdata/compiler/disabled/nil_p_no_basic_object.rb +++ /dev/null @@ -1,50 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -extend T::Sig - -module MyModule - def calls_nil_p - T.unsafe(self).nil? - end -end - -class MyObject - include ::MyModule -end - -class MyBasicObject < BasicObject - include ::MyModule -end - -def soft_no_method_error(&blk) - begin - yield - rescue NoMethodError => exn - puts exn.message - end -end - -nil_ = T.unsafe(nil) -obj = T.unsafe(Object.new) -basic_obj = T.unsafe(BasicObject.new) - -soft_no_method_error {p nil_.nil?} -soft_no_method_error {p obj.nil?} -soft_no_method_error {p basic_obj.nil?} - -soft_no_method_error {p MyObject.new.nil?} -soft_no_method_error {p MyObject.new.calls_nil_p} - -soft_no_method_error {p T.unsafe(MyBasicObject.new).nil?} -soft_no_method_error {p MyBasicObject.new.calls_nil_p} - -T::Sig::WithoutRuntime.sig {params(x: MyModule).void} -def takes_my_module(x) - p T.unsafe(x).nil? -end - -soft_no_method_error {takes_my_module(MyObject.new)} -soft_no_method_error {takes_my_module(MyBasicObject.new)} - diff --git a/test/testdata/compiler/disabled/parsed_wrong.rb b/test/testdata/compiler/disabled/parsed_wrong.rb deleted file mode 100644 index 2ad14a57a0..0000000000 --- a/test/testdata/compiler/disabled/parsed_wrong.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -a = < {5} -end - -a1 = A.new -p a1.foo diff --git a/test/testdata/compiler/disabled/regex_backref.rb b/test/testdata/compiler/disabled/regex_backref.rb deleted file mode 100644 index 51dca7a08e..0000000000 --- a/test/testdata/compiler/disabled/regex_backref.rb +++ /dev/null @@ -1,20 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -PATTERN = /(\S+):(\d+)/ - -PATTERN.match('this is foo.rb:123 in a sentence') do - p $~ - - p $& - - p $` - - p $' - - p $1 - p $2 - - p $+ -end diff --git a/test/testdata/compiler/disabled/regexp_error_too_eager.rb b/test/testdata/compiler/disabled/regexp_error_too_eager.rb deleted file mode 100644 index 3724d4d6c6..0000000000 --- a/test/testdata/compiler/disabled/regexp_error_too_eager.rb +++ /dev/null @@ -1,8 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -puts "hi" -if T.unsafe(false) - Regexp.new("?") -end diff --git a/test/testdata/compiler/disabled/rescue_splat.rb b/test/testdata/compiler/disabled/rescue_splat.rb deleted file mode 100644 index 749647004c..0000000000 --- a/test/testdata/compiler/disabled/rescue_splat.rb +++ /dev/null @@ -1,32 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -def rescue_ok?(cases, &blk) - begin - blk.call - rescue *cases - :rescued_from_splat - rescue - :rescued_from_catch_all - else - :no_exception - end -end - -cases = [ArgumentError, TypeError] -p (rescue_ok?(cases) do - T.unsafe(3.0) + "aha" - end) -p (rescue_ok?(cases) do - T.unsafe(1).to_a - end) -p (rescue_ok?(cases) do - raise "Uncaught" - end) -p (rescue_ok?(cases) do - T.unsafe([1, 2, 3]).first(4, 5) - end) -p (rescue_ok?(cases) do - 2+2 - end) diff --git a/test/testdata/compiler/disabled/root_weirdness.rb b/test/testdata/compiler/disabled/root_weirdness.rb deleted file mode 100644 index d3cb783710..0000000000 --- a/test/testdata/compiler/disabled/root_weirdness.rb +++ /dev/null @@ -1,40 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -p self - -def on_object - puts 'on_object' -end - -def self.on_class_of_object - puts 'self.on_class_of_object' -end - -class << self - def on_root_singleton - puts 'on_root_singleton' - end -end - -on_object -on_class_of_object -on_root_singleton - -begin - 1.on_object -rescue => e - p e -end - -begin - 1.on_class_of_object -rescue => e - p e -end - -begin - 1.on_root_singleton -rescue => e - p e -end diff --git a/test/testdata/compiler/disabled/ruby_struct.rb b/test/testdata/compiler/disabled/ruby_struct.rb deleted file mode 100644 index 5450fa7e38..0000000000 --- a/test/testdata/compiler/disabled/ruby_struct.rb +++ /dev/null @@ -1,7 +0,0 @@ -# typed: true -# compiled: true -# frozen_string_literal: true - -Thing = Struct.new(:foo, :bar, :baz) - -Thing.new(1357,'hi',false) diff --git a/test/testdata/compiler/disabled/splat_with_extra_kwargs.rb b/test/testdata/compiler/disabled/splat_with_extra_kwargs.rb deleted file mode 100644 index fc4de797a9..0000000000 --- a/test/testdata/compiler/disabled/splat_with_extra_kwargs.rb +++ /dev/null @@ -1,13 +0,0 @@ -# compiled: true -# typed: true -# frozen_string_literal: true - -def f(c: 3) - p (c) -end - -begin - f(*[{c: 10, d: 8}]) -rescue ArgumentError => e - p e.message -end diff --git a/test/testdata/compiler/disabled/stdlib_rbi__1.rb b/test/testdata/compiler/disabled/stdlib_rbi__1.rb deleted file mode 100644 index c8a09a61de..0000000000 --- a/test/testdata/compiler/disabled/stdlib_rbi__1.rb +++ /dev/null @@ -1,4 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -puts("hello world") diff --git a/test/testdata/compiler/disabled/stdlib_rbi__2.rbi b/test/testdata/compiler/disabled/stdlib_rbi__2.rbi deleted file mode 100644 index 50675e3caa..0000000000 --- a/test/testdata/compiler/disabled/stdlib_rbi__2.rbi +++ /dev/null @@ -1,6 +0,0 @@ -# frozen_string_literal: true -# typed: true -class Array - include ::JSON::Ext::Generator::GeneratorMethods::Array - def to_h; end -end diff --git a/test/testdata/compiler/disabled/string_subtype.rb b/test/testdata/compiler/disabled/string_subtype.rb deleted file mode 100644 index 1675237b90..0000000000 --- a/test/testdata/compiler/disabled/string_subtype.rb +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -extend T::Sig - -# length is not the only method that we need to be careful about, but it's a -# reasonable starting place. - -class Stringlike < String - def length - 28 - end -end - -sig {params(string: String).returns(Integer)} -def ref(string) - string.length -end - -s = Stringlike.new("default") - -p ref(s) diff --git a/test/testdata/compiler/disabled/tracepoint_call.rb b/test/testdata/compiler/disabled/tracepoint_call.rb deleted file mode 100644 index e1892263eb..0000000000 --- a/test/testdata/compiler/disabled/tracepoint_call.rb +++ /dev/null @@ -1,19 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -def foo -end - -count = 0 -TracePoint.trace(:call) do |t| - if t.method_id == :foo - count += 1 - end -end - -p count -foo -p count -foo -p count diff --git a/test/testdata/compiler/disabled/typed_kwsplat_arg.rb b/test/testdata/compiler/disabled/typed_kwsplat_arg.rb deleted file mode 100644 index 718212d2f4..0000000000 --- a/test/testdata/compiler/disabled/typed_kwsplat_arg.rb +++ /dev/null @@ -1,22 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -extend T::Sig - -# The runtime asserts that the values of the kwsplat hash are the declared type. -# The compiler, at the time this testcase was written, doesn't do that. -sig {params(msg: String, kwargs: Integer).void} -def foo(msg, **kwargs) - p msg -end - -begin - foo("shouldn't execute the body", a: 1, b: 2, c: T.unsafe('nope')) -rescue - p "got exception as expected" -else - p "we shouldn't be here" -end - -foo("should execute the body", a: 1, b: 2, c: 3) diff --git a/test/testdata/compiler/disabled/unresolved_constant.rb b/test/testdata/compiler/disabled/unresolved_constant.rb deleted file mode 100644 index 701175de50..0000000000 --- a/test/testdata/compiler/disabled/unresolved_constant.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -# dmitry: I don't think we'll support this any time soon -def foo - A -end diff --git a/test/testdata/compiler/disabled/validate_exp_tests/test.opt.ll.exp b/test/testdata/compiler/disabled/validate_exp_tests/test.opt.ll.exp deleted file mode 100644 index 17ae5952bb..0000000000 --- a/test/testdata/compiler/disabled/validate_exp_tests/test.opt.ll.exp +++ /dev/null @@ -1,234 +0,0 @@ -; ModuleID = 'payload' -source_filename = "llvm-link" -target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" -target triple = "x86_64-unknown-linux_gnu" - -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } -%struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.__pthread_cond_s } -%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } -%union.anon = type { i64 } -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } -%struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } -%struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } -%struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } -%struct.anon.0 = type { i16, [2 x i8] } -%struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } -%struct.rb_iseq_location_struct = type { i64, i64, i64, i64, i32, %struct.rb_code_location_struct } -%struct.rb_code_location_struct = type { %struct.rb_code_position_struct, %struct.rb_code_position_struct } -%struct.rb_code_position_struct = type { i32, i32 } -%struct.iseq_insn_info = type { %struct.iseq_insn_info_entry*, i32*, i32, %struct.succ_index_table* } -%struct.iseq_insn_info_entry = type opaque -%struct.succ_index_table = type opaque -%struct.iseq_catch_table = type opaque -%union.iseq_inline_storage_entry = type { %struct.iseq_inline_cache_entry } -%struct.iseq_inline_cache_entry = type { i64, %struct.rb_cref_struct*, i64 } -%struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } -%struct.rb_scope_visi_struct = type { i8, [3 x i8] } -%struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } -%struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } -%union.anon.13 = type { %struct.rb_method_cfunc_struct } -%struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%union.anon.15 = type { i32 } -%struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.16 = type { i64, i64, i64, i64* } -%union.anon.17 = type { %struct.anon.18 } -%struct.anon.18 = type { i64, i32 } -%struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } -%struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } -%struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } -%struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } -%struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } -%struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } -%struct.__sigset_t = type { [16 x i64] } -%struct.rb_calling_info = type { i64, i64, i32, i32 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } -%union.anon.9 = type { %union.pthread_cond_t } -%struct.rb_unblock_callback = type { void (i8*)*, i8* } -%struct.rb_mutex_struct = type opaque -%struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.10 = type { %struct.anon.11 } -%struct.anon.11 = type { i64, i64, i32 } -%struct.rb_fiber_struct = type opaque -%struct.anon.5 = type { [65 x i64] } -%struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } -%struct.rb_event_hook_struct = type opaque -%struct.rb_postponed_job_struct = type opaque -%struct.list_head = type { %struct.list_node } -%union.pthread_mutex_t = type { %struct.__pthread_mutex_s } -%struct.__pthread_mutex_s = type { i32, i32, i32, i32, i32, i16, i16, %struct.__pthread_internal_list } -%struct.__pthread_internal_list = type { %struct.__pthread_internal_list*, %struct.__pthread_internal_list* } -%struct.rb_objspace = type opaque -%struct.rb_at_exit_list = type { void (%struct.rb_vm_struct*)*, %struct.rb_at_exit_list* } -%struct.st_table = type { i8, i8, i8, i32, %struct.st_hash_type*, i64, i64*, i64, i64, %struct.st_table_entry* } -%struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } -%struct.st_table_entry = type opaque -%struct.rb_builtin_function = type opaque -%struct.anon.6 = type { i64, i64, i64, i64 } -%struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } -%struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } -%struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } -%struct.rb_call_info_with_kwarg = type { %struct.rb_call_info, %struct.rb_call_info_kw_arg* } -%struct.rb_call_info_kw_arg = type { i32, [1 x i64] } - -@ruby_current_vm_ptr = external local_unnamed_addr global %struct.rb_vm_struct*, align 8 -@ruby_current_execution_context_ptr = external local_unnamed_addr global %struct.rb_execution_context_struct*, align 8 -@rb_eRuntimeError = external local_unnamed_addr global i64, align 8 -@.str.9 = private unnamed_addr constant [95 x i8] c"sorbet_getBuildSCMRevision: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@.str.10 = private unnamed_addr constant [93 x i8] c"sorbet_getIsReleaseBuild: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@"stackFramePrecomputed_func_.$152" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"rubyIdPrecomputed_" = internal unnamed_addr global i64 0, align 8 -@"str_" = private unnamed_addr constant [17 x i8] c"\00", align 1 -@"rubyStrFrozen_" = internal unnamed_addr global i64 0, align 8 -@"rubyStrFrozen_test/testdata/compiler/disabled/validate_exp_tests/test.rb" = internal unnamed_addr global i64 0, align 8 -@"str_test/testdata/compiler/disabled/validate_exp_tests/test.rb" = private unnamed_addr constant [59 x i8] c"test/testdata/compiler/disabled/validate_exp_tests/test.rb\00", align 1 -@iseqEncodedArray = internal global [6 x i64] zeroinitializer -@fileLineNumberInfo = internal global %struct.SorbetLineNumberInfo zeroinitializer -@"rubyStrFrozen_Hello!" = internal unnamed_addr global i64 0, align 8 -@"str_Hello!" = private unnamed_addr constant [7 x i8] c"Hello!\00", align 1 -@ic_puts = internal global %struct.FunctionInlineCache zeroinitializer -@rubyIdPrecomputed_puts = internal unnamed_addr global i64 0, align 8 -@str_puts = private unnamed_addr constant [5 x i8] c"puts\00", align 1 - -declare %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64, i64, i64, i64, %struct.rb_iseq_struct*, i32, i32, %struct.SorbetLineNumberInfo*, i64*, i32, i32) local_unnamed_addr #0 - -declare void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo*, i64*, i32) local_unnamed_addr #0 - -declare i64 @sorbet_readRealpath() local_unnamed_addr #0 - -declare void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache*, i64, i32, i32, i32, i64*) local_unnamed_addr #0 - -declare i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache*, i64) local_unnamed_addr #0 - -declare void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_iseq_struct*) local_unnamed_addr #0 - -declare i64 @rb_intern2(i8*, i64) local_unnamed_addr #0 - -declare i64 @rb_fstring_new(i8*, i64) local_unnamed_addr #0 - -declare void @rb_gc_register_mark_object(i64) local_unnamed_addr #0 - -; Function Attrs: noreturn -declare void @rb_raise(i64, i8*, ...) local_unnamed_addr #1 - -; Function Attrs: nounwind ssp uwtable -define weak i32 @sorbet_getIsReleaseBuild() local_unnamed_addr #2 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !4 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([93 x i8], [93 x i8]* @.str.10, i64 0, i64 0)) #4 - unreachable -} - -; Function Attrs: nounwind ssp uwtable -define weak i8* @sorbet_getBuildSCMRevision() local_unnamed_addr #2 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !4 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([95 x i8], [95 x i8]* @.str.9, i64 0, i64 0)) #4 - unreachable -} - -; Function Attrs: sspreq -define void @Init_test() local_unnamed_addr #3 { -entry: - %locals.i.i = alloca i64, i32 0, align 8 - - ; Call a function that doesn't exist to force a error that won't show up as a diff - %realpath = tail call i64 @sorbet_readRealpath_fake() - - %0 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([17 x i8], [17 x i8]* @"str_", i64 0, i64 0), i64 noundef 16) #5 - store i64 %0, i64* @"rubyIdPrecomputed_", align 8 - %1 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([5 x i8], [5 x i8]* @str_puts, i64 0, i64 0), i64 noundef 4) #5 - store i64 %1, i64* @rubyIdPrecomputed_puts, align 8 - %2 = tail call i64 @rb_fstring_new(i8* noundef getelementptr inbounds ([17 x i8], [17 x i8]* @"str_", i64 0, i64 0), i64 noundef 16) #5 - tail call void @rb_gc_register_mark_object(i64 %2) #5 - store i64 %2, i64* @"rubyStrFrozen_", align 8 - %3 = tail call i64 @rb_fstring_new(i8* noundef getelementptr inbounds ([59 x i8], [59 x i8]* @"str_test/testdata/compiler/disabled/validate_exp_tests/test.rb", i64 0, i64 0), i64 noundef 58) #5 - tail call void @rb_gc_register_mark_object(i64 %3) #5 - store i64 %3, i64* @"rubyStrFrozen_test/testdata/compiler/disabled/validate_exp_tests/test.rb", align 8 - tail call void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef getelementptr inbounds ([6 x i64], [6 x i64]* @iseqEncodedArray, i32 0, i32 0), i32 noundef 6) - %"rubyId_.i.i" = load i64, i64* @"rubyIdPrecomputed_", align 8 - %"rubyStr_.i.i" = load i64, i64* @"rubyStrFrozen_", align 8 - %"rubyStr_test/testdata/compiler/disabled/validate_exp_tests/test.rb.i.i" = load i64, i64* @"rubyStrFrozen_test/testdata/compiler/disabled/validate_exp_tests/test.rb", align 8 - %4 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_.i.i", i64 %"rubyId_.i.i", i64 %"rubyStr_test/testdata/compiler/disabled/validate_exp_tests/test.rb.i.i", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 0, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals.i.i, i32 noundef 0, i32 noundef 2) - store %struct.rb_iseq_struct* %4, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.$152", align 8 - %5 = call i64 @rb_fstring_new(i8* noundef getelementptr inbounds ([7 x i8], [7 x i8]* @"str_Hello!", i64 0, i64 0), i64 noundef 6) #5 - call void @rb_gc_register_mark_object(i64 %5) #5 - store i64 %5, i64* @"rubyStrFrozen_Hello!", align 8 - %rubyId_puts.i = load i64, i64* @rubyIdPrecomputed_puts, align 8, !dbg !8 - call void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts, i64 %rubyId_puts.i, i32 noundef 20, i32 noundef 1, i32 noundef 0, i64* noundef null), !dbg !8 - %6 = load %struct.rb_vm_struct*, %struct.rb_vm_struct** @ruby_current_vm_ptr, align 8, !tbaa !13 - %7 = getelementptr inbounds %struct.rb_vm_struct, %struct.rb_vm_struct* %6, i64 0, i32 18 - %8 = load i64, i64* %7, align 8, !tbaa !15 - %9 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !13 - %10 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %9, i64 0, i32 2 - %11 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %10, align 8, !tbaa !25 - %stackFrame.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.$152", align 8 - %12 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %11, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame.i, %struct.rb_iseq_struct** %12, align 8, !tbaa !28 - %13 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %11, i64 0, i32 4 - %14 = load i64*, i64** %13, align 8, !tbaa !30 - %15 = load i64, i64* %14, align 8, !tbaa !4 - %16 = and i64 %15, -33 - store i64 %16, i64* %14, align 8, !tbaa !4 - call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %9, %struct.rb_control_frame_struct* %11, %struct.rb_iseq_struct* %stackFrame.i) #5 - %17 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %11, i64 0, i32 0 - store i64* getelementptr inbounds ([6 x i64], [6 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %17, align 8, !dbg !31, !tbaa !13 - %"rubyStr_Hello!.i" = load i64, i64* @"rubyStrFrozen_Hello!", align 8, !dbg !32 - %18 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %11, i64 0, i32 1, !dbg !8 - %19 = load i64*, i64** %18, align 8, !dbg !8 - store i64 %8, i64* %19, align 8, !dbg !8, !tbaa !4 - %20 = getelementptr inbounds i64, i64* %19, i64 1, !dbg !8 - store i64 %"rubyStr_Hello!.i", i64* %20, align 8, !dbg !8, !tbaa !4 - %21 = getelementptr inbounds i64, i64* %20, i64 1, !dbg !8 - store i64* %21, i64** %18, align 8, !dbg !8 - %send = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts, i64 0), !dbg !8 - ret void -} - -attributes #0 = { "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #1 = { noreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #2 = { nounwind ssp uwtable "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #3 = { sspreq } -attributes #4 = { noreturn nounwind } -attributes #5 = { nounwind } - -!llvm.module.flags = !{!0} -!llvm.dbg.cu = !{!1} - -!0 = !{i32 2, !"Debug Info Version", i32 3} -!1 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "Sorbet LLVM", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !3) -!2 = !DIFile(filename: "test/testdata/compiler/disabled/validate_exp_tests/test.rb", directory: ".") -!3 = !{} -!4 = !{!5, !5, i64 0} -!5 = !{!"long", !6, i64 0} -!6 = !{!"omnipotent char", !7, i64 0} -!7 = !{!"Simple C/C++ TBAA"} -!8 = !DILocation(line: 5, column: 1, scope: !9) -!9 = distinct !DISubprogram(name: ".", linkageName: "func_.$152", scope: null, file: !2, line: 5, type: !10, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !1, retainedNodes: !3) -!10 = !DISubroutineType(types: !11) -!11 = !{!12} -!12 = !DIBasicType(name: "VALUE", size: 64, encoding: DW_ATE_signed) -!13 = !{!14, !14, i64 0} -!14 = !{!"any pointer", !6, i64 0} -!15 = !{!16, !5, i64 400} -!16 = !{!"rb_vm_struct", !5, i64 0, !17, i64 8, !14, i64 192, !14, i64 200, !14, i64 208, !21, i64 216, !6, i64 224, !18, i64 264, !18, i64 280, !18, i64 296, !18, i64 312, !5, i64 328, !20, i64 336, !20, i64 340, !20, i64 344, !20, i64 344, !20, i64 344, !20, i64 344, !20, i64 348, !5, i64 352, !6, i64 360, !5, i64 400, !5, i64 408, !5, i64 416, !5, i64 424, !5, i64 432, !5, i64 440, !5, i64 448, !14, i64 456, !14, i64 464, !22, i64 472, !23, i64 992, !14, i64 1016, !14, i64 1024, !20, i64 1032, !20, i64 1036, !18, i64 1040, !6, i64 1056, !5, i64 1096, !5, i64 1104, !5, i64 1112, !5, i64 1120, !5, i64 1128, !20, i64 1136, !14, i64 1144, !14, i64 1152, !14, i64 1160, !14, i64 1168, !14, i64 1176, !14, i64 1184, !20, i64 1192, !24, i64 1200, !6, i64 1232} -!17 = !{!"rb_global_vm_lock_struct", !14, i64 0, !6, i64 8, !18, i64 48, !14, i64 64, !20, i64 72, !6, i64 80, !6, i64 128, !20, i64 176, !20, i64 180} -!18 = !{!"list_head", !19, i64 0} -!19 = !{!"list_node", !14, i64 0, !14, i64 8} -!20 = !{!"int", !6, i64 0} -!21 = !{!"long long", !6, i64 0} -!22 = !{!"", !6, i64 0} -!23 = !{!"rb_hook_list_struct", !14, i64 0, !20, i64 8, !20, i64 12, !20, i64 16} -!24 = !{!"", !5, i64 0, !5, i64 8, !5, i64 16, !5, i64 24} -!25 = !{!26, !14, i64 16} -!26 = !{!"rb_execution_context_struct", !14, i64 0, !5, i64 8, !14, i64 16, !14, i64 24, !14, i64 32, !20, i64 40, !20, i64 44, !14, i64 48, !14, i64 56, !14, i64 64, !5, i64 72, !5, i64 80, !14, i64 88, !5, i64 96, !14, i64 104, !14, i64 112, !5, i64 120, !5, i64 128, !6, i64 136, !6, i64 137, !5, i64 144, !27, i64 152} -!27 = !{!"", !14, i64 0, !14, i64 8, !5, i64 16, !6, i64 24} -!28 = !{!29, !14, i64 16} -!29 = !{!"rb_control_frame_struct", !14, i64 0, !14, i64 8, !14, i64 16, !5, i64 24, !14, i64 32, !14, i64 40, !14, i64 48} -!30 = !{!29, !14, i64 32} -!31 = !DILocation(line: 0, scope: !9) -!32 = !DILocation(line: 5, column: 6, scope: !9) diff --git a/test/testdata/compiler/disabled/validate_exp_tests/test.rb b/test/testdata/compiler/disabled/validate_exp_tests/test.rb deleted file mode 100644 index 0c5f652ee3..0000000000 --- a/test/testdata/compiler/disabled/validate_exp_tests/test.rb +++ /dev/null @@ -1,15 +0,0 @@ -# compiled: true -# frozen_string_literal: true -# typed: true - -# This test is expected to fail, as its exp file has an error in it. This error -# will cause the llvm to fail to validate before a diff is performed. Failing in -# this manner wouldn't cause diff-diff.rb to report an error, so this exercises -# that case to ensure that it's failing properly. - -# The oracle test needs to fail as well, but it's not the failure we're -# interested in. We used to compare the output of `Time.new`, but empirical -# evidence says that sometimes the compiled version and the interpreted version -# can print out the same time. Instead, we'll use this fancy compiler intrinsic -# which is guaranteed to produce different output in each scenario. -puts T::Private::Compiler.running_compiled? diff --git a/test/testdata/compiler/disabled/wrapped_interface_from_uncompiled__1.rb b/test/testdata/compiler/disabled/wrapped_interface_from_uncompiled__1.rb deleted file mode 100644 index 2b2070e76d..0000000000 --- a/test/testdata/compiler/disabled/wrapped_interface_from_uncompiled__1.rb +++ /dev/null @@ -1,27 +0,0 @@ -# frozen_string_literal: true -# typed: strict -# compiled: true - -require_relative './wrapped_interface_from_uncompiled__2' - -class Test - extend T::Sig - - sig {params(base: Base).void} - def self.test1(base) ; end - - sig {params(base: Base).returns(Base)} - def self.test2(base) - base - end - - sig {returns(Base)} - def self.test3 - make_wrapped_a - end -end - -a = make_wrapped_a -Test.test1(a) -Test.test2(a) -Test.test3 diff --git a/test/testdata/compiler/disabled/wrapped_interface_from_uncompiled__2.rb b/test/testdata/compiler/disabled/wrapped_interface_from_uncompiled__2.rb deleted file mode 100644 index 6e7cae2510..0000000000 --- a/test/testdata/compiler/disabled/wrapped_interface_from_uncompiled__2.rb +++ /dev/null @@ -1,19 +0,0 @@ -# frozen_string_literal: true -# typed: strict -# compiled: false - -extend T::Sig - -module Base - extend T::Helpers - interface! -end - -class A - include Base -end - -sig {returns(Base)} -def make_wrapped_a - Base.wrap_instance(A.new) -end diff --git a/test/testdata/compiler/ec_tag_required.rb b/test/testdata/compiler/ec_tag_required.rb index b698f10ad0..59cf26e6c2 100644 --- a/test/testdata/compiler/ec_tag_required.rb +++ b/test/testdata/compiler/ec_tag_required.rb @@ -1,11 +1,7 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL -# INITIAL-LABEL: define internal i64 @"func_Object#6func_a" -# INITIAL-NOT: call { i64, i8 } @sorbet_vm_return_from_block_wrapper -# INITIAL{LITERAL}: } def func_a if T.unsafe(35 > 36) return 209 @@ -23,9 +19,6 @@ def func_a end end -# INITIAL-LABEL: define internal i64 @"func_Object#6func_b" -# INITIAL: call { i64, i8 } @sorbet_vm_return_from_block_wrapper -# INITIAL{LITERAL}: } def func_b 1.times { return 33 } end @@ -34,18 +27,12 @@ def justyield yield end -# INITIAL-LABEL: define internal i64 @"func_Object#6func_c" -# INITIAL: call { i64, i8 } @sorbet_vm_return_from_block_wrapper -# INITIAL{LITERAL}: } def func_c if T.unsafe(3+3 > 5) puts (justyield { return 33 }) end end -# INITIAL-LABEL: define internal i64 @"func_Object#6func_d" -# INITIAL-NOT: call { i64, i8 } @sorbet_vm_return_from_block_wrapper -# INITIAL{LITERAL}: } def func_d if T.unsafe(3+3 > 5) puts (justyield { 33 }) diff --git a/test/testdata/compiler/exceptions/basic.opt.ll.exp b/test/testdata/compiler/exceptions/basic.opt.ll.exp deleted file mode 100644 index 33771c3ad3..0000000000 --- a/test/testdata/compiler/exceptions/basic.opt.ll.exp +++ /dev/null @@ -1,807 +0,0 @@ -; ModuleID = 'payload' -source_filename = "llvm-link" -target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } -%struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.__pthread_cond_s } -%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } -%union.anon = type { i64 } -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } -%struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } -%struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } -%struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } -%struct.anon.0 = type { i16, [2 x i8] } -%struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } -%struct.rb_iseq_location_struct = type { i64, i64, i64, i64, i32, %struct.rb_code_location_struct } -%struct.rb_code_location_struct = type { %struct.rb_code_position_struct, %struct.rb_code_position_struct } -%struct.rb_code_position_struct = type { i32, i32 } -%struct.iseq_insn_info = type { %struct.iseq_insn_info_entry*, i32*, i32, %struct.succ_index_table* } -%struct.iseq_insn_info_entry = type opaque -%struct.succ_index_table = type opaque -%struct.iseq_catch_table = type opaque -%union.iseq_inline_storage_entry = type { %struct.iseq_inline_cache_entry } -%struct.iseq_inline_cache_entry = type { i64, %struct.rb_cref_struct*, i64 } -%struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } -%struct.rb_scope_visi_struct = type { i8, [3 x i8] } -%struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } -%struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } -%union.anon.13 = type { %struct.rb_method_cfunc_struct } -%struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%union.anon.15 = type { i32 } -%struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.16 = type { i64, i64, i64, i64* } -%union.anon.17 = type { %struct.anon.18 } -%struct.anon.18 = type { i64, i32 } -%struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } -%struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } -%struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } -%struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } -%struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } -%struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } -%struct.__sigset_t = type { [16 x i64] } -%struct.rb_calling_info = type { i64, i64, i32, i32 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } -%union.anon.9 = type { %union.pthread_cond_t } -%struct.rb_unblock_callback = type { void (i8*)*, i8* } -%struct.rb_mutex_struct = type opaque -%struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.10 = type { %struct.anon.11 } -%struct.anon.11 = type { i64, i64, i32 } -%struct.rb_fiber_struct = type opaque -%struct.anon.5 = type { [65 x i64] } -%struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } -%struct.rb_event_hook_struct = type opaque -%struct.rb_postponed_job_struct = type opaque -%struct.list_head = type { %struct.list_node } -%union.pthread_mutex_t = type { %struct.__pthread_mutex_s } -%struct.__pthread_mutex_s = type { i32, i32, i32, i32, i32, i16, i16, %struct.__pthread_internal_list } -%struct.__pthread_internal_list = type { %struct.__pthread_internal_list*, %struct.__pthread_internal_list* } -%struct.rb_objspace = type opaque -%struct.rb_at_exit_list = type { void (%struct.rb_vm_struct*)*, %struct.rb_at_exit_list* } -%struct.st_table = type { i8, i8, i8, i32, %struct.st_hash_type*, i64, i64*, i64, i64, %struct.st_table_entry* } -%struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } -%struct.st_table_entry = type opaque -%struct.rb_builtin_function = type opaque -%struct.anon.6 = type { i64, i64, i64, i64 } -%struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } -%struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } -%struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } -%struct.rb_call_info_with_kwarg = type { %struct.rb_call_info, %struct.rb_call_info_kw_arg* } -%struct.rb_call_info_kw_arg = type { i32, [1 x i64] } - -@ruby_current_vm_ptr = external local_unnamed_addr global %struct.rb_vm_struct*, align 8 -@ruby_current_execution_context_ptr = external local_unnamed_addr global %struct.rb_execution_context_struct*, align 8 -@ruby_vm_global_constant_state = external local_unnamed_addr global i64, align 8 -@sorbet_getTRetry.retry = internal constant [25 x i8] c"T::Private::Retry::RETRY\00", align 16 -@rb_eRuntimeError = external local_unnamed_addr global i64, align 8 -@.str.9 = private unnamed_addr constant [95 x i8] c"sorbet_getBuildSCMRevision: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@.str.10 = private unnamed_addr constant [93 x i8] c"sorbet_getIsReleaseBuild: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@"stackFramePrecomputed_func_.13" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@iseqEncodedArray = internal global [28 x i64] zeroinitializer -@fileLineNumberInfo = internal global %struct.SorbetLineNumberInfo zeroinitializer -@ic_puts = internal global %struct.FunctionInlineCache zeroinitializer -@ic_test = internal global %struct.FunctionInlineCache zeroinitializer -@ic_puts.1 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_test.2 = internal global %struct.FunctionInlineCache zeroinitializer -@stackFramePrecomputed_func_A.4test = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"stackFramePrecomputed_func_A.4test$block_2" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"stackFramePrecomputed_func_A.4test$block_3" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"" = internal unnamed_addr global i64 0 -@ic_puts.3 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_puts.4 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_raise = internal global %struct.FunctionInlineCache zeroinitializer -@"ic_===" = internal global %struct.FunctionInlineCache zeroinitializer -@ic_puts.5 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_puts.6 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_puts.7 = internal global %struct.FunctionInlineCache zeroinitializer -@"stackFramePrecomputed_func_A.13" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@sorbet_moduleStringTable = internal unnamed_addr constant [278 x i8] c"\00test/testdata/compiler/exceptions/basic.rb\00A\00Object\00=== no-raise ===\00puts\00test\00=== raise ===\00master\00\00x\00\00\00\00rescue in test\00ensure in test\00begin\00foo\00raise\00StandardError\00Module\00===\00else\00ensure\00\00normal\00", align 1 -@sorbet_moduleIDTable = internal unnamed_addr global [14 x i64] zeroinitializer, align 8 -@sorbet_moduleIDDescriptors = internal unnamed_addr constant [14 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 16 }, %struct.rb_code_position_struct { i32 86, i32 4 }, %struct.rb_code_position_struct { i32 91, i32 4 }, %struct.rb_code_position_struct { i32 117, i32 16 }, %struct.rb_code_position_struct { i32 134, i32 1 }, %struct.rb_code_position_struct { i32 136, i32 18 }, %struct.rb_code_position_struct { i32 155, i32 7 }, %struct.rb_code_position_struct { i32 163, i32 14 }, %struct.rb_code_position_struct { i32 178, i32 14 }, %struct.rb_code_position_struct { i32 193, i32 14 }, %struct.rb_code_position_struct { i32 218, i32 5 }, %struct.rb_code_position_struct { i32 245, i32 3 }, %struct.rb_code_position_struct { i32 261, i32 9 }, %struct.rb_code_position_struct { i32 271, i32 6 }], align 8 -@sorbet_moduleRubyStringTable = internal unnamed_addr global [12 x i64] zeroinitializer, align 8 -@sorbet_moduleRubyStringDescriptors = internal unnamed_addr constant [12 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 16 }, %struct.rb_code_position_struct { i32 17, i32 42 }, %struct.rb_code_position_struct { i32 69, i32 16 }, %struct.rb_code_position_struct { i32 96, i32 13 }, %struct.rb_code_position_struct { i32 91, i32 4 }, %struct.rb_code_position_struct { i32 178, i32 14 }, %struct.rb_code_position_struct { i32 193, i32 14 }, %struct.rb_code_position_struct { i32 208, i32 5 }, %struct.rb_code_position_struct { i32 214, i32 3 }, %struct.rb_code_position_struct { i32 249, i32 4 }, %struct.rb_code_position_struct { i32 254, i32 6 }, %struct.rb_code_position_struct { i32 261, i32 9 }], align 8 -@rb_cObject = external local_unnamed_addr constant i64 -@guard_epoch_A = linkonce local_unnamed_addr global i64 0 -@guarded_const_A = linkonce local_unnamed_addr global i64 0 -@rb_eStandardError = external local_unnamed_addr constant i64 -@rb_cModule = external local_unnamed_addr constant i64 - -; Function Attrs: nounwind readnone willreturn -declare i64 @rb_obj_is_kind_of(i64, i64) local_unnamed_addr #0 - -; Function Attrs: noreturn -declare void @sorbet_raiseArity(i32, i32, i32) local_unnamed_addr #1 - -declare %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64, i64, i64, i64, %struct.rb_iseq_struct*, i32, i32, %struct.SorbetLineNumberInfo*, i64*, i32, i32) local_unnamed_addr #2 - -declare void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo*, i64*, i32) local_unnamed_addr #2 - -declare i64 @sorbet_getConstant(i8*, i64) local_unnamed_addr #2 - -declare i64 @sorbet_readRealpath() local_unnamed_addr #2 - -declare %struct.rb_control_frame_struct* @sorbet_pushStaticInitFrame(i64) local_unnamed_addr #2 - -declare void @sorbet_popFrame() local_unnamed_addr #2 - -declare void @sorbet_vm_env_write_slowpath(i64*, i32, i64) local_unnamed_addr #2 - -declare void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) local_unnamed_addr #2 - -declare i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache*, i64) local_unnamed_addr #2 - -declare void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_iseq_struct*) local_unnamed_addr #2 - -declare void @sorbet_setExceptionStackFrame(%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_iseq_struct*) local_unnamed_addr #2 - -declare void @sorbet_vm_define_method(i64, i8*, i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)*, i8*, %struct.rb_iseq_struct*, i1 zeroext) local_unnamed_addr #2 - -declare void @sorbet_vm_intern_ids(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #2 - -declare void @sorbet_vm_init_string_table(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #2 - -declare i64 @sorbet_run_exception_handling(%struct.rb_execution_context_struct*, i64 (i64**, i64, %struct.rb_control_frame_struct*)*, i64**, i64, %struct.rb_control_frame_struct*, i64 (i64**, i64, %struct.rb_control_frame_struct*)*, i64 (i64**, i64, %struct.rb_control_frame_struct*)*, i64 (i64**, i64, %struct.rb_control_frame_struct*)*, i64, i64, i64) local_unnamed_addr #2 - -declare i64 @rb_define_class(i8*, i64) local_unnamed_addr #2 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #3 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #3 - -; Function Attrs: noreturn -declare void @rb_raise(i64, i8*, ...) local_unnamed_addr #1 - -declare i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct*, i32) local_unnamed_addr #2 - -; Function Attrs: allocsize(0,1) -declare noalias nonnull i8* @ruby_xcalloc(i64, i64) local_unnamed_addr #4 - -; Function Attrs: allocsize(0,1) -declare noalias nonnull i8* @ruby_xmalloc2(i64, i64) local_unnamed_addr #4 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) #3 - -; Function Attrs: nounwind ssp uwtable -define weak i32 @sorbet_getIsReleaseBuild() local_unnamed_addr #5 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([93 x i8], [93 x i8]* @.str.10, i64 0, i64 0)) #11 - unreachable -} - -; Function Attrs: nounwind ssp uwtable -define weak i8* @sorbet_getBuildSCMRevision() local_unnamed_addr #5 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([95 x i8], [95 x i8]* @.str.9, i64 0, i64 0)) #11 - unreachable -} - -; Function Attrs: sspreq -define void @Init_basic() local_unnamed_addr #6 { -entry: - %positional_table.i.i = alloca i64, align 8, !dbg !10 - %locals.i14.i = alloca i64, i32 0, align 8 - %locals.i9.i = alloca i64, i32 5, align 8 - %locals.i.i = alloca i64, i32 0, align 8 - %realpath = tail call i64 @sorbet_readRealpath() - tail call void @sorbet_vm_intern_ids(i64* noundef getelementptr inbounds ([14 x i64], [14 x i64]* @sorbet_moduleIDTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([14 x %struct.rb_code_position_struct], [14 x %struct.rb_code_position_struct]* @sorbet_moduleIDDescriptors, i32 0, i32 0), i32 noundef 14, i8* noundef getelementptr inbounds ([278 x i8], [278 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_vm_init_string_table(i64* noundef getelementptr inbounds ([12 x i64], [12 x i64]* @sorbet_moduleRubyStringTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([12 x %struct.rb_code_position_struct], [12 x %struct.rb_code_position_struct]* @sorbet_moduleRubyStringDescriptors, i32 0, i32 0), i32 noundef 12, i8* noundef getelementptr inbounds ([278 x i8], [278 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i32 0, i32 0), i32 noundef 28) - %"rubyId_.i.i" = load i64, i64* getelementptr inbounds ([14 x i64], [14 x i64]* @sorbet_moduleIDTable, i64 0, i64 0), align 8, !invariant.load !5 - %"rubyStr_.i.i" = load i64, i64* getelementptr inbounds ([12 x i64], [12 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 0), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/exceptions/basic.rb.i.i" = load i64, i64* getelementptr inbounds ([12 x i64], [12 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_.i.i", i64 %"rubyId_.i.i", i64 %"rubyStr_test/testdata/compiler/exceptions/basic.rb.i.i", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 0, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals.i.i, i32 noundef 0, i32 noundef 2) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - %rubyId_puts.i = load i64, i64* getelementptr inbounds ([14 x i64], [14 x i64]* @sorbet_moduleIDTable, i64 0, i64 1), align 8, !dbg !17, !invariant.load !5 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts, i64 %rubyId_puts.i, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !17 - %rubyId_test.i = load i64, i64* getelementptr inbounds ([14 x i64], [14 x i64]* @sorbet_moduleIDTable, i64 0, i64 2), align 8, !dbg !18, !invariant.load !5 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_test, i64 %rubyId_test.i, i32 noundef 16, i32 noundef 1, i32 noundef 0), !dbg !18 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts.1, i64 %rubyId_puts.i, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !19 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_test.2, i64 %rubyId_test.i, i32 noundef 16, i32 noundef 1, i32 noundef 0), !dbg !20 - %1 = bitcast i64* %locals.i9.i to i8* - call void @llvm.lifetime.start.p0i8(i64 40, i8* nonnull %1) - %rubyStr_test.i.i = load i64, i64* getelementptr inbounds ([12 x i64], [12 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 4), align 8, !invariant.load !5 - %2 = load <4 x i64>, <4 x i64>* bitcast (i64* getelementptr inbounds ([14 x i64], [14 x i64]* @sorbet_moduleIDTable, i64 0, i64 3) to <4 x i64>*), align 8, !invariant.load !5 - %3 = bitcast i64* %locals.i9.i to <4 x i64>* - store <4 x i64> %2, <4 x i64>* %3, align 8 - %"rubyId_.i.i" = load i64, i64* getelementptr inbounds ([14 x i64], [14 x i64]* @sorbet_moduleIDTable, i64 0, i64 7), align 8, !invariant.load !5 - %4 = getelementptr i64, i64* %locals.i9.i, i32 4 - store i64 %"rubyId_.i.i", i64* %4, align 8 - %5 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %rubyStr_test.i.i, i64 %rubyId_test.i, i64 %"rubyStr_test/testdata/compiler/exceptions/basic.rb.i.i", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 1, i32 noundef 6, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull align 8 %locals.i9.i, i32 noundef 5, i32 noundef 2) - store %struct.rb_iseq_struct* %5, %struct.rb_iseq_struct** @stackFramePrecomputed_func_A.4test, align 8 - call void @llvm.lifetime.end.p0i8(i64 40, i8* nonnull %1) - %"rubyId_rescue in test.i.i" = load i64, i64* getelementptr inbounds ([14 x i64], [14 x i64]* @sorbet_moduleIDTable, i64 0, i64 8), align 8, !invariant.load !5 - %"rubyStr_rescue in test.i.i" = load i64, i64* getelementptr inbounds ([12 x i64], [12 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 5), align 8, !invariant.load !5 - %6 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_rescue in test.i.i", i64 %"rubyId_rescue in test.i.i", i64 %"rubyStr_test/testdata/compiler/exceptions/basic.rb.i.i", i64 %realpath, %struct.rb_iseq_struct* %5, i32 noundef 4, i32 noundef 6, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef null, i32 noundef 0, i32 noundef 2) - store %struct.rb_iseq_struct* %6, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_A.4test$block_2", align 8 - %stackFrame.i11.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @stackFramePrecomputed_func_A.4test, align 8 - %"rubyId_ensure in test.i.i" = load i64, i64* getelementptr inbounds ([14 x i64], [14 x i64]* @sorbet_moduleIDTable, i64 0, i64 9), align 8, !invariant.load !5 - %"rubyStr_ensure in test.i.i" = load i64, i64* getelementptr inbounds ([12 x i64], [12 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 6), align 8, !invariant.load !5 - %7 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_ensure in test.i.i", i64 %"rubyId_ensure in test.i.i", i64 %"rubyStr_test/testdata/compiler/exceptions/basic.rb.i.i", i64 %realpath, %struct.rb_iseq_struct* %stackFrame.i11.i, i32 noundef 5, i32 noundef 6, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef null, i32 noundef 0, i32 noundef 2) - store %struct.rb_iseq_struct* %7, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_A.4test$block_3", align 8 - %8 = call i64 @sorbet_getConstant(i8* noundef getelementptr inbounds ([25 x i8], [25 x i8]* @sorbet_getTRetry.retry, i64 0, i64 0), i64 noundef 25) #12 - store i64 %8, i64* @"", align 8 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts.3, i64 %rubyId_puts.i, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !21 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts.4, i64 %rubyId_puts.i, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !24 - %rubyId_raise.i = load i64, i64* getelementptr inbounds ([14 x i64], [14 x i64]* @sorbet_moduleIDTable, i64 0, i64 10), align 8, !dbg !25, !invariant.load !5 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_raise, i64 %rubyId_raise.i, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !25 - %"rubyId_===.i" = load i64, i64* getelementptr inbounds ([14 x i64], [14 x i64]* @sorbet_moduleIDTable, i64 0, i64 11), align 8, !dbg !26, !invariant.load !5 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @"ic_===", i64 %"rubyId_===.i", i32 noundef 16, i32 noundef 1, i32 noundef 0), !dbg !26 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts.5, i64 %rubyId_puts.i, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !28 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts.6, i64 %rubyId_puts.i, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !29 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts.7, i64 %rubyId_puts.i, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !31 - %"rubyId_.i.i" = load i64, i64* getelementptr inbounds ([14 x i64], [14 x i64]* @sorbet_moduleIDTable, i64 0, i64 12), align 8, !invariant.load !5 - %"rubyStr_.i.i" = load i64, i64* getelementptr inbounds ([12 x i64], [12 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 11), align 8, !invariant.load !5 - %9 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_.i.i", i64 %"rubyId_.i.i", i64 %"rubyStr_test/testdata/compiler/exceptions/basic.rb.i.i", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 3, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals.i14.i, i32 noundef 0, i32 noundef 4) - store %struct.rb_iseq_struct* %9, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_A.13", align 8 - %10 = load %struct.rb_vm_struct*, %struct.rb_vm_struct** @ruby_current_vm_ptr, align 8, !tbaa !33 - %11 = getelementptr inbounds %struct.rb_vm_struct, %struct.rb_vm_struct* %10, i64 0, i32 18 - %12 = load i64, i64* %11, align 8, !tbaa !35 - %13 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !33 - %14 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %13, i64 0, i32 2 - %15 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %14, align 8, !tbaa !45 - %stackFrame.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - %16 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %15, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame.i, %struct.rb_iseq_struct** %16, align 8, !tbaa !48 - %17 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %15, i64 0, i32 4 - %18 = load i64*, i64** %17, align 8, !tbaa !50 - %19 = load i64, i64* %18, align 8, !tbaa !6 - %20 = and i64 %19, -33 - store i64 %20, i64* %18, align 8, !tbaa !6 - call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %13, %struct.rb_control_frame_struct* %15, %struct.rb_iseq_struct* %stackFrame.i) #12 - %21 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %15, i64 0, i32 0 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %21, align 8, !dbg !51, !tbaa !33 - %22 = load i64, i64* @rb_cObject, align 8, !dbg !52 - %23 = call i64 @rb_define_class(i8* getelementptr inbounds ([278 x i8], [278 x i8]* @sorbet_moduleStringTable, i64 0, i64 60), i64 %22) #12, !dbg !52 - %24 = call %struct.rb_control_frame_struct* @sorbet_pushStaticInitFrame(i64 %23) #12, !dbg !52 - %25 = bitcast i64* %positional_table.i.i to i8* - call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %25) #12 - %stackFrame.i.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_A.13", align 8 - %26 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !33 - %27 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %26, i64 0, i32 2 - %28 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %27, align 8, !tbaa !45 - %29 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %28, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame.i.i, %struct.rb_iseq_struct** %29, align 8, !tbaa !48 - %30 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %28, i64 0, i32 4 - %31 = load i64*, i64** %30, align 8, !tbaa !50 - %32 = load i64, i64* %31, align 8, !tbaa !6 - %33 = and i64 %32, -33 - store i64 %33, i64* %31, align 8, !tbaa !6 - call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %26, %struct.rb_control_frame_struct* %28, %struct.rb_iseq_struct* %stackFrame.i.i) #12 - %34 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %24, i64 0, i32 0 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 6), i64** %34, align 8, !dbg !53, !tbaa !33 - %35 = load i64, i64* @guard_epoch_A, align 8, !dbg !10 - %36 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !10, !tbaa !54 - %needTakeSlowPath = icmp ne i64 %35, %36, !dbg !10 - br i1 %needTakeSlowPath, label %37, label %38, !dbg !10, !prof !55 - -37: ; preds = %entry - call void @const_recompute_A(), !dbg !10 - br label %38, !dbg !10 - -38: ; preds = %entry, %37 - %39 = load i64, i64* @guarded_const_A, align 8, !dbg !10 - %40 = load i64, i64* @guard_epoch_A, align 8, !dbg !10 - %41 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !10, !tbaa !54 - %guardUpdated = icmp eq i64 %40, %41, !dbg !10 - call void @llvm.assume(i1 %guardUpdated), !dbg !10 - %stackFrame7.i.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @stackFramePrecomputed_func_A.4test, align 8, !dbg !10 - %42 = call noalias nonnull i8* @ruby_xcalloc(i64 noundef 1, i64 noundef 64) #13, !dbg !10 - %43 = bitcast i8* %42 to i16*, !dbg !10 - %44 = load i16, i16* %43, align 8, !dbg !10 - %45 = and i16 %44, -384, !dbg !10 - %46 = or i16 %45, 1, !dbg !10 - store i16 %46, i16* %43, align 8, !dbg !10 - %47 = getelementptr inbounds i8, i8* %42, i64 8, !dbg !10 - %48 = bitcast i8* %47 to i32*, !dbg !10 - store i32 1, i32* %48, align 8, !dbg !10, !tbaa !56 - %49 = getelementptr inbounds i8, i8* %42, i64 12, !dbg !10 - %50 = getelementptr inbounds i8, i8* %42, i64 4, !dbg !10 - %51 = bitcast i8* %50 to i32*, !dbg !10 - call void @llvm.memset.p0i8.i64(i8* nonnull align 4 %49, i8 0, i64 20, i1 false) #12, !dbg !10 - store i32 1, i32* %51, align 4, !dbg !10, !tbaa !59 - %52 = extractelement <4 x i64> %2, i32 1, !dbg !10 - store i64 %52, i64* %positional_table.i.i, align 8, !dbg !10 - %53 = call noalias nonnull i8* @ruby_xmalloc2(i64 noundef 1, i64 noundef 8) #13, !dbg !10 - call void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture nonnull writeonly align 1 %53, i8* nocapture noundef nonnull readonly align 8 dereferenceable(8) %25, i64 noundef 8, i1 noundef false) #12, !dbg !10 - %54 = getelementptr inbounds i8, i8* %42, i64 32, !dbg !10 - %55 = bitcast i8* %54 to i8**, !dbg !10 - store i8* %53, i8** %55, align 8, !dbg !10, !tbaa !60 - call void @sorbet_vm_define_method(i64 %39, i8* getelementptr inbounds ([278 x i8], [278 x i8]* @sorbet_moduleStringTable, i64 0, i64 91), i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)* noundef @func_A.4test, i8* nonnull %42, %struct.rb_iseq_struct* %stackFrame7.i.i, i1 noundef zeroext true) #12, !dbg !10 - call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %25) #12 - call void @sorbet_popFrame() #12, !dbg !52 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 23), i64** %21, align 8, !dbg !52, !tbaa !33 - %"rubyStr_=== no-raise ===.i" = load i64, i64* getelementptr inbounds ([12 x i64], [12 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 2), align 8, !dbg !61, !invariant.load !5 - %56 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %15, i64 0, i32 1, !dbg !17 - %57 = load i64*, i64** %56, align 8, !dbg !17 - store i64 %12, i64* %57, align 8, !dbg !17, !tbaa !6 - %58 = getelementptr inbounds i64, i64* %57, i64 1, !dbg !17 - store i64 %"rubyStr_=== no-raise ===.i", i64* %58, align 8, !dbg !17, !tbaa !6 - %59 = getelementptr inbounds i64, i64* %58, i64 1, !dbg !17 - store i64* %59, i64** %56, align 8, !dbg !17 - %send = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts, i64 0), !dbg !17 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 24), i64** %21, align 8, !dbg !17, !tbaa !33 - %60 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %15, i64 0, i32 1, !dbg !18 - %61 = load i64*, i64** %60, align 8, !dbg !18 - store i64 %39, i64* %61, align 8, !dbg !18, !tbaa !6 - %62 = getelementptr inbounds i64, i64* %61, i64 1, !dbg !18 - store i64 0, i64* %62, align 8, !dbg !18, !tbaa !6 - %63 = getelementptr inbounds i64, i64* %62, i64 1, !dbg !18 - store i64* %63, i64** %60, align 8, !dbg !18 - %send3 = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_test, i64 0), !dbg !18 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 26), i64** %21, align 8, !dbg !18, !tbaa !33 - %"rubyStr_=== raise ===.i" = load i64, i64* getelementptr inbounds ([12 x i64], [12 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 3), align 8, !dbg !62, !invariant.load !5 - %64 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %15, i64 0, i32 1, !dbg !19 - %65 = load i64*, i64** %64, align 8, !dbg !19 - store i64 %12, i64* %65, align 8, !dbg !19, !tbaa !6 - %66 = getelementptr inbounds i64, i64* %65, i64 1, !dbg !19 - store i64 %"rubyStr_=== raise ===.i", i64* %66, align 8, !dbg !19, !tbaa !6 - %67 = getelementptr inbounds i64, i64* %66, i64 1, !dbg !19 - store i64* %67, i64** %64, align 8, !dbg !19 - %send5 = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts.1, i64 0), !dbg !19 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 27), i64** %21, align 8, !dbg !19, !tbaa !33 - %68 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %15, i64 0, i32 1, !dbg !20 - %69 = load i64*, i64** %68, align 8, !dbg !20 - store i64 %39, i64* %69, align 8, !dbg !20, !tbaa !6 - %70 = getelementptr inbounds i64, i64* %69, i64 1, !dbg !20 - store i64 20, i64* %70, align 8, !dbg !20, !tbaa !6 - %71 = getelementptr inbounds i64, i64* %70, i64 1, !dbg !20 - store i64* %71, i64** %68, align 8, !dbg !20 - %send7 = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_test.2, i64 0), !dbg !20 - ret void -} - -; Function Attrs: nounwind sspreq uwtable -define internal i64 @func_A.4test(i32 %argc, i64* nocapture readonly %argArray, i64 %selfRaw, %struct.rb_control_frame_struct* nonnull align 8 dereferenceable(8) %cfp, i8* nocapture nofree readnone %calling, i8* nocapture nofree readnone %callData) #7 !dbg !23 { -functionEntryInitializers: - %0 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 0 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 6), i64** %0, align 8, !tbaa !33 - %tooManyArgs = icmp ugt i32 %argc, 1, !dbg !63 - %tooFewArgs = icmp ult i32 %argc, 1, !dbg !63 - %or.cond = or i1 %tooManyArgs, %tooFewArgs, !dbg !63 - br i1 %or.cond, label %argCountFailBlock, label %fillRequiredArgs, !dbg !63, !prof !64 - -postProcess: ; preds = %sorbet_writeLocal.exit, %exception-continue - %".sroa.0.0" = phi i64 [ %13, %exception-continue ], [ %10, %sorbet_writeLocal.exit ], !dbg !65 - ret i64 %".sroa.0.0" - -argCountFailBlock: ; preds = %functionEntryInitializers - tail call void @sorbet_raiseArity(i32 %argc, i32 noundef 1, i32 noundef 1) #14, !dbg !63 - unreachable, !dbg !63 - -fillRequiredArgs: ; preds = %functionEntryInitializers - %rawArg_x = load i64, i64* %argArray, align 8, !dbg !63 - %1 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 4, !dbg !63 - %2 = load i64*, i64** %1, align 8, !dbg !63, !tbaa !50 - %3 = load i64, i64* %2, align 8, !dbg !63, !tbaa !6 - %4 = and i64 %3, 8, !dbg !63 - %5 = icmp eq i64 %4, 0, !dbg !63 - br i1 %5, label %6, label %8, !dbg !63, !prof !66 - -6: ; preds = %fillRequiredArgs - %7 = getelementptr inbounds i64, i64* %2, i64 -4, !dbg !63 - store i64 %rawArg_x, i64* %7, align 8, !dbg !63, !tbaa !6 - br label %sorbet_writeLocal.exit, !dbg !63 - -8: ; preds = %fillRequiredArgs - tail call void @sorbet_vm_env_write_slowpath(i64* nonnull align 8 dereferenceable(8) %2, i32 noundef -4, i64 %rawArg_x) #12, !dbg !63 - br label %sorbet_writeLocal.exit, !dbg !63 - -sorbet_writeLocal.exit: ; preds = %6, %8 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 13), i64** %0, align 8, !dbg !65, !tbaa !33 - %9 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !67, !tbaa !33 - %"" = load i64, i64* @"", align 8, !dbg !67 - %10 = tail call i64 @sorbet_run_exception_handling(%struct.rb_execution_context_struct* %9, i64 (i64**, i64, %struct.rb_control_frame_struct*)* noundef @"func_A.4test$block_1", i64** nonnull %0, i64 noundef 0, %struct.rb_control_frame_struct* nonnull %cfp, i64 (i64**, i64, %struct.rb_control_frame_struct*)* noundef @"func_A.4test$block_2", i64 (i64**, i64, %struct.rb_control_frame_struct*)* noundef @"func_A.4test$block_4", i64 (i64**, i64, %struct.rb_control_frame_struct*)* noundef @"func_A.4test$block_3", i64 %"", i64 noundef 0, i64 noundef 0), !dbg !67 - %ensureReturnValue = icmp ne i64 %10, 52, !dbg !67 - br i1 %ensureReturnValue, label %postProcess, label %exception-continue, !dbg !67 - -exception-continue: ; preds = %sorbet_writeLocal.exit - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 20), i64** %0, align 8, !tbaa !33 - %11 = load i64*, i64** %1, align 8, !dbg !68, !tbaa !50 - %12 = getelementptr inbounds i64, i64* %11, i64 -5, !dbg !68 - %13 = load i64, i64* %12, align 8, !dbg !68, !tbaa !6 - br label %postProcess, !dbg !68 -} - -; Function Attrs: ssp -define internal noundef i64 @"func_A.4test$block_1"(i64** nocapture nonnull writeonly align 8 dereferenceable(8) %pc, i64 %localsOffset, %struct.rb_control_frame_struct* %cfp) #8 !dbg !22 { -functionEntryInitializers: - %0 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !33 - %1 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %0, i64 0, i32 2 - %2 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %1, align 8, !tbaa !45 - %3 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 3 - %4 = load i64, i64* %3, align 8, !tbaa !69 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 8), i64** %pc, align 8, !tbaa !33 - %rubyStr_begin = load i64, i64* getelementptr inbounds ([12 x i64], [12 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 7), align 8, !dbg !70, !invariant.load !5 - %5 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 1, !dbg !21 - %6 = load i64*, i64** %5, align 8, !dbg !21 - store i64 %4, i64* %6, align 8, !dbg !21, !tbaa !6 - %7 = getelementptr inbounds i64, i64* %6, i64 1, !dbg !21 - store i64 %rubyStr_begin, i64* %7, align 8, !dbg !21, !tbaa !6 - %8 = getelementptr inbounds i64, i64* %7, i64 1, !dbg !21 - store i64* %8, i64** %5, align 8, !dbg !21 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts.3, i64 0), !dbg !21 - %9 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 4, !dbg !21 - %10 = load i64*, i64** %9, align 8, !dbg !21, !tbaa !50 - %11 = getelementptr inbounds i64, i64* %10, i64 -4, !dbg !21 - %12 = load i64, i64* %11, align 8, !dbg !21, !tbaa !6 - %13 = and i64 %12, -9, !dbg !21 - %14 = icmp ne i64 %13, 0, !dbg !21 - br i1 %14, label %BB5, label %BB7, !dbg !21 - -BB5: ; preds = %functionEntryInitializers - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 10), i64** %pc, align 8, !tbaa !33 - %15 = load i64*, i64** %9, align 8, !dbg !24, !tbaa !50 - %16 = getelementptr inbounds i64, i64* %15, i64 -4, !dbg !24 - %17 = load i64, i64* %16, align 8, !dbg !24, !tbaa !6 - %18 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 1, !dbg !24 - %19 = load i64*, i64** %18, align 8, !dbg !24 - store i64 %4, i64* %19, align 8, !dbg !24, !tbaa !6 - %20 = getelementptr inbounds i64, i64* %19, i64 1, !dbg !24 - store i64 %17, i64* %20, align 8, !dbg !24, !tbaa !6 - %21 = getelementptr inbounds i64, i64* %20, i64 1, !dbg !24 - store i64* %21, i64** %18, align 8, !dbg !24 - %send25 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts.4, i64 0), !dbg !24 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 11), i64** %pc, align 8, !dbg !24, !tbaa !33 - %rubyStr_foo = load i64, i64* getelementptr inbounds ([12 x i64], [12 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 8), align 8, !dbg !71, !invariant.load !5 - %22 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 1, !dbg !25 - %23 = load i64*, i64** %22, align 8, !dbg !25 - store i64 %4, i64* %23, align 8, !dbg !25, !tbaa !6 - %24 = getelementptr inbounds i64, i64* %23, i64 1, !dbg !25 - store i64 %rubyStr_foo, i64* %24, align 8, !dbg !25, !tbaa !6 - %25 = getelementptr inbounds i64, i64* %24, i64 1, !dbg !25 - store i64* %25, i64** %22, align 8, !dbg !25 - %send27 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_raise, i64 0), !dbg !25 - %26 = load i64*, i64** %9, align 8, !dbg !25, !tbaa !50 - %27 = load i64, i64* %26, align 8, !dbg !25, !tbaa !6 - %28 = and i64 %27, 8, !dbg !25 - %29 = icmp eq i64 %28, 0, !dbg !25 - br i1 %29, label %30, label %32, !dbg !25, !prof !66 - -30: ; preds = %BB5 - %31 = getelementptr inbounds i64, i64* %26, i64 -5, !dbg !25 - store i64 %send27, i64* %31, align 8, !dbg !25, !tbaa !6 - br label %BB7, !dbg !25 - -32: ; preds = %BB5 - tail call void @sorbet_vm_env_write_slowpath(i64* nonnull align 8 dereferenceable(8) %26, i32 noundef -5, i64 %send27) #12, !dbg !25 - br label %BB7, !dbg !25 - -BB7: ; preds = %32, %30, %functionEntryInitializers - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 13), i64** %pc, align 8, !tbaa !33 - ret i64 52 -} - -; Function Attrs: ssp -define internal noundef i64 @"func_A.4test$block_2"(i64** nocapture nofree readnone %pc, i64 %localsOffset, %struct.rb_control_frame_struct* nocapture nofree readnone %cfp) #8 !dbg !27 { -vm_get_ep.exit37: - %0 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !33 - %1 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %0, i64 0, i32 2 - %2 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %1, align 8, !tbaa !45 - %3 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 3 - %4 = load i64, i64* %3, align 8, !tbaa !69 - %stackFrame = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_A.4test$block_2", align 8 - tail call void @sorbet_setExceptionStackFrame(%struct.rb_execution_context_struct* %0, %struct.rb_control_frame_struct* %2, %struct.rb_iseq_struct* %stackFrame) #12 - %5 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !33 - %6 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %5, i64 0, i32 2 - %7 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %6, align 8, !tbaa !45 - %8 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %7, i64 0, i32 0 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 13), i64** %8, align 8, !tbaa !33 - %9 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !26, !tbaa !33 - %10 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %9, i64 0, i32 2, !dbg !26 - %11 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %10, align 8, !dbg !26, !tbaa !45 - %12 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %11, i64 0, i32 4, !dbg !26 - %13 = load i64*, i64** %12, align 8, !dbg !26 - %14 = getelementptr inbounds i64, i64* %13, i64 -1, !dbg !26 - %15 = load i64, i64* %14, align 8, !dbg !26, !tbaa !6 - %16 = and i64 %15, -4, !dbg !26 - %17 = inttoptr i64 %16 to i64*, !dbg !26 - %18 = getelementptr inbounds i64, i64* %17, i64 -3, !dbg !26 - %19 = load i64, i64* %18, align 8, !dbg !26, !tbaa !6 - %20 = load i64, i64* @rb_eStandardError, align 8, !dbg !26 - %21 = load i64, i64* @rb_cModule, align 8, !dbg !26 - %22 = tail call i64 @rb_obj_is_kind_of(i64 %19, i64 %20), !dbg !26 - %23 = icmp eq i64 %22, 20, !dbg !26 - %24 = select i1 %23, i64 20, i64 0, !dbg !26 - %25 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %9, i64 0, i32 5, !dbg !26 - %26 = load i32, i32* %25, align 8, !dbg !26, !tbaa !72 - %27 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %9, i64 0, i32 6, !dbg !26 - %28 = load i32, i32* %27, align 4, !dbg !26, !tbaa !73 - %29 = xor i32 %28, -1, !dbg !26 - %30 = and i32 %29, %26, !dbg !26 - %31 = icmp eq i32 %30, 0, !dbg !26 - br i1 %31, label %afterSend, label %86, !dbg !26, !prof !66 - -blockExit: ; preds = %83, %81, %68, %66 - tail call void @sorbet_popFrame() - ret i64 52 - -vm_get_ep.exit35: ; preds = %afterSend - %32 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !74, !tbaa !33 - %33 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %32, i64 0, i32 2, !dbg !74 - %34 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %33, align 8, !dbg !74, !tbaa !45 - %35 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %34, i64 0, i32 4, !dbg !74 - %36 = load i64*, i64** %35, align 8, !dbg !74 - %37 = getelementptr inbounds i64, i64* %36, i64 -1, !dbg !74 - %38 = load i64, i64* %37, align 8, !dbg !74, !tbaa !6 - %39 = and i64 %38, -4, !dbg !74 - %40 = inttoptr i64 %39 to i64*, !dbg !74 - %41 = load i64, i64* %40, align 8, !dbg !74, !tbaa !6 - %42 = and i64 %41, 8, !dbg !74 - %43 = icmp eq i64 %42, 0, !dbg !74 - br i1 %43, label %44, label %46, !dbg !74, !prof !66 - -44: ; preds = %vm_get_ep.exit35 - %45 = getelementptr inbounds i64, i64* %40, i64 -3, !dbg !74 - store i64 8, i64* %45, align 8, !dbg !74, !tbaa !6 - br label %vm_get_ep.exit33, !dbg !74 - -46: ; preds = %vm_get_ep.exit35 - tail call void @sorbet_vm_env_write_slowpath(i64* nonnull align 8 dereferenceable(8) %40, i32 noundef -3, i64 noundef 8) #12, !dbg !74 - br label %vm_get_ep.exit33, !dbg !74 - -vm_get_ep.exit33: ; preds = %44, %46 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 14), i64** %8, align 8, !dbg !75, !tbaa !33 - %47 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !28, !tbaa !33 - %48 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %47, i64 0, i32 2, !dbg !28 - %49 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %48, align 8, !dbg !28, !tbaa !45 - %50 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %49, i64 0, i32 1, !dbg !28 - %51 = load i64*, i64** %50, align 8, !dbg !28 - store i64 %4, i64* %51, align 8, !dbg !28, !tbaa !6 - %52 = getelementptr inbounds i64, i64* %51, i64 1, !dbg !28 - store i64 %19, i64* %52, align 8, !dbg !28, !tbaa !6 - %53 = getelementptr inbounds i64, i64* %52, i64 1, !dbg !28 - store i64* %53, i64** %50, align 8, !dbg !28 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts.5, i64 0), !dbg !28 - %54 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !28, !tbaa !33 - %55 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %54, i64 0, i32 2, !dbg !28 - %56 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %55, align 8, !dbg !28, !tbaa !45 - %57 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %56, i64 0, i32 4, !dbg !28 - %58 = load i64*, i64** %57, align 8, !dbg !28 - %59 = getelementptr inbounds i64, i64* %58, i64 -1, !dbg !28 - %60 = load i64, i64* %59, align 8, !dbg !28, !tbaa !6 - %61 = and i64 %60, -4, !dbg !28 - %62 = inttoptr i64 %61 to i64*, !dbg !28 - %63 = load i64, i64* %62, align 8, !dbg !28, !tbaa !6 - %64 = and i64 %63, 8, !dbg !28 - %65 = icmp eq i64 %64, 0, !dbg !28 - br i1 %65, label %66, label %68, !dbg !28, !prof !66 - -66: ; preds = %vm_get_ep.exit33 - %67 = getelementptr inbounds i64, i64* %62, i64 -5, !dbg !28 - store i64 %send, i64* %67, align 8, !dbg !28, !tbaa !6 - br label %blockExit, !dbg !28 - -68: ; preds = %vm_get_ep.exit33 - tail call void @sorbet_vm_env_write_slowpath(i64* nonnull align 8 dereferenceable(8) %62, i32 noundef -5, i64 %send) #12, !dbg !28 - br label %blockExit, !dbg !28 - -vm_get_ep.exit: ; preds = %afterSend - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 8), i64** %8, align 8, !tbaa !33 - %69 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !76, !tbaa !33 - %70 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %69, i64 0, i32 2, !dbg !76 - %71 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %70, align 8, !dbg !76, !tbaa !45 - %72 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %71, i64 0, i32 4, !dbg !76 - %73 = load i64*, i64** %72, align 8, !dbg !76 - %74 = getelementptr inbounds i64, i64* %73, i64 -1, !dbg !76 - %75 = load i64, i64* %74, align 8, !dbg !76, !tbaa !6 - %76 = and i64 %75, -4, !dbg !76 - %77 = inttoptr i64 %76 to i64*, !dbg !76 - %78 = load i64, i64* %77, align 8, !dbg !76, !tbaa !6 - %79 = and i64 %78, 8, !dbg !76 - %80 = icmp eq i64 %79, 0, !dbg !76 - br i1 %80, label %81, label %83, !dbg !76, !prof !66 - -81: ; preds = %vm_get_ep.exit - %82 = getelementptr inbounds i64, i64* %77, i64 -7, !dbg !76 - store i64 20, i64* %82, align 8, !dbg !76, !tbaa !6 - br label %blockExit, !dbg !76 - -83: ; preds = %vm_get_ep.exit - tail call void @sorbet_vm_env_write_slowpath(i64* nonnull align 8 dereferenceable(8) %77, i32 noundef -7, i64 noundef 20) #12, !dbg !76 - br label %blockExit, !dbg !76 - -afterSend: ; preds = %86, %vm_get_ep.exit37 - %84 = and i64 %24, -9, !dbg !26 - %85 = icmp ne i64 %84, 0, !dbg !26 - br i1 %85, label %vm_get_ep.exit35, label %vm_get_ep.exit, !dbg !26 - -86: ; preds = %vm_get_ep.exit37 - %87 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %9, i64 0, i32 8, !dbg !26 - %88 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %87, align 8, !dbg !26, !tbaa !77 - %89 = tail call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %88, i32 noundef 0) #12, !dbg !26 - br label %afterSend, !dbg !26 -} - -; Function Attrs: ssp -define internal noundef i64 @"func_A.4test$block_3"(i64** nocapture nofree readnone %pc, i64 %localsOffset, %struct.rb_control_frame_struct* nocapture nofree readnone %cfp) #8 !dbg !32 { -functionEntryInitializers: - %0 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !33 - %1 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %0, i64 0, i32 2 - %2 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %1, align 8, !tbaa !45 - %3 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 3 - %4 = load i64, i64* %3, align 8, !tbaa !69 - %stackFrame = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_A.4test$block_3", align 8 - tail call void @sorbet_setExceptionStackFrame(%struct.rb_execution_context_struct* %0, %struct.rb_control_frame_struct* %2, %struct.rb_iseq_struct* %stackFrame) #12 - %5 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !33 - %6 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %5, i64 0, i32 2 - %7 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %6, align 8, !tbaa !45 - %8 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %7, i64 0, i32 0 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 18), i64** %8, align 8, !tbaa !33 - %rubyStr_ensure = load i64, i64* getelementptr inbounds ([12 x i64], [12 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 10), align 8, !dbg !78, !invariant.load !5 - %9 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !31, !tbaa !33 - %10 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %9, i64 0, i32 2, !dbg !31 - %11 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %10, align 8, !dbg !31, !tbaa !45 - %12 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %11, i64 0, i32 1, !dbg !31 - %13 = load i64*, i64** %12, align 8, !dbg !31 - store i64 %4, i64* %13, align 8, !dbg !31, !tbaa !6 - %14 = getelementptr inbounds i64, i64* %13, i64 1, !dbg !31 - store i64 %rubyStr_ensure, i64* %14, align 8, !dbg !31, !tbaa !6 - %15 = getelementptr inbounds i64, i64* %14, i64 1, !dbg !31 - store i64* %15, i64** %12, align 8, !dbg !31 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts.7, i64 0), !dbg !31 - tail call void @sorbet_popFrame() - ret i64 52 -} - -; Function Attrs: ssp -define internal noundef i64 @"func_A.4test$block_4"(i64** nocapture nonnull writeonly align 8 dereferenceable(8) %pc, i64 %localsOffset, %struct.rb_control_frame_struct* %cfp) #8 !dbg !30 { -functionEntryInitializers: - %0 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !33 - %1 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %0, i64 0, i32 2 - %2 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %1, align 8, !tbaa !45 - %3 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 3 - %4 = load i64, i64* %3, align 8, !tbaa !69 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 16), i64** %pc, align 8, !tbaa !33 - %rubyStr_else = load i64, i64* getelementptr inbounds ([12 x i64], [12 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 9), align 8, !dbg !79, !invariant.load !5 - %5 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 1, !dbg !29 - %6 = load i64*, i64** %5, align 8, !dbg !29 - store i64 %4, i64* %6, align 8, !dbg !29, !tbaa !6 - %7 = getelementptr inbounds i64, i64* %6, i64 1, !dbg !29 - store i64 %rubyStr_else, i64* %7, align 8, !dbg !29, !tbaa !6 - %8 = getelementptr inbounds i64, i64* %7, i64 1, !dbg !29 - store i64* %8, i64** %5, align 8, !dbg !29 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts.6, i64 0), !dbg !29 - %9 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 4, !dbg !29 - %10 = load i64*, i64** %9, align 8, !dbg !29, !tbaa !50 - %11 = load i64, i64* %10, align 8, !dbg !29, !tbaa !6 - %12 = and i64 %11, 8, !dbg !29 - %13 = icmp eq i64 %12, 0, !dbg !29 - br i1 %13, label %14, label %16, !dbg !29, !prof !66 - -14: ; preds = %functionEntryInitializers - %15 = getelementptr inbounds i64, i64* %10, i64 -5, !dbg !29 - store i64 %send, i64* %15, align 8, !dbg !29, !tbaa !6 - br label %sorbet_writeLocal.exit, !dbg !29 - -16: ; preds = %functionEntryInitializers - tail call void @sorbet_vm_env_write_slowpath(i64* nonnull align 8 dereferenceable(8) %10, i32 noundef -5, i64 %send) #12, !dbg !29 - br label %sorbet_writeLocal.exit, !dbg !29 - -sorbet_writeLocal.exit: ; preds = %14, %16 - ret i64 52 -} - -; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly -declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #9 - -; Function Attrs: nofree nosync nounwind willreturn -declare void @llvm.assume(i1 noundef) #10 - -; Function Attrs: ssp -define linkonce void @const_recompute_A() local_unnamed_addr #8 { - %1 = tail call i64 @sorbet_getConstant(i8* getelementptr inbounds ([278 x i8], [278 x i8]* @sorbet_moduleStringTable, i64 0, i64 60), i64 1) - store i64 %1, i64* @guarded_const_A, align 8 - %2 = load i64, i64* @ruby_vm_global_constant_state, align 8, !tbaa !54 - store i64 %2, i64* @guard_epoch_A, align 8 - ret void -} - -attributes #0 = { nounwind readnone willreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #1 = { noreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #2 = { "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #3 = { argmemonly nofree nosync nounwind willreturn } -attributes #4 = { allocsize(0,1) "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #5 = { nounwind ssp uwtable "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #6 = { sspreq } -attributes #7 = { nounwind sspreq uwtable } -attributes #8 = { ssp } -attributes #9 = { argmemonly nofree nosync nounwind willreturn writeonly } -attributes #10 = { nofree nosync nounwind willreturn } -attributes #11 = { noreturn nounwind } -attributes #12 = { nounwind } -attributes #13 = { nounwind allocsize(0,1) } -attributes #14 = { noreturn } - -!llvm.module.flags = !{!0, !1, !2} -!llvm.dbg.cu = !{!3} - -!0 = !{i32 2, !"Debug Info Version", i32 3} -!1 = !{i32 4, !"cf-protection-return", i32 1} -!2 = !{i32 4, !"cf-protection-branch", i32 1} -!3 = distinct !DICompileUnit(language: DW_LANG_C, file: !4, producer: "Sorbet LLVM", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !5) -!4 = !DIFile(filename: "test/testdata/compiler/exceptions/basic.rb", directory: ".") -!5 = !{} -!6 = !{!7, !7, i64 0} -!7 = !{!"long", !8, i64 0} -!8 = !{!"omnipotent char", !9, i64 0} -!9 = !{!"Simple C/C++ TBAA"} -!10 = !DILocation(line: 6, column: 3, scope: !11, inlinedAt: !15) -!11 = distinct !DISubprogram(name: "A.", linkageName: "func_A.13L62", scope: null, file: !4, line: 5, type: !12, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!12 = !DISubroutineType(types: !13) -!13 = !{!14} -!14 = !DIBasicType(name: "VALUE", size: 64, encoding: DW_ATE_signed) -!15 = distinct !DILocation(line: 5, column: 1, scope: !16) -!16 = distinct !DISubprogram(name: ".", linkageName: "func_.13", scope: null, file: !4, line: 5, type: !12, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!17 = !DILocation(line: 23, column: 1, scope: !16) -!18 = !DILocation(line: 24, column: 1, scope: !16) -!19 = !DILocation(line: 26, column: 1, scope: !16) -!20 = !DILocation(line: 27, column: 1, scope: !16) -!21 = !DILocation(line: 8, column: 7, scope: !22) -!22 = distinct !DISubprogram(name: "A.test", linkageName: "func_A.4test$block_1", scope: !23, file: !4, line: 6, type: !12, scopeLine: 6, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!23 = distinct !DISubprogram(name: "A.test", linkageName: "func_A.4test", scope: null, file: !4, line: 6, type: !12, scopeLine: 6, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!24 = !DILocation(line: 10, column: 9, scope: !22) -!25 = !DILocation(line: 11, column: 9, scope: !22) -!26 = !DILocation(line: 13, column: 15, scope: !27) -!27 = distinct !DISubprogram(name: "A.test", linkageName: "func_A.4test$block_2", scope: !23, file: !4, line: 6, type: !12, scopeLine: 6, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!28 = !DILocation(line: 14, column: 7, scope: !27) -!29 = !DILocation(line: 16, column: 7, scope: !30) -!30 = distinct !DISubprogram(name: "A.test", linkageName: "func_A.4test$block_4", scope: !23, file: !4, line: 6, type: !12, scopeLine: 6, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!31 = !DILocation(line: 18, column: 7, scope: !32) -!32 = distinct !DISubprogram(name: "A.test", linkageName: "func_A.4test$block_3", scope: !23, file: !4, line: 6, type: !12, scopeLine: 6, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!33 = !{!34, !34, i64 0} -!34 = !{!"any pointer", !8, i64 0} -!35 = !{!36, !7, i64 400} -!36 = !{!"rb_vm_struct", !7, i64 0, !37, i64 8, !34, i64 192, !34, i64 200, !34, i64 208, !41, i64 216, !8, i64 224, !38, i64 264, !38, i64 280, !38, i64 296, !38, i64 312, !7, i64 328, !40, i64 336, !40, i64 340, !40, i64 344, !40, i64 344, !40, i64 344, !40, i64 344, !40, i64 348, !7, i64 352, !8, i64 360, !7, i64 400, !7, i64 408, !7, i64 416, !7, i64 424, !7, i64 432, !7, i64 440, !7, i64 448, !34, i64 456, !34, i64 464, !42, i64 472, !43, i64 992, !34, i64 1016, !34, i64 1024, !40, i64 1032, !40, i64 1036, !38, i64 1040, !8, i64 1056, !7, i64 1096, !7, i64 1104, !7, i64 1112, !7, i64 1120, !7, i64 1128, !40, i64 1136, !34, i64 1144, !34, i64 1152, !34, i64 1160, !34, i64 1168, !34, i64 1176, !34, i64 1184, !40, i64 1192, !44, i64 1200, !8, i64 1232} -!37 = !{!"rb_global_vm_lock_struct", !34, i64 0, !8, i64 8, !38, i64 48, !34, i64 64, !40, i64 72, !8, i64 80, !8, i64 128, !40, i64 176, !40, i64 180} -!38 = !{!"list_head", !39, i64 0} -!39 = !{!"list_node", !34, i64 0, !34, i64 8} -!40 = !{!"int", !8, i64 0} -!41 = !{!"long long", !8, i64 0} -!42 = !{!"", !8, i64 0} -!43 = !{!"rb_hook_list_struct", !34, i64 0, !40, i64 8, !40, i64 12, !40, i64 16} -!44 = !{!"", !7, i64 0, !7, i64 8, !7, i64 16, !7, i64 24} -!45 = !{!46, !34, i64 16} -!46 = !{!"rb_execution_context_struct", !34, i64 0, !7, i64 8, !34, i64 16, !34, i64 24, !34, i64 32, !40, i64 40, !40, i64 44, !34, i64 48, !34, i64 56, !34, i64 64, !7, i64 72, !7, i64 80, !34, i64 88, !7, i64 96, !34, i64 104, !34, i64 112, !7, i64 120, !7, i64 128, !8, i64 136, !8, i64 137, !7, i64 144, !47, i64 152} -!47 = !{!"", !34, i64 0, !34, i64 8, !7, i64 16, !8, i64 24} -!48 = !{!49, !34, i64 16} -!49 = !{!"rb_control_frame_struct", !34, i64 0, !34, i64 8, !34, i64 16, !7, i64 24, !34, i64 32, !34, i64 40, !34, i64 48} -!50 = !{!49, !34, i64 32} -!51 = !DILocation(line: 0, scope: !16) -!52 = !DILocation(line: 5, column: 1, scope: !16) -!53 = !DILocation(line: 0, scope: !11, inlinedAt: !15) -!54 = !{!41, !41, i64 0} -!55 = !{!"branch_weights", i32 1, i32 10000} -!56 = !{!57, !40, i64 8} -!57 = !{!"rb_sorbet_param_struct", !58, i64 0, !40, i64 4, !40, i64 8, !40, i64 12, !40, i64 16, !40, i64 20, !40, i64 24, !40, i64 28, !34, i64 32, !40, i64 40, !40, i64 44, !40, i64 48, !40, i64 52, !34, i64 56} -!58 = !{!"", !40, i64 0, !40, i64 0, !40, i64 0, !40, i64 0, !40, i64 0, !40, i64 0, !40, i64 0, !40, i64 0, !40, i64 1, !40, i64 1} -!59 = !{!57, !40, i64 4} -!60 = !{!57, !34, i64 32} -!61 = !DILocation(line: 23, column: 6, scope: !16) -!62 = !DILocation(line: 26, column: 6, scope: !16) -!63 = !DILocation(line: 6, column: 3, scope: !23) -!64 = !{!"branch_weights", i32 4001, i32 4000000} -!65 = !DILocation(line: 0, scope: !23) -!66 = !{!"branch_weights", i32 2000, i32 1} -!67 = !DILocation(line: 13, column: 5, scope: !23) -!68 = !DILocation(line: 20, column: 3, scope: !23) -!69 = !{!49, !7, i64 24} -!70 = !DILocation(line: 8, column: 12, scope: !22) -!71 = !DILocation(line: 11, column: 15, scope: !22) -!72 = !{!46, !40, i64 40} -!73 = !{!46, !40, i64 44} -!74 = !DILocation(line: 0, scope: !27) -!75 = !DILocation(line: 13, column: 5, scope: !27) -!76 = !DILocation(line: 8, column: 7, scope: !27) -!77 = !{!46, !34, i64 56} -!78 = !DILocation(line: 18, column: 12, scope: !32) -!79 = !DILocation(line: 16, column: 12, scope: !30) diff --git a/test/testdata/compiler/final_method_assume_result_type__1.rb b/test/testdata/compiler/final_method_assume_result_type__1.rb index 8673a98319..d83b521e80 100644 --- a/test/testdata/compiler/final_method_assume_result_type__1.rb +++ b/test/testdata/compiler/final_method_assume_result_type__1.rb @@ -1,27 +1,16 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: LOWERED -# NOTE: explicitly not testing the interaction with interpreted code here +# NOTE explicitly not testing the interaction with interpreted code here # because we only emit direct calls if the call would be compiled -> compiled. -# - require_relative './final_method_assume_result_type__2' -# LOWERED-LABEL: @"func_Object#21test_singleton_method" -# LOWERED: @direct_func_Compiled.21test_singleton_method -# LOWERED-NOT: @sorbet_i_isa_Array -# LOWERED{LITERAL}: } def test_singleton_method Compiled.test_singleton_method.length end -# LOWERED-LABEL: @"func_Object#20test_instance_method" -# LOWERED: @"direct_func_Compiled#20test_instance_method" -# LOWERED-NOT: @sorbet_i_isa_Array -# LOWERED{LITERAL}: } def test_instance_method compiled_inst = T.let(Compiled.new, Compiled) compiled_inst.test_instance_method.length diff --git a/test/testdata/compiler/final_method_child_class.rb b/test/testdata/compiler/final_method_child_class.rb index 24bc921ac3..1f1b909a6f 100644 --- a/test/testdata/compiler/final_method_child_class.rb +++ b/test/testdata/compiler/final_method_child_class.rb @@ -1,7 +1,6 @@ # typed: true # compiled: true # frozen_string_literal: true -# run_filecheck: INITIAL class Parent extend T::Sig @@ -20,6 +19,3 @@ class Child < Parent; end # won't end up being a direct call in the generated llvm. Child.new.final_method -# INITIAL-LABEL: define internal i64 @"func_.13 -# INITIAL: call i64{{.*}}@sorbet_i_send(%struct.FunctionInlineCache* @ic_final_method -# INITIAL{LITERAL}: } diff --git a/test/testdata/compiler/final_method_square_brackets.rb b/test/testdata/compiler/final_method_square_brackets.rb index bb605cbabd..4f0ad40bb9 100644 --- a/test/testdata/compiler/final_method_square_brackets.rb +++ b/test/testdata/compiler/final_method_square_brackets.rb @@ -1,7 +1,6 @@ # typed: true # compiled: true # frozen_string_literal: true -# run_filecheck: INITIAL class Parent extend T::Sig @@ -24,6 +23,3 @@ def [] # # This test is intended to ensure we don't run into that situation again. -# INITIAL-LABEL: define internal i64 @"func_.13 -# INITIAL: call i64 @"direct_func_Parent#2[]" -# INITIAL{LITERAL}: } diff --git a/test/testdata/compiler/float-intrinsics.opt.ll.exp b/test/testdata/compiler/float-intrinsics.opt.ll.exp deleted file mode 100644 index 4f8f4fa733..0000000000 --- a/test/testdata/compiler/float-intrinsics.opt.ll.exp +++ /dev/null @@ -1,1789 +0,0 @@ -; ModuleID = 'payload' -source_filename = "llvm-link" -target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } -%struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.__pthread_cond_s } -%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } -%union.anon = type { i64 } -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } -%struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } -%struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } -%struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } -%struct.anon.0 = type { i16, [2 x i8] } -%struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } -%struct.rb_iseq_location_struct = type { i64, i64, i64, i64, i32, %struct.rb_code_location_struct } -%struct.rb_code_location_struct = type { %struct.rb_code_position_struct, %struct.rb_code_position_struct } -%struct.rb_code_position_struct = type { i32, i32 } -%struct.iseq_insn_info = type { %struct.iseq_insn_info_entry*, i32*, i32, %struct.succ_index_table* } -%struct.iseq_insn_info_entry = type opaque -%struct.succ_index_table = type opaque -%struct.iseq_catch_table = type opaque -%union.iseq_inline_storage_entry = type { %struct.iseq_inline_cache_entry } -%struct.iseq_inline_cache_entry = type { i64, %struct.rb_cref_struct*, i64 } -%struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } -%struct.rb_scope_visi_struct = type { i8, [3 x i8] } -%struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } -%struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } -%union.anon.13 = type { %struct.rb_method_cfunc_struct } -%struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%union.anon.15 = type { i32 } -%struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.16 = type { i64, i64, i64, i64* } -%union.anon.17 = type { %struct.anon.18 } -%struct.anon.18 = type { i64, i32 } -%struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } -%struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } -%struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } -%struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } -%struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } -%struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } -%struct.__sigset_t = type { [16 x i64] } -%struct.rb_calling_info = type { i64, i64, i32, i32 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } -%union.anon.9 = type { %union.pthread_cond_t } -%struct.rb_unblock_callback = type { void (i8*)*, i8* } -%struct.rb_mutex_struct = type opaque -%struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.10 = type { %struct.anon.11 } -%struct.anon.11 = type { i64, i64, i32 } -%struct.rb_fiber_struct = type opaque -%struct.anon.5 = type { [65 x i64] } -%struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } -%struct.rb_event_hook_struct = type opaque -%struct.rb_postponed_job_struct = type opaque -%struct.list_head = type { %struct.list_node } -%union.pthread_mutex_t = type { %struct.__pthread_mutex_s } -%struct.__pthread_mutex_s = type { i32, i32, i32, i32, i32, i16, i16, %struct.__pthread_internal_list } -%struct.__pthread_internal_list = type { %struct.__pthread_internal_list*, %struct.__pthread_internal_list* } -%struct.rb_objspace = type opaque -%struct.rb_at_exit_list = type { void (%struct.rb_vm_struct*)*, %struct.rb_at_exit_list* } -%struct.st_table = type { i8, i8, i8, i32, %struct.st_hash_type*, i64, i64*, i64, i64, %struct.st_table_entry* } -%struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } -%struct.st_table_entry = type opaque -%struct.rb_builtin_function = type opaque -%struct.anon.6 = type { i64, i64, i64, i64 } -%struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } -%struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } -%struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } -%struct.rb_call_info_with_kwarg = type { %struct.rb_call_info, %struct.rb_call_info_kw_arg* } -%struct.rb_call_info_kw_arg = type { i32, [1 x i64] } -%struct.iseq_inline_iv_cache_entry = type { i64, i64 } - -@ruby_current_vm_ptr = external local_unnamed_addr global %struct.rb_vm_struct*, align 8 -@ruby_current_execution_context_ptr = external local_unnamed_addr global %struct.rb_execution_context_struct*, align 8 -@ruby_vm_global_constant_state = external local_unnamed_addr global i64, align 8 -@rb_eRuntimeError = external local_unnamed_addr global i64, align 8 -@.str.9 = private unnamed_addr constant [95 x i8] c"sorbet_getBuildSCMRevision: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@.str.10 = private unnamed_addr constant [93 x i8] c"sorbet_getIsReleaseBuild: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@"stackFramePrecomputed_func_Object#4plus" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@iseqEncodedArray = internal global [49 x i64] zeroinitializer -@fileLineNumberInfo = internal global %struct.SorbetLineNumberInfo zeroinitializer -@"stackFramePrecomputed_func_Object#5minus" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@ic_- = internal global %struct.FunctionInlineCache zeroinitializer -@"stackFramePrecomputed_func_Object#2lt" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"stackFramePrecomputed_func_Object#3lte" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"stackFramePrecomputed_func_.13" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"stackFramePrecomputed_func_.13$block_1" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"stackFramePrecomputed_func_.13$block_2" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"stackFramePrecomputed_func_.13$block_3" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"stackFramePrecomputed_func_.13$block_4" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@ic_untyped = internal global %struct.FunctionInlineCache zeroinitializer -@ic_params = internal global %struct.FunctionInlineCache zeroinitializer -@ic_untyped.1 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_returns = internal global %struct.FunctionInlineCache zeroinitializer -@ic_untyped.2 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_params.3 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_untyped.4 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_returns.5 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_untyped.6 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_params.7 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_returns.8 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_untyped.9 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_params.10 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_returns.11 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_extend = internal global %struct.FunctionInlineCache zeroinitializer -@ic_plus = internal global %struct.FunctionInlineCache zeroinitializer -@ic_p = internal global %struct.FunctionInlineCache zeroinitializer -@ic_minus = internal global %struct.FunctionInlineCache zeroinitializer -@ic_p.12 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_lt = internal global %struct.FunctionInlineCache zeroinitializer -@ic_p.13 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_lt.14 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_p.15 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_lte = internal global %struct.FunctionInlineCache zeroinitializer -@ic_p.16 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_lte.17 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_p.18 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_lte.19 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_p.20 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_plus.21 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_p.22 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_Rational = internal global %struct.FunctionInlineCache zeroinitializer -@ic_plus.23 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_p.24 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_Complex = internal global %struct.FunctionInlineCache zeroinitializer -@ic_plus.25 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_p.26 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_minus.27 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_p.28 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_Rational.29 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_minus.30 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_p.31 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_Complex.32 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_minus.33 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_p.34 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_lt.35 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_p.36 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_Rational.37 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_lt.38 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_p.39 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_lte.40 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_p.41 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_Rational.42 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_lte.43 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_p.44 = internal global %struct.FunctionInlineCache zeroinitializer -@sorbet_moduleStringTable = internal unnamed_addr constant [309 x i8] c"plus\00test/testdata/compiler/float-intrinsics.rb\00Parameter 'x'\00Float\00Parameter 'y'\00T.untyped\00+\00Return value\00minus\00-\00lt\00<\00T::Boolean\00lte\00<=\00\00\00block in \00x\00y\00untyped\00T\00params\00returns\00T::Sig\00extend\00normal\00Object\00p\008.9\00Rational\00Kernel\005\00Complex\0015.4\0018\0025.4\005.923\00master\00", align 1 -@sorbet_moduleIDTable = internal unnamed_addr global [21 x i64] zeroinitializer, align 8 -@sorbet_moduleIDDescriptors = internal unnamed_addr constant [21 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 4 }, %struct.rb_code_position_struct { i32 92, i32 1 }, %struct.rb_code_position_struct { i32 107, i32 5 }, %struct.rb_code_position_struct { i32 113, i32 1 }, %struct.rb_code_position_struct { i32 115, i32 2 }, %struct.rb_code_position_struct { i32 118, i32 1 }, %struct.rb_code_position_struct { i32 131, i32 3 }, %struct.rb_code_position_struct { i32 135, i32 2 }, %struct.rb_code_position_struct { i32 138, i32 16 }, %struct.rb_code_position_struct { i32 155, i32 12 }, %struct.rb_code_position_struct { i32 168, i32 25 }, %struct.rb_code_position_struct { i32 194, i32 1 }, %struct.rb_code_position_struct { i32 196, i32 1 }, %struct.rb_code_position_struct { i32 198, i32 7 }, %struct.rb_code_position_struct { i32 208, i32 6 }, %struct.rb_code_position_struct { i32 215, i32 7 }, %struct.rb_code_position_struct { i32 230, i32 6 }, %struct.rb_code_position_struct { i32 237, i32 6 }, %struct.rb_code_position_struct { i32 251, i32 1 }, %struct.rb_code_position_struct { i32 257, i32 8 }, %struct.rb_code_position_struct { i32 275, i32 7 }], align 8 -@sorbet_moduleRubyStringTable = internal unnamed_addr global [13 x i64] zeroinitializer, align 8 -@sorbet_moduleRubyStringDescriptors = internal unnamed_addr constant [13 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 4 }, %struct.rb_code_position_struct { i32 5, i32 42 }, %struct.rb_code_position_struct { i32 107, i32 5 }, %struct.rb_code_position_struct { i32 115, i32 2 }, %struct.rb_code_position_struct { i32 131, i32 3 }, %struct.rb_code_position_struct { i32 138, i32 16 }, %struct.rb_code_position_struct { i32 168, i32 25 }, %struct.rb_code_position_struct { i32 253, i32 3 }, %struct.rb_code_position_struct { i32 273, i32 1 }, %struct.rb_code_position_struct { i32 283, i32 4 }, %struct.rb_code_position_struct { i32 288, i32 2 }, %struct.rb_code_position_struct { i32 291, i32 4 }, %struct.rb_code_position_struct { i32 296, i32 5 }], align 8 -@guard_epoch_T = linkonce local_unnamed_addr global i64 0 -@guarded_const_T = linkonce local_unnamed_addr global i64 0 -@rb_cFloat = external local_unnamed_addr constant i64 -@"guard_epoch_T::Boolean" = linkonce local_unnamed_addr global i64 0 -@"guarded_const_T::Boolean" = linkonce local_unnamed_addr global i64 0 -@"guard_epoch_T::Sig" = linkonce local_unnamed_addr global i64 0 -@"guarded_const_T::Sig" = linkonce local_unnamed_addr global i64 0 -@rb_cObject = external local_unnamed_addr constant i64 -@rb_mKernel = external local_unnamed_addr constant i64 - -declare i64 @rb_float_plus(i64, i64) local_unnamed_addr #0 - -declare i64 @sorbet_flo_lt(i64, i64) local_unnamed_addr #0 - -declare i64 @sorbet_flo_le(i64, i64) local_unnamed_addr #0 - -; Function Attrs: nounwind readnone willreturn -declare i64 @rb_id2sym(i64) local_unnamed_addr #1 - -; Function Attrs: cold noreturn -declare void @sorbet_cast_failure(i64, i8*, i8*) local_unnamed_addr #2 - -; Function Attrs: noreturn -declare void @sorbet_raiseArity(i32, i32, i32) local_unnamed_addr #3 - -declare %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64, i64, i64, i64, %struct.rb_iseq_struct*, i32, i32, %struct.SorbetLineNumberInfo*, i64*, i32, i32) local_unnamed_addr #0 - -declare void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo*, i64*, i32) local_unnamed_addr #0 - -declare i64 @sorbet_getConstant(i8*, i64) local_unnamed_addr #0 - -declare i64 @sorbet_readRealpath() local_unnamed_addr #0 - -declare void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) local_unnamed_addr #0 - -declare i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache*, i64) local_unnamed_addr #0 - -declare void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_iseq_struct*) local_unnamed_addr #0 - -declare void @sorbet_vm_register_sig(i64, i64, i64, i64, i64 (i64, i64, i32, i64*, i64)*) local_unnamed_addr #0 - -declare void @sorbet_vm_define_method(i64, i8*, i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)*, i8*, %struct.rb_iseq_struct*, i1 zeroext) local_unnamed_addr #0 - -declare void @sorbet_vm_intern_ids(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #0 - -declare void @sorbet_vm_init_string_table(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #0 - -; Function Attrs: noreturn -declare void @rb_raise(i64, i8*, ...) local_unnamed_addr #3 - -declare i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct*, i32) local_unnamed_addr #0 - -; Function Attrs: allocsize(0,1) -declare noalias nonnull i8* @ruby_xcalloc(i64, i64) local_unnamed_addr #4 - -; Function Attrs: allocsize(0,1) -declare noalias nonnull i8* @ruby_xmalloc2(i64, i64) local_unnamed_addr #4 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) #5 - -; Function Attrs: nounwind ssp uwtable -define weak i32 @sorbet_getIsReleaseBuild() local_unnamed_addr #6 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([93 x i8], [93 x i8]* @.str.10, i64 0, i64 0)) #14 - unreachable -} - -; Function Attrs: nounwind ssp uwtable -define weak i8* @sorbet_getBuildSCMRevision() local_unnamed_addr #6 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([95 x i8], [95 x i8]* @.str.9, i64 0, i64 0)) #14 - unreachable -} - -define internal fastcc void @sorbet_globalConstructors(i64 %realpath) unnamed_addr { -allocRubyIds: - tail call void @sorbet_vm_intern_ids(i64* noundef getelementptr inbounds ([21 x i64], [21 x i64]* @sorbet_moduleIDTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([21 x %struct.rb_code_position_struct], [21 x %struct.rb_code_position_struct]* @sorbet_moduleIDDescriptors, i32 0, i32 0), i32 noundef 21, i8* noundef getelementptr inbounds ([309 x i8], [309 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_vm_init_string_table(i64* noundef getelementptr inbounds ([13 x i64], [13 x i64]* @sorbet_moduleRubyStringTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([13 x %struct.rb_code_position_struct], [13 x %struct.rb_code_position_struct]* @sorbet_moduleRubyStringDescriptors, i32 0, i32 0), i32 noundef 13, i8* noundef getelementptr inbounds ([309 x i8], [309 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i32 0, i32 0), i32 noundef 49) - tail call fastcc void @"Constr_stackFramePrecomputed_func_Object#4plus"(i64 %realpath) - tail call fastcc void @"Constr_stackFramePrecomputed_func_Object#5minus"(i64 %realpath) - %rubyId_- = load i64, i64* getelementptr inbounds ([21 x i64], [21 x i64]* @sorbet_moduleIDTable, i64 0, i64 3), align 8, !dbg !10, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_-, i64 %rubyId_-, i32 noundef 16, i32 noundef 1, i32 noundef 0), !dbg !10 - tail call fastcc void @"Constr_stackFramePrecomputed_func_Object#2lt"(i64 %realpath) - tail call fastcc void @"Constr_stackFramePrecomputed_func_Object#3lte"(i64 %realpath) - tail call fastcc void @"Constr_stackFramePrecomputed_func_.13"(i64 %realpath) - tail call fastcc void @"Constr_stackFramePrecomputed_func_.13$block_1"(i64 %realpath) - tail call fastcc void @"Constr_stackFramePrecomputed_func_.13$block_2"(i64 %realpath) - tail call fastcc void @"Constr_stackFramePrecomputed_func_.13$block_3"(i64 %realpath) - tail call fastcc void @"Constr_stackFramePrecomputed_func_.13$block_4"(i64 %realpath) - %rubyId_untyped = load i64, i64* getelementptr inbounds ([21 x i64], [21 x i64]* @sorbet_moduleIDTable, i64 0, i64 13), align 8, !dbg !15, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_untyped, i64 %rubyId_untyped, i32 noundef 16, i32 noundef 0, i32 noundef 0), !dbg !15 - %rubyId_params = load i64, i64* getelementptr inbounds ([21 x i64], [21 x i64]* @sorbet_moduleIDTable, i64 0, i64 14), align 8, !dbg !18, !invariant.load !5 - %rubyId_x = load i64, i64* getelementptr inbounds ([21 x i64], [21 x i64]* @sorbet_moduleIDTable, i64 0, i64 11), align 8, !dbg !18, !invariant.load !5 - %rubyId_y = load i64, i64* getelementptr inbounds ([21 x i64], [21 x i64]* @sorbet_moduleIDTable, i64 0, i64 12), align 8, !dbg !18, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_params, i64 %rubyId_params, i32 noundef 68, i32 noundef 2, i32 noundef 2, i64 %rubyId_x, i64 %rubyId_y), !dbg !18 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_untyped.1, i64 %rubyId_untyped, i32 noundef 16, i32 noundef 0, i32 noundef 0), !dbg !19 - %rubyId_returns = load i64, i64* getelementptr inbounds ([21 x i64], [21 x i64]* @sorbet_moduleIDTable, i64 0, i64 15), align 8, !dbg !18, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_returns, i64 %rubyId_returns, i32 noundef 16, i32 noundef 1, i32 noundef 0), !dbg !18 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_untyped.2, i64 %rubyId_untyped, i32 noundef 16, i32 noundef 0, i32 noundef 0), !dbg !20 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_params.3, i64 %rubyId_params, i32 noundef 68, i32 noundef 2, i32 noundef 2, i64 %rubyId_x, i64 %rubyId_y), !dbg !22 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_untyped.4, i64 %rubyId_untyped, i32 noundef 16, i32 noundef 0, i32 noundef 0), !dbg !23 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_returns.5, i64 %rubyId_returns, i32 noundef 16, i32 noundef 1, i32 noundef 0), !dbg !22 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_untyped.6, i64 %rubyId_untyped, i32 noundef 16, i32 noundef 0, i32 noundef 0), !dbg !24 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_params.7, i64 %rubyId_params, i32 noundef 68, i32 noundef 2, i32 noundef 2, i64 %rubyId_x, i64 %rubyId_y), !dbg !26 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_returns.8, i64 %rubyId_returns, i32 noundef 16, i32 noundef 1, i32 noundef 0), !dbg !26 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_untyped.9, i64 %rubyId_untyped, i32 noundef 16, i32 noundef 0, i32 noundef 0), !dbg !27 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_params.10, i64 %rubyId_params, i32 noundef 68, i32 noundef 2, i32 noundef 2, i64 %rubyId_x, i64 %rubyId_y), !dbg !29 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_returns.11, i64 %rubyId_returns, i32 noundef 16, i32 noundef 1, i32 noundef 0), !dbg !29 - %rubyId_extend = load i64, i64* getelementptr inbounds ([21 x i64], [21 x i64]* @sorbet_moduleIDTable, i64 0, i64 16), align 8, !dbg !30, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_extend, i64 %rubyId_extend, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !30 - %rubyId_plus = load i64, i64* getelementptr inbounds ([21 x i64], [21 x i64]* @sorbet_moduleIDTable, i64 0, i64 0), align 8, !dbg !31, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_plus, i64 %rubyId_plus, i32 noundef 20, i32 noundef 2, i32 noundef 0), !dbg !31 - %rubyId_p = load i64, i64* getelementptr inbounds ([21 x i64], [21 x i64]* @sorbet_moduleIDTable, i64 0, i64 18), align 8, !dbg !32, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_p, i64 %rubyId_p, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !32 - %rubyId_minus = load i64, i64* getelementptr inbounds ([21 x i64], [21 x i64]* @sorbet_moduleIDTable, i64 0, i64 2), align 8, !dbg !33, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_minus, i64 %rubyId_minus, i32 noundef 20, i32 noundef 2, i32 noundef 0), !dbg !33 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_p.12, i64 %rubyId_p, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !34 - %rubyId_lt = load i64, i64* getelementptr inbounds ([21 x i64], [21 x i64]* @sorbet_moduleIDTable, i64 0, i64 4), align 8, !dbg !35, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_lt, i64 %rubyId_lt, i32 noundef 20, i32 noundef 2, i32 noundef 0), !dbg !35 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_p.13, i64 %rubyId_p, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !36 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_lt.14, i64 %rubyId_lt, i32 noundef 20, i32 noundef 2, i32 noundef 0), !dbg !37 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_p.15, i64 %rubyId_p, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !38 - %rubyId_lte = load i64, i64* getelementptr inbounds ([21 x i64], [21 x i64]* @sorbet_moduleIDTable, i64 0, i64 6), align 8, !dbg !39, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_lte, i64 %rubyId_lte, i32 noundef 20, i32 noundef 2, i32 noundef 0), !dbg !39 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_p.16, i64 %rubyId_p, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !40 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_lte.17, i64 %rubyId_lte, i32 noundef 20, i32 noundef 2, i32 noundef 0), !dbg !41 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_p.18, i64 %rubyId_p, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !42 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_lte.19, i64 %rubyId_lte, i32 noundef 20, i32 noundef 2, i32 noundef 0), !dbg !43 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_p.20, i64 %rubyId_p, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !44 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_plus.21, i64 %rubyId_plus, i32 noundef 20, i32 noundef 2, i32 noundef 0), !dbg !45 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_p.22, i64 %rubyId_p, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !46 - %rubyId_Rational = load i64, i64* getelementptr inbounds ([21 x i64], [21 x i64]* @sorbet_moduleIDTable, i64 0, i64 19), align 8, !dbg !47, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_Rational, i64 %rubyId_Rational, i32 noundef 16, i32 noundef 1, i32 noundef 0), !dbg !47 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_plus.23, i64 %rubyId_plus, i32 noundef 20, i32 noundef 2, i32 noundef 0), !dbg !48 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_p.24, i64 %rubyId_p, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !49 - %rubyId_Complex = load i64, i64* getelementptr inbounds ([21 x i64], [21 x i64]* @sorbet_moduleIDTable, i64 0, i64 20), align 8, !dbg !50, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_Complex, i64 %rubyId_Complex, i32 noundef 16, i32 noundef 2, i32 noundef 0), !dbg !50 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_plus.25, i64 %rubyId_plus, i32 noundef 20, i32 noundef 2, i32 noundef 0), !dbg !51 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_p.26, i64 %rubyId_p, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !52 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_minus.27, i64 %rubyId_minus, i32 noundef 20, i32 noundef 2, i32 noundef 0), !dbg !53 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_p.28, i64 %rubyId_p, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !54 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_Rational.29, i64 %rubyId_Rational, i32 noundef 16, i32 noundef 1, i32 noundef 0), !dbg !55 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_minus.30, i64 %rubyId_minus, i32 noundef 20, i32 noundef 2, i32 noundef 0), !dbg !56 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_p.31, i64 %rubyId_p, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !57 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_Complex.32, i64 %rubyId_Complex, i32 noundef 16, i32 noundef 2, i32 noundef 0), !dbg !58 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_minus.33, i64 %rubyId_minus, i32 noundef 20, i32 noundef 2, i32 noundef 0), !dbg !59 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_p.34, i64 %rubyId_p, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !60 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_lt.35, i64 %rubyId_lt, i32 noundef 20, i32 noundef 2, i32 noundef 0), !dbg !61 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_p.36, i64 %rubyId_p, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !62 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_Rational.37, i64 %rubyId_Rational, i32 noundef 16, i32 noundef 1, i32 noundef 0), !dbg !63 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_lt.38, i64 %rubyId_lt, i32 noundef 20, i32 noundef 2, i32 noundef 0), !dbg !64 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_p.39, i64 %rubyId_p, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !65 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_lte.40, i64 %rubyId_lte, i32 noundef 20, i32 noundef 2, i32 noundef 0), !dbg !66 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_p.41, i64 %rubyId_p, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !67 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_Rational.42, i64 %rubyId_Rational, i32 noundef 16, i32 noundef 1, i32 noundef 0), !dbg !68 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_lte.43, i64 %rubyId_lte, i32 noundef 20, i32 noundef 2, i32 noundef 0), !dbg !69 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_p.44, i64 %rubyId_p, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !70 - ret void -} - -; Function Attrs: nounwind sspreq uwtable -define internal i64 @"func_Object#4plus"(i32 %argc, i64* nocapture readonly %argArray, i64 %selfRaw, %struct.rb_control_frame_struct* nocapture nonnull writeonly align 8 dereferenceable(8) %cfp, i8* nocapture nofree readnone %calling, i8* nocapture nofree readnone %callData) #7 !dbg !71 { -functionEntryInitializers: - %callArgs = alloca [2 x i64], align 8 - %0 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 0 - store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 8), i64** %0, align 8, !tbaa !72 - %tooManyArgs = icmp ugt i32 %argc, 2, !dbg !74 - %tooFewArgs = icmp ult i32 %argc, 2, !dbg !74 - %or.cond = or i1 %tooManyArgs, %tooFewArgs, !dbg !74 - br i1 %or.cond, label %argCountFailBlock, label %fillRequiredArgs, !dbg !74, !prof !75 - -argCountFailBlock: ; preds = %functionEntryInitializers - tail call void @sorbet_raiseArity(i32 %argc, i32 noundef 2, i32 noundef 2) #15, !dbg !74 - unreachable, !dbg !74 - -fillRequiredArgs: ; preds = %functionEntryInitializers - %rawArg_x = load i64, i64* %argArray, align 8, !dbg !74 - %1 = getelementptr i64, i64* %argArray, i32 1, !dbg !74 - %rawArg_y = load i64, i64* %1, align 8, !dbg !74 - %2 = and i64 %rawArg_x, 3, !dbg !76 - %3 = icmp eq i64 %2, 2, !dbg !76 - br i1 %3, label %typeTestSuccess6, label %4, !dbg !76 - -4: ; preds = %fillRequiredArgs - %5 = and i64 %rawArg_x, 7, !dbg !76 - %6 = icmp ne i64 %5, 0, !dbg !76 - %7 = and i64 %rawArg_x, -9, !dbg !76 - %8 = icmp eq i64 %7, 0, !dbg !76 - %9 = or i1 %6, %8, !dbg !76 - br i1 %9, label %codeRepl21, label %sorbet_isa_Float.exit, !dbg !76 - -sorbet_isa_Float.exit: ; preds = %4 - %10 = inttoptr i64 %rawArg_x to %struct.iseq_inline_iv_cache_entry*, !dbg !76 - %11 = getelementptr inbounds %struct.iseq_inline_iv_cache_entry, %struct.iseq_inline_iv_cache_entry* %10, i64 0, i32 0, !dbg !76 - %12 = load i64, i64* %11, align 8, !dbg !76, !tbaa !77 - %13 = and i64 %12, 31, !dbg !76 - %14 = icmp eq i64 %13, 4, !dbg !76 - br i1 %14, label %typeTestSuccess6, label %codeRepl21, !dbg !76, !prof !79 - -codeRepl21: ; preds = %4, %sorbet_isa_Float.exit - tail call fastcc void @"func_Object#2lt.cold.3"(i64 %rawArg_x) #16, !dbg !76 - unreachable - -typeTestSuccess6: ; preds = %fillRequiredArgs, %sorbet_isa_Float.exit - store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 9), i64** %0, align 8, !dbg !80, !tbaa !72 - %callArgs0Addr = getelementptr [2 x i64], [2 x i64]* %callArgs, i64 0, i64 0, !dbg !81 - store i64 %rawArg_y, i64* %callArgs0Addr, align 8, !dbg !81 - tail call void @llvm.experimental.noalias.scope.decl(metadata !82), !dbg !81 - %15 = tail call i64 @rb_float_plus(i64 %rawArg_x, i64 %rawArg_y) #17, !dbg !81, !noalias !82 - %16 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !81, !tbaa !72 - %17 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %16, i64 0, i32 5, !dbg !81 - %18 = load i32, i32* %17, align 8, !dbg !81, !tbaa !85 - %19 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %16, i64 0, i32 6, !dbg !81 - %20 = load i32, i32* %19, align 4, !dbg !81, !tbaa !89 - %21 = xor i32 %20, -1, !dbg !81 - %22 = and i32 %21, %18, !dbg !81 - %23 = icmp eq i32 %22, 0, !dbg !81 - br i1 %23, label %typeTestSuccess13, label %24, !dbg !81, !prof !79 - -24: ; preds = %typeTestSuccess6 - %25 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %16, i64 0, i32 8, !dbg !81 - %26 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %25, align 8, !dbg !81, !tbaa !90 - %27 = tail call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %26, i32 noundef 0) #17, !dbg !81 - br label %typeTestSuccess13, !dbg !81 - -typeTestSuccess13: ; preds = %24, %typeTestSuccess6 - ret i64 %15 -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_Object#4plus"(i64 %realpath) unnamed_addr #8 { -entryInitializers: - %rubyId_plus = load i64, i64* getelementptr inbounds ([21 x i64], [21 x i64]* @sorbet_moduleIDTable, i64 0, i64 0), align 8, !invariant.load !5 - %rubyStr_plus = load i64, i64* getelementptr inbounds ([13 x i64], [13 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 0), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/float-intrinsics.rb" = load i64, i64* getelementptr inbounds ([13 x i64], [13 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %rubyStr_plus, i64 %rubyId_plus, i64 %"rubyStr_test/testdata/compiler/float-intrinsics.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 1, i32 noundef 8, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 2) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Object#4plus", align 8 - ret void -} - -; Function Attrs: nounwind sspreq uwtable -define internal i64 @"func_Object#5minus"(i32 %argc, i64* nocapture readonly %argArray, i64 %selfRaw, %struct.rb_control_frame_struct* nonnull align 8 dereferenceable(8) %cfp, i8* nocapture nofree readnone %calling, i8* nocapture nofree readnone %callData) #7 !dbg !11 { -functionEntryInitializers: - %0 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 0 - store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 13), i64** %0, align 8, !tbaa !72 - %tooManyArgs = icmp ugt i32 %argc, 2, !dbg !91 - %tooFewArgs = icmp ult i32 %argc, 2, !dbg !91 - %or.cond = or i1 %tooManyArgs, %tooFewArgs, !dbg !91 - br i1 %or.cond, label %argCountFailBlock, label %fillRequiredArgs, !dbg !91, !prof !75 - -argCountFailBlock: ; preds = %functionEntryInitializers - tail call void @sorbet_raiseArity(i32 %argc, i32 noundef 2, i32 noundef 2) #15, !dbg !91 - unreachable, !dbg !91 - -fillRequiredArgs: ; preds = %functionEntryInitializers - %1 = getelementptr i64, i64* %argArray, i32 1, !dbg !91 - %2 = bitcast i64* %argArray to <2 x i64>*, !dbg !91 - %3 = load <2 x i64>, <2 x i64>* %2, align 8, !dbg !91 - %4 = extractelement <2 x i64> %3, i32 0, !dbg !92 - %5 = and i64 %4, 3, !dbg !92 - %6 = icmp eq i64 %5, 2, !dbg !92 - br i1 %6, label %typeTestSuccess6, label %7, !dbg !92 - -7: ; preds = %fillRequiredArgs - %8 = and i64 %4, 7, !dbg !92 - %9 = icmp ne i64 %8, 0, !dbg !92 - %10 = and i64 %4, -9, !dbg !92 - %11 = icmp eq i64 %10, 0, !dbg !92 - %12 = or i1 %9, %11, !dbg !92 - br i1 %12, label %codeRepl19, label %sorbet_isa_Float.exit, !dbg !92 - -sorbet_isa_Float.exit: ; preds = %7 - %13 = inttoptr i64 %4 to %struct.iseq_inline_iv_cache_entry*, !dbg !92 - %14 = getelementptr inbounds %struct.iseq_inline_iv_cache_entry, %struct.iseq_inline_iv_cache_entry* %13, i64 0, i32 0, !dbg !92 - %15 = load i64, i64* %14, align 8, !dbg !92, !tbaa !77 - %16 = and i64 %15, 31, !dbg !92 - %17 = icmp eq i64 %16, 4, !dbg !92 - br i1 %17, label %typeTestSuccess6, label %codeRepl19, !dbg !92, !prof !79 - -codeRepl19: ; preds = %7, %sorbet_isa_Float.exit - tail call fastcc void @"func_Object#2lt.cold.3"(i64 %4) #16, !dbg !92 - unreachable - -typeTestSuccess6: ; preds = %fillRequiredArgs, %sorbet_isa_Float.exit - store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 14), i64** %0, align 8, !dbg !93, !tbaa !72 - %18 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 1, !dbg !10 - %19 = load i64*, i64** %18, align 8, !dbg !10 - %20 = getelementptr inbounds i64, i64* %19, i64 1, !dbg !10 - %21 = bitcast i64* %19 to <2 x i64>*, !dbg !10 - store <2 x i64> %3, <2 x i64>* %21, align 8, !dbg !10, !tbaa !6 - %22 = getelementptr inbounds i64, i64* %20, i64 1, !dbg !10 - store i64* %22, i64** %18, align 8, !dbg !10 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_-, i64 0), !dbg !10 - ret i64 %send -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_Object#5minus"(i64 %realpath) unnamed_addr #8 { -entryInitializers: - %rubyId_minus = load i64, i64* getelementptr inbounds ([21 x i64], [21 x i64]* @sorbet_moduleIDTable, i64 0, i64 2), align 8, !invariant.load !5 - %rubyStr_minus = load i64, i64* getelementptr inbounds ([13 x i64], [13 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 2), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/float-intrinsics.rb" = load i64, i64* getelementptr inbounds ([13 x i64], [13 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %rubyStr_minus, i64 %rubyId_minus, i64 %"rubyStr_test/testdata/compiler/float-intrinsics.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 1, i32 noundef 13, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 2) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Object#5minus", align 8 - ret void -} - -; Function Attrs: nounwind sspreq uwtable -define internal i64 @"func_Object#2lt"(i32 %argc, i64* nocapture readonly %argArray, i64 %selfRaw, %struct.rb_control_frame_struct* nocapture nonnull writeonly align 8 dereferenceable(8) %cfp, i8* nocapture nofree readnone %calling, i8* nocapture nofree readnone %callData) #7 !dbg !94 { -functionEntryInitializers: - %callArgs = alloca [2 x i64], align 8 - %0 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 0 - store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 18), i64** %0, align 8, !tbaa !72 - %tooManyArgs = icmp ugt i32 %argc, 2, !dbg !95 - %tooFewArgs = icmp ult i32 %argc, 2, !dbg !95 - %or.cond = or i1 %tooManyArgs, %tooFewArgs, !dbg !95 - br i1 %or.cond, label %argCountFailBlock, label %fillRequiredArgs, !dbg !95, !prof !75 - -argCountFailBlock: ; preds = %functionEntryInitializers - tail call void @sorbet_raiseArity(i32 %argc, i32 noundef 2, i32 noundef 2) #15, !dbg !95 - unreachable, !dbg !95 - -fillRequiredArgs: ; preds = %functionEntryInitializers - %rawArg_x = load i64, i64* %argArray, align 8, !dbg !95 - %1 = getelementptr i64, i64* %argArray, i32 1, !dbg !95 - %rawArg_y = load i64, i64* %1, align 8, !dbg !95 - %2 = and i64 %rawArg_x, 3, !dbg !96 - %3 = icmp eq i64 %2, 2, !dbg !96 - br i1 %3, label %typeTestSuccess6, label %4, !dbg !96 - -4: ; preds = %fillRequiredArgs - %5 = and i64 %rawArg_x, 7, !dbg !96 - %6 = icmp ne i64 %5, 0, !dbg !96 - %7 = and i64 %rawArg_x, -9, !dbg !96 - %8 = icmp eq i64 %7, 0, !dbg !96 - %9 = or i1 %6, %8, !dbg !96 - br i1 %9, label %codeRepl21, label %sorbet_isa_Float.exit, !dbg !96 - -sorbet_isa_Float.exit: ; preds = %4 - %10 = inttoptr i64 %rawArg_x to %struct.iseq_inline_iv_cache_entry*, !dbg !96 - %11 = getelementptr inbounds %struct.iseq_inline_iv_cache_entry, %struct.iseq_inline_iv_cache_entry* %10, i64 0, i32 0, !dbg !96 - %12 = load i64, i64* %11, align 8, !dbg !96, !tbaa !77 - %13 = and i64 %12, 31, !dbg !96 - %14 = icmp eq i64 %13, 4, !dbg !96 - br i1 %14, label %typeTestSuccess6, label %codeRepl21, !dbg !96, !prof !79 - -codeRepl21: ; preds = %4, %sorbet_isa_Float.exit - tail call fastcc void @"func_Object#2lt.cold.3"(i64 %rawArg_x) #16, !dbg !96 - unreachable - -typeTestSuccess6: ; preds = %fillRequiredArgs, %sorbet_isa_Float.exit - store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 19), i64** %0, align 8, !dbg !97, !tbaa !72 - %callArgs0Addr = getelementptr [2 x i64], [2 x i64]* %callArgs, i64 0, i64 0, !dbg !98 - store i64 %rawArg_y, i64* %callArgs0Addr, align 8, !dbg !98 - tail call void @llvm.experimental.noalias.scope.decl(metadata !99), !dbg !98 - %15 = tail call i64 @sorbet_flo_lt(i64 %rawArg_x, i64 %rawArg_y) #17, !dbg !98, !noalias !99 - %16 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !98, !tbaa !72 - %17 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %16, i64 0, i32 5, !dbg !98 - %18 = load i32, i32* %17, align 8, !dbg !98, !tbaa !85 - %19 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %16, i64 0, i32 6, !dbg !98 - %20 = load i32, i32* %19, align 4, !dbg !98, !tbaa !89 - %21 = xor i32 %20, -1, !dbg !98 - %22 = and i32 %21, %18, !dbg !98 - %23 = icmp eq i32 %22, 0, !dbg !98 - br i1 %23, label %rb_vm_check_ints.exit, label %24, !dbg !98, !prof !79 - -24: ; preds = %typeTestSuccess6 - %25 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %16, i64 0, i32 8, !dbg !98 - %26 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %25, align 8, !dbg !98, !tbaa !90 - %27 = tail call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %26, i32 noundef 0) #17, !dbg !98 - br label %rb_vm_check_ints.exit, !dbg !98 - -rb_vm_check_ints.exit: ; preds = %typeTestSuccess6, %24 - switch i64 %15, label %codeRepl [ - i64 20, label %typeTestSuccess13 - i64 0, label %typeTestSuccess13 - ], !prof !102 - -typeTestSuccess13: ; preds = %rb_vm_check_ints.exit, %rb_vm_check_ints.exit - ret i64 %15 - -codeRepl: ; preds = %rb_vm_check_ints.exit - tail call fastcc void @"func_Object#2lt.cold.1"(i64 %15) #16, !dbg !103 - unreachable -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_Object#2lt"(i64 %realpath) unnamed_addr #8 { -entryInitializers: - %rubyId_lt = load i64, i64* getelementptr inbounds ([21 x i64], [21 x i64]* @sorbet_moduleIDTable, i64 0, i64 4), align 8, !invariant.load !5 - %rubyStr_lt = load i64, i64* getelementptr inbounds ([13 x i64], [13 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 3), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/float-intrinsics.rb" = load i64, i64* getelementptr inbounds ([13 x i64], [13 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %rubyStr_lt, i64 %rubyId_lt, i64 %"rubyStr_test/testdata/compiler/float-intrinsics.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 1, i32 noundef 18, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 2) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Object#2lt", align 8 - ret void -} - -; Function Attrs: nounwind sspreq uwtable -define internal i64 @"func_Object#3lte"(i32 %argc, i64* nocapture readonly %argArray, i64 %selfRaw, %struct.rb_control_frame_struct* nocapture nonnull writeonly align 8 dereferenceable(8) %cfp, i8* nocapture nofree readnone %calling, i8* nocapture nofree readnone %callData) #7 !dbg !104 { -functionEntryInitializers: - %callArgs = alloca [2 x i64], align 8 - %0 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 0 - store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 23), i64** %0, align 8, !tbaa !72 - %tooManyArgs = icmp ugt i32 %argc, 2, !dbg !105 - %tooFewArgs = icmp ult i32 %argc, 2, !dbg !105 - %or.cond = or i1 %tooManyArgs, %tooFewArgs, !dbg !105 - br i1 %or.cond, label %argCountFailBlock, label %fillRequiredArgs, !dbg !105, !prof !75 - -argCountFailBlock: ; preds = %functionEntryInitializers - tail call void @sorbet_raiseArity(i32 %argc, i32 noundef 2, i32 noundef 2) #15, !dbg !105 - unreachable, !dbg !105 - -fillRequiredArgs: ; preds = %functionEntryInitializers - %rawArg_x = load i64, i64* %argArray, align 8, !dbg !105 - %1 = getelementptr i64, i64* %argArray, i32 1, !dbg !105 - %rawArg_y = load i64, i64* %1, align 8, !dbg !105 - %2 = and i64 %rawArg_x, 3, !dbg !106 - %3 = icmp eq i64 %2, 2, !dbg !106 - br i1 %3, label %typeTestSuccess6, label %4, !dbg !106 - -4: ; preds = %fillRequiredArgs - %5 = and i64 %rawArg_x, 7, !dbg !106 - %6 = icmp ne i64 %5, 0, !dbg !106 - %7 = and i64 %rawArg_x, -9, !dbg !106 - %8 = icmp eq i64 %7, 0, !dbg !106 - %9 = or i1 %6, %8, !dbg !106 - br i1 %9, label %codeRepl21, label %sorbet_isa_Float.exit, !dbg !106 - -sorbet_isa_Float.exit: ; preds = %4 - %10 = inttoptr i64 %rawArg_x to %struct.iseq_inline_iv_cache_entry*, !dbg !106 - %11 = getelementptr inbounds %struct.iseq_inline_iv_cache_entry, %struct.iseq_inline_iv_cache_entry* %10, i64 0, i32 0, !dbg !106 - %12 = load i64, i64* %11, align 8, !dbg !106, !tbaa !77 - %13 = and i64 %12, 31, !dbg !106 - %14 = icmp eq i64 %13, 4, !dbg !106 - br i1 %14, label %typeTestSuccess6, label %codeRepl21, !dbg !106, !prof !79 - -codeRepl21: ; preds = %4, %sorbet_isa_Float.exit - tail call fastcc void @"func_Object#2lt.cold.3"(i64 %rawArg_x) #16, !dbg !106 - unreachable - -typeTestSuccess6: ; preds = %fillRequiredArgs, %sorbet_isa_Float.exit - store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 24), i64** %0, align 8, !dbg !107, !tbaa !72 - %callArgs0Addr = getelementptr [2 x i64], [2 x i64]* %callArgs, i64 0, i64 0, !dbg !108 - store i64 %rawArg_y, i64* %callArgs0Addr, align 8, !dbg !108 - tail call void @llvm.experimental.noalias.scope.decl(metadata !109), !dbg !108 - %15 = tail call i64 @sorbet_flo_le(i64 %rawArg_x, i64 %rawArg_y) #17, !dbg !108, !noalias !109 - %16 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !108, !tbaa !72 - %17 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %16, i64 0, i32 5, !dbg !108 - %18 = load i32, i32* %17, align 8, !dbg !108, !tbaa !85 - %19 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %16, i64 0, i32 6, !dbg !108 - %20 = load i32, i32* %19, align 4, !dbg !108, !tbaa !89 - %21 = xor i32 %20, -1, !dbg !108 - %22 = and i32 %21, %18, !dbg !108 - %23 = icmp eq i32 %22, 0, !dbg !108 - br i1 %23, label %rb_vm_check_ints.exit, label %24, !dbg !108, !prof !79 - -24: ; preds = %typeTestSuccess6 - %25 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %16, i64 0, i32 8, !dbg !108 - %26 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %25, align 8, !dbg !108, !tbaa !90 - %27 = tail call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %26, i32 noundef 0) #17, !dbg !108 - br label %rb_vm_check_ints.exit, !dbg !108 - -rb_vm_check_ints.exit: ; preds = %typeTestSuccess6, %24 - switch i64 %15, label %codeRepl [ - i64 20, label %typeTestSuccess13 - i64 0, label %typeTestSuccess13 - ], !prof !102 - -typeTestSuccess13: ; preds = %rb_vm_check_ints.exit, %rb_vm_check_ints.exit - ret i64 %15 - -codeRepl: ; preds = %rb_vm_check_ints.exit - tail call fastcc void @"func_Object#2lt.cold.1"(i64 %15) #16, !dbg !112 - unreachable -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_Object#3lte"(i64 %realpath) unnamed_addr #8 { -entryInitializers: - %rubyId_lte = load i64, i64* getelementptr inbounds ([21 x i64], [21 x i64]* @sorbet_moduleIDTable, i64 0, i64 6), align 8, !invariant.load !5 - %rubyStr_lte = load i64, i64* getelementptr inbounds ([13 x i64], [13 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 4), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/float-intrinsics.rb" = load i64, i64* getelementptr inbounds ([13 x i64], [13 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %rubyStr_lte, i64 %rubyId_lte, i64 %"rubyStr_test/testdata/compiler/float-intrinsics.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 1, i32 noundef 23, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 2) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Object#3lte", align 8 - ret void -} - -; Function Attrs: ssp -define internal i64 @"func_.13$block_1"(i64 %firstYieldArgRaw, i64 %localsOffset, i32 %argc, i64* nocapture nofree readnone %argArray, i64 %blockArg) #8 !dbg !16 { -functionEntryInitializers: - %0 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !72 - %1 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %0, i64 0, i32 2 - %2 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %1, align 8, !tbaa !113 - %3 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 3 - %4 = load i64, i64* %3, align 8, !tbaa !114 - %stackFrame = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13$block_1", align 8 - %5 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame, %struct.rb_iseq_struct** %5, align 8, !tbaa !116 - %6 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 4 - %7 = load i64*, i64** %6, align 8, !tbaa !117 - %8 = load i64, i64* %7, align 8, !tbaa !6 - %9 = and i64 %8, -129 - store i64 %9, i64* %7, align 8, !tbaa !6 - %10 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 0 - store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 7), i64** %10, align 8, !tbaa !72 - %11 = load i64, i64* @guard_epoch_T, align 8, !dbg !15 - %12 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !15, !tbaa !118 - %needTakeSlowPath = icmp ne i64 %11, %12, !dbg !15 - br i1 %needTakeSlowPath, label %13, label %14, !dbg !15, !prof !120 - -13: ; preds = %functionEntryInitializers - tail call void @const_recompute_T(), !dbg !15 - br label %14, !dbg !15 - -14: ; preds = %functionEntryInitializers, %13 - %15 = load i64, i64* @guarded_const_T, align 8, !dbg !15 - %16 = load i64, i64* @guard_epoch_T, align 8, !dbg !15 - %17 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !15, !tbaa !118 - %guardUpdated = icmp eq i64 %16, %17, !dbg !15 - tail call void @llvm.assume(i1 %guardUpdated), !dbg !15 - %18 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 1, !dbg !15 - %19 = load i64*, i64** %18, align 8, !dbg !15 - store i64 %15, i64* %19, align 8, !dbg !15, !tbaa !6 - %20 = getelementptr inbounds i64, i64* %19, i64 1, !dbg !15 - store i64* %20, i64** %18, align 8, !dbg !15 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_untyped, i64 0), !dbg !15 - %21 = load i64, i64* @rb_cFloat, align 8, !dbg !18 - %22 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 1, !dbg !18 - %23 = load i64*, i64** %22, align 8, !dbg !18 - store i64 %4, i64* %23, align 8, !dbg !18, !tbaa !6 - %24 = getelementptr inbounds i64, i64* %23, i64 1, !dbg !18 - store i64 %21, i64* %24, align 8, !dbg !18, !tbaa !6 - %25 = getelementptr inbounds i64, i64* %24, i64 1, !dbg !18 - store i64 %send, i64* %25, align 8, !dbg !18, !tbaa !6 - %26 = getelementptr inbounds i64, i64* %25, i64 1, !dbg !18 - store i64* %26, i64** %22, align 8, !dbg !18 - %send38 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_params, i64 0), !dbg !18 - %27 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 1, !dbg !19 - %28 = load i64*, i64** %27, align 8, !dbg !19 - store i64 %15, i64* %28, align 8, !dbg !19, !tbaa !6 - %29 = getelementptr inbounds i64, i64* %28, i64 1, !dbg !19 - store i64* %29, i64** %27, align 8, !dbg !19 - %send40 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_untyped.1, i64 0), !dbg !19 - %30 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 1, !dbg !18 - %31 = load i64*, i64** %30, align 8, !dbg !18 - store i64 %send38, i64* %31, align 8, !dbg !18, !tbaa !6 - %32 = getelementptr inbounds i64, i64* %31, i64 1, !dbg !18 - store i64 %send40, i64* %32, align 8, !dbg !18, !tbaa !6 - %33 = getelementptr inbounds i64, i64* %32, i64 1, !dbg !18 - store i64* %33, i64** %30, align 8, !dbg !18 - %send42 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_returns, i64 0), !dbg !18 - ret i64 %send42, !dbg !18 -} - -; Function Attrs: ssp -define internal i64 @"func_.13$block_2"(i64 %firstYieldArgRaw, i64 %localsOffset, i32 %argc, i64* nocapture nofree readnone %argArray, i64 %blockArg) #8 !dbg !21 { -functionEntryInitializers: - %0 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !72 - %1 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %0, i64 0, i32 2 - %2 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %1, align 8, !tbaa !113 - %3 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 3 - %4 = load i64, i64* %3, align 8, !tbaa !114 - %stackFrame = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13$block_2", align 8 - %5 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame, %struct.rb_iseq_struct** %5, align 8, !tbaa !116 - %6 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 4 - %7 = load i64*, i64** %6, align 8, !tbaa !117 - %8 = load i64, i64* %7, align 8, !tbaa !6 - %9 = and i64 %8, -129 - store i64 %9, i64* %7, align 8, !tbaa !6 - %10 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 0 - store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 12), i64** %10, align 8, !tbaa !72 - %11 = load i64, i64* @guard_epoch_T, align 8, !dbg !20 - %12 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !20, !tbaa !118 - %needTakeSlowPath = icmp ne i64 %11, %12, !dbg !20 - br i1 %needTakeSlowPath, label %13, label %14, !dbg !20, !prof !120 - -13: ; preds = %functionEntryInitializers - tail call void @const_recompute_T(), !dbg !20 - br label %14, !dbg !20 - -14: ; preds = %functionEntryInitializers, %13 - %15 = load i64, i64* @guarded_const_T, align 8, !dbg !20 - %16 = load i64, i64* @guard_epoch_T, align 8, !dbg !20 - %17 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !20, !tbaa !118 - %guardUpdated = icmp eq i64 %16, %17, !dbg !20 - tail call void @llvm.assume(i1 %guardUpdated), !dbg !20 - %18 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 1, !dbg !20 - %19 = load i64*, i64** %18, align 8, !dbg !20 - store i64 %15, i64* %19, align 8, !dbg !20, !tbaa !6 - %20 = getelementptr inbounds i64, i64* %19, i64 1, !dbg !20 - store i64* %20, i64** %18, align 8, !dbg !20 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_untyped.2, i64 0), !dbg !20 - %21 = load i64, i64* @rb_cFloat, align 8, !dbg !22 - %22 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 1, !dbg !22 - %23 = load i64*, i64** %22, align 8, !dbg !22 - store i64 %4, i64* %23, align 8, !dbg !22, !tbaa !6 - %24 = getelementptr inbounds i64, i64* %23, i64 1, !dbg !22 - store i64 %21, i64* %24, align 8, !dbg !22, !tbaa !6 - %25 = getelementptr inbounds i64, i64* %24, i64 1, !dbg !22 - store i64 %send, i64* %25, align 8, !dbg !22, !tbaa !6 - %26 = getelementptr inbounds i64, i64* %25, i64 1, !dbg !22 - store i64* %26, i64** %22, align 8, !dbg !22 - %send38 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_params.3, i64 0), !dbg !22 - %27 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 1, !dbg !23 - %28 = load i64*, i64** %27, align 8, !dbg !23 - store i64 %15, i64* %28, align 8, !dbg !23, !tbaa !6 - %29 = getelementptr inbounds i64, i64* %28, i64 1, !dbg !23 - store i64* %29, i64** %27, align 8, !dbg !23 - %send40 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_untyped.4, i64 0), !dbg !23 - %30 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 1, !dbg !22 - %31 = load i64*, i64** %30, align 8, !dbg !22 - store i64 %send38, i64* %31, align 8, !dbg !22, !tbaa !6 - %32 = getelementptr inbounds i64, i64* %31, i64 1, !dbg !22 - store i64 %send40, i64* %32, align 8, !dbg !22, !tbaa !6 - %33 = getelementptr inbounds i64, i64* %32, i64 1, !dbg !22 - store i64* %33, i64** %30, align 8, !dbg !22 - %send42 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_returns.5, i64 0), !dbg !22 - ret i64 %send42, !dbg !22 -} - -; Function Attrs: ssp -define internal i64 @"func_.13$block_3"(i64 %firstYieldArgRaw, i64 %localsOffset, i32 %argc, i64* nocapture nofree readnone %argArray, i64 %blockArg) #8 !dbg !25 { -functionEntryInitializers: - %0 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !72 - %1 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %0, i64 0, i32 2 - %2 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %1, align 8, !tbaa !113 - %3 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 3 - %4 = load i64, i64* %3, align 8, !tbaa !114 - %stackFrame = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13$block_3", align 8 - %5 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame, %struct.rb_iseq_struct** %5, align 8, !tbaa !116 - %6 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 4 - %7 = load i64*, i64** %6, align 8, !tbaa !117 - %8 = load i64, i64* %7, align 8, !tbaa !6 - %9 = and i64 %8, -129 - store i64 %9, i64* %7, align 8, !tbaa !6 - %10 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 0 - store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 17), i64** %10, align 8, !tbaa !72 - %11 = load i64, i64* @guard_epoch_T, align 8, !dbg !24 - %12 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !24, !tbaa !118 - %needTakeSlowPath = icmp ne i64 %11, %12, !dbg !24 - br i1 %needTakeSlowPath, label %13, label %14, !dbg !24, !prof !120 - -13: ; preds = %functionEntryInitializers - tail call void @const_recompute_T(), !dbg !24 - br label %14, !dbg !24 - -14: ; preds = %functionEntryInitializers, %13 - %15 = load i64, i64* @guarded_const_T, align 8, !dbg !24 - %16 = load i64, i64* @guard_epoch_T, align 8, !dbg !24 - %17 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !24, !tbaa !118 - %guardUpdated = icmp eq i64 %16, %17, !dbg !24 - tail call void @llvm.assume(i1 %guardUpdated), !dbg !24 - %18 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 1, !dbg !24 - %19 = load i64*, i64** %18, align 8, !dbg !24 - store i64 %15, i64* %19, align 8, !dbg !24, !tbaa !6 - %20 = getelementptr inbounds i64, i64* %19, i64 1, !dbg !24 - store i64* %20, i64** %18, align 8, !dbg !24 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_untyped.6, i64 0), !dbg !24 - %21 = load i64, i64* @rb_cFloat, align 8, !dbg !26 - %22 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 1, !dbg !26 - %23 = load i64*, i64** %22, align 8, !dbg !26 - store i64 %4, i64* %23, align 8, !dbg !26, !tbaa !6 - %24 = getelementptr inbounds i64, i64* %23, i64 1, !dbg !26 - store i64 %21, i64* %24, align 8, !dbg !26, !tbaa !6 - %25 = getelementptr inbounds i64, i64* %24, i64 1, !dbg !26 - store i64 %send, i64* %25, align 8, !dbg !26, !tbaa !6 - %26 = getelementptr inbounds i64, i64* %25, i64 1, !dbg !26 - store i64* %26, i64** %22, align 8, !dbg !26 - %send34 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_params.7, i64 0), !dbg !26 - %27 = load i64, i64* @"guard_epoch_T::Boolean", align 8, !dbg !26 - %28 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !26, !tbaa !118 - %needTakeSlowPath31 = icmp ne i64 %27, %28, !dbg !26 - br i1 %needTakeSlowPath31, label %29, label %30, !dbg !26, !prof !120 - -29: ; preds = %14 - tail call void @"const_recompute_T::Boolean"(), !dbg !26 - br label %30, !dbg !26 - -30: ; preds = %14, %29 - %31 = load i64, i64* @"guarded_const_T::Boolean", align 8, !dbg !26 - %32 = load i64, i64* @"guard_epoch_T::Boolean", align 8, !dbg !26 - %33 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !26, !tbaa !118 - %guardUpdated32 = icmp eq i64 %32, %33, !dbg !26 - tail call void @llvm.assume(i1 %guardUpdated32), !dbg !26 - %34 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 1, !dbg !26 - %35 = load i64*, i64** %34, align 8, !dbg !26 - store i64 %send34, i64* %35, align 8, !dbg !26, !tbaa !6 - %36 = getelementptr inbounds i64, i64* %35, i64 1, !dbg !26 - store i64 %31, i64* %36, align 8, !dbg !26, !tbaa !6 - %37 = getelementptr inbounds i64, i64* %36, i64 1, !dbg !26 - store i64* %37, i64** %34, align 8, !dbg !26 - %send36 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_returns.8, i64 0), !dbg !26 - ret i64 %send36, !dbg !26 -} - -; Function Attrs: ssp -define internal i64 @"func_.13$block_4"(i64 %firstYieldArgRaw, i64 %localsOffset, i32 %argc, i64* nocapture nofree readnone %argArray, i64 %blockArg) #8 !dbg !28 { -functionEntryInitializers: - %0 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !72 - %1 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %0, i64 0, i32 2 - %2 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %1, align 8, !tbaa !113 - %3 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 3 - %4 = load i64, i64* %3, align 8, !tbaa !114 - %stackFrame = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13$block_4", align 8 - %5 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame, %struct.rb_iseq_struct** %5, align 8, !tbaa !116 - %6 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 4 - %7 = load i64*, i64** %6, align 8, !tbaa !117 - %8 = load i64, i64* %7, align 8, !tbaa !6 - %9 = and i64 %8, -129 - store i64 %9, i64* %7, align 8, !tbaa !6 - %10 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 0 - store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 22), i64** %10, align 8, !tbaa !72 - %11 = load i64, i64* @guard_epoch_T, align 8, !dbg !27 - %12 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !27, !tbaa !118 - %needTakeSlowPath = icmp ne i64 %11, %12, !dbg !27 - br i1 %needTakeSlowPath, label %13, label %14, !dbg !27, !prof !120 - -13: ; preds = %functionEntryInitializers - tail call void @const_recompute_T(), !dbg !27 - br label %14, !dbg !27 - -14: ; preds = %functionEntryInitializers, %13 - %15 = load i64, i64* @guarded_const_T, align 8, !dbg !27 - %16 = load i64, i64* @guard_epoch_T, align 8, !dbg !27 - %17 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !27, !tbaa !118 - %guardUpdated = icmp eq i64 %16, %17, !dbg !27 - tail call void @llvm.assume(i1 %guardUpdated), !dbg !27 - %18 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 1, !dbg !27 - %19 = load i64*, i64** %18, align 8, !dbg !27 - store i64 %15, i64* %19, align 8, !dbg !27, !tbaa !6 - %20 = getelementptr inbounds i64, i64* %19, i64 1, !dbg !27 - store i64* %20, i64** %18, align 8, !dbg !27 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_untyped.9, i64 0), !dbg !27 - %21 = load i64, i64* @rb_cFloat, align 8, !dbg !29 - %22 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 1, !dbg !29 - %23 = load i64*, i64** %22, align 8, !dbg !29 - store i64 %4, i64* %23, align 8, !dbg !29, !tbaa !6 - %24 = getelementptr inbounds i64, i64* %23, i64 1, !dbg !29 - store i64 %21, i64* %24, align 8, !dbg !29, !tbaa !6 - %25 = getelementptr inbounds i64, i64* %24, i64 1, !dbg !29 - store i64 %send, i64* %25, align 8, !dbg !29, !tbaa !6 - %26 = getelementptr inbounds i64, i64* %25, i64 1, !dbg !29 - store i64* %26, i64** %22, align 8, !dbg !29 - %send34 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_params.10, i64 0), !dbg !29 - %27 = load i64, i64* @"guard_epoch_T::Boolean", align 8, !dbg !29 - %28 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !29, !tbaa !118 - %needTakeSlowPath31 = icmp ne i64 %27, %28, !dbg !29 - br i1 %needTakeSlowPath31, label %29, label %30, !dbg !29, !prof !120 - -29: ; preds = %14 - tail call void @"const_recompute_T::Boolean"(), !dbg !29 - br label %30, !dbg !29 - -30: ; preds = %14, %29 - %31 = load i64, i64* @"guarded_const_T::Boolean", align 8, !dbg !29 - %32 = load i64, i64* @"guard_epoch_T::Boolean", align 8, !dbg !29 - %33 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !29, !tbaa !118 - %guardUpdated32 = icmp eq i64 %32, %33, !dbg !29 - tail call void @llvm.assume(i1 %guardUpdated32), !dbg !29 - %34 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 1, !dbg !29 - %35 = load i64*, i64** %34, align 8, !dbg !29 - store i64 %send34, i64* %35, align 8, !dbg !29, !tbaa !6 - %36 = getelementptr inbounds i64, i64* %35, i64 1, !dbg !29 - store i64 %31, i64* %36, align 8, !dbg !29, !tbaa !6 - %37 = getelementptr inbounds i64, i64* %36, i64 1, !dbg !29 - store i64* %37, i64** %34, align 8, !dbg !29 - %send36 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_returns.11, i64 0), !dbg !29 - ret i64 %send36, !dbg !29 -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_.13"(i64 %realpath) unnamed_addr #8 { -entryInitializers: - %"rubyId_" = load i64, i64* getelementptr inbounds ([21 x i64], [21 x i64]* @sorbet_moduleIDTable, i64 0, i64 8), align 8, !invariant.load !5 - %"rubyStr_" = load i64, i64* getelementptr inbounds ([13 x i64], [13 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 5), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/float-intrinsics.rb" = load i64, i64* getelementptr inbounds ([13 x i64], [13 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, align 8 - %"rubyId_" = load i64, i64* getelementptr inbounds ([21 x i64], [21 x i64]* @sorbet_moduleIDTable, i64 0, i64 9), align 8, !invariant.load !5 - store i64 %"rubyId_", i64* %locals, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_", i64 %"rubyId_", i64 %"rubyStr_test/testdata/compiler/float-intrinsics.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 0, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull align 8 %locals, i32 noundef 1, i32 noundef 4) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - ret void -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_.13$block_1"(i64 %realpath) unnamed_addr #8 { -entryInitializers: - %stackFrame = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - %"rubyId_block in " = load i64, i64* getelementptr inbounds ([21 x i64], [21 x i64]* @sorbet_moduleIDTable, i64 0, i64 10), align 8, !invariant.load !5 - %"rubyStr_block in " = load i64, i64* getelementptr inbounds ([13 x i64], [13 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 6), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/float-intrinsics.rb" = load i64, i64* getelementptr inbounds ([13 x i64], [13 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %0 = tail call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_block in ", i64 %"rubyId_block in ", i64 %"rubyStr_test/testdata/compiler/float-intrinsics.rb", i64 %realpath, %struct.rb_iseq_struct* %stackFrame, i32 noundef 2, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef null, i32 noundef 0, i32 noundef 4) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13$block_1", align 8 - ret void -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_.13$block_2"(i64 %realpath) unnamed_addr #8 { -entryInitializers: - %stackFrame = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - %"rubyId_block in " = load i64, i64* getelementptr inbounds ([21 x i64], [21 x i64]* @sorbet_moduleIDTable, i64 0, i64 10), align 8, !invariant.load !5 - %"rubyStr_block in " = load i64, i64* getelementptr inbounds ([13 x i64], [13 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 6), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/float-intrinsics.rb" = load i64, i64* getelementptr inbounds ([13 x i64], [13 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %0 = tail call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_block in ", i64 %"rubyId_block in ", i64 %"rubyStr_test/testdata/compiler/float-intrinsics.rb", i64 %realpath, %struct.rb_iseq_struct* %stackFrame, i32 noundef 2, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef null, i32 noundef 0, i32 noundef 4) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13$block_2", align 8 - ret void -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_.13$block_3"(i64 %realpath) unnamed_addr #8 { -entryInitializers: - %stackFrame = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - %"rubyId_block in " = load i64, i64* getelementptr inbounds ([21 x i64], [21 x i64]* @sorbet_moduleIDTable, i64 0, i64 10), align 8, !invariant.load !5 - %"rubyStr_block in " = load i64, i64* getelementptr inbounds ([13 x i64], [13 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 6), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/float-intrinsics.rb" = load i64, i64* getelementptr inbounds ([13 x i64], [13 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %0 = tail call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_block in ", i64 %"rubyId_block in ", i64 %"rubyStr_test/testdata/compiler/float-intrinsics.rb", i64 %realpath, %struct.rb_iseq_struct* %stackFrame, i32 noundef 2, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef null, i32 noundef 0, i32 noundef 4) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13$block_3", align 8 - ret void -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_.13$block_4"(i64 %realpath) unnamed_addr #8 { -entryInitializers: - %stackFrame = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - %"rubyId_block in " = load i64, i64* getelementptr inbounds ([21 x i64], [21 x i64]* @sorbet_moduleIDTable, i64 0, i64 10), align 8, !invariant.load !5 - %"rubyStr_block in " = load i64, i64* getelementptr inbounds ([13 x i64], [13 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 6), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/float-intrinsics.rb" = load i64, i64* getelementptr inbounds ([13 x i64], [13 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %0 = tail call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_block in ", i64 %"rubyId_block in ", i64 %"rubyStr_test/testdata/compiler/float-intrinsics.rb", i64 %realpath, %struct.rb_iseq_struct* %stackFrame, i32 noundef 2, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef null, i32 noundef 0, i32 noundef 4) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13$block_4", align 8 - ret void -} - -; Function Attrs: sspreq -define void @Init_float-intrinsics() local_unnamed_addr #9 { -entry: - %positional_table.i = alloca i64, i32 2, align 8, !dbg !121 - %positional_table320.i = alloca i64, i32 2, align 8, !dbg !122 - %positional_table334.i = alloca i64, i32 2, align 8, !dbg !123 - %positional_table348.i = alloca i64, i32 2, align 8, !dbg !124 - %realpath = tail call i64 @sorbet_readRealpath() - tail call fastcc void @sorbet_globalConstructors(i64 %realpath) - %0 = load %struct.rb_vm_struct*, %struct.rb_vm_struct** @ruby_current_vm_ptr, align 8, !tbaa !72 - %1 = getelementptr inbounds %struct.rb_vm_struct, %struct.rb_vm_struct* %0, i64 0, i32 18 - %2 = load i64, i64* %1, align 8, !tbaa !125 - %3 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !72 - %4 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %3, i64 0, i32 2 - %5 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %4, align 8, !tbaa !113 - %6 = bitcast i64* %positional_table.i to i8* - call void @llvm.lifetime.start.p0i8(i64 16, i8* nonnull %6) - %7 = bitcast i64* %positional_table320.i to i8* - call void @llvm.lifetime.start.p0i8(i64 16, i8* nonnull %7) - %8 = bitcast i64* %positional_table334.i to i8* - call void @llvm.lifetime.start.p0i8(i64 16, i8* nonnull %8) - %9 = bitcast i64* %positional_table348.i to i8* - call void @llvm.lifetime.start.p0i8(i64 16, i8* nonnull %9) - %stackFrame.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - %10 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame.i, %struct.rb_iseq_struct** %10, align 8, !tbaa !116 - %11 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 4 - %12 = load i64*, i64** %11, align 8, !tbaa !117 - %13 = load i64, i64* %12, align 8, !tbaa !6 - %14 = and i64 %13, -33 - store i64 %14, i64* %12, align 8, !tbaa !6 - tail call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %3, %struct.rb_control_frame_struct* %5, %struct.rb_iseq_struct* %stackFrame.i) #17 - %15 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 0 - store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 7), i64** %15, align 8, !dbg !133, !tbaa !72 - %rubyId_plus.i = load i64, i64* getelementptr inbounds ([21 x i64], [21 x i64]* @sorbet_moduleIDTable, i64 0, i64 0), align 8, !dbg !134, !invariant.load !5 - %rawSym.i = tail call i64 @rb_id2sym(i64 %rubyId_plus.i) #17, !dbg !134 - tail call void @sorbet_vm_register_sig(i64 noundef 0, i64 %rawSym.i, i64 %2, i64 noundef 8, i64 (i64, i64, i32, i64*, i64)* noundef @"func_.13$block_1") #17, !dbg !134 - store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 12), i64** %15, align 8, !dbg !134, !tbaa !72 - %rubyId_minus.i = load i64, i64* getelementptr inbounds ([21 x i64], [21 x i64]* @sorbet_moduleIDTable, i64 0, i64 2), align 8, !dbg !135, !invariant.load !5 - %rawSym257.i = tail call i64 @rb_id2sym(i64 %rubyId_minus.i) #17, !dbg !135 - tail call void @sorbet_vm_register_sig(i64 noundef 0, i64 %rawSym257.i, i64 %2, i64 noundef 8, i64 (i64, i64, i32, i64*, i64)* noundef @"func_.13$block_2") #17, !dbg !135 - store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 17), i64** %15, align 8, !dbg !135, !tbaa !72 - %rubyId_lt.i = load i64, i64* getelementptr inbounds ([21 x i64], [21 x i64]* @sorbet_moduleIDTable, i64 0, i64 4), align 8, !dbg !136, !invariant.load !5 - %rawSym271.i = tail call i64 @rb_id2sym(i64 %rubyId_lt.i) #17, !dbg !136 - tail call void @sorbet_vm_register_sig(i64 noundef 0, i64 %rawSym271.i, i64 %2, i64 noundef 8, i64 (i64, i64, i32, i64*, i64)* noundef @"func_.13$block_3") #17, !dbg !136 - store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 22), i64** %15, align 8, !dbg !136, !tbaa !72 - %rubyId_lte.i = load i64, i64* getelementptr inbounds ([21 x i64], [21 x i64]* @sorbet_moduleIDTable, i64 0, i64 6), align 8, !dbg !137, !invariant.load !5 - %rawSym285.i = tail call i64 @rb_id2sym(i64 %rubyId_lte.i) #17, !dbg !137 - tail call void @sorbet_vm_register_sig(i64 noundef 0, i64 %rawSym285.i, i64 %2, i64 noundef 8, i64 (i64, i64, i32, i64*, i64)* noundef @"func_.13$block_4") #17, !dbg !137 - store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %15, align 8, !dbg !137, !tbaa !72 - %16 = load i64, i64* @"guard_epoch_T::Sig", align 8, !dbg !30 - %17 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !30, !tbaa !118 - %needTakeSlowPath = icmp ne i64 %16, %17, !dbg !30 - br i1 %needTakeSlowPath, label %18, label %19, !dbg !30, !prof !120 - -18: ; preds = %entry - tail call void @"const_recompute_T::Sig"(), !dbg !30 - br label %19, !dbg !30 - -19: ; preds = %entry, %18 - %20 = load i64, i64* @"guarded_const_T::Sig", align 8, !dbg !30 - %21 = load i64, i64* @"guard_epoch_T::Sig", align 8, !dbg !30 - %22 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !30, !tbaa !118 - %guardUpdated = icmp eq i64 %21, %22, !dbg !30 - tail call void @llvm.assume(i1 %guardUpdated), !dbg !30 - %23 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !30 - %24 = load i64*, i64** %23, align 8, !dbg !30 - store i64 %2, i64* %24, align 8, !dbg !30, !tbaa !6 - %25 = getelementptr inbounds i64, i64* %24, i64 1, !dbg !30 - store i64 %20, i64* %25, align 8, !dbg !30, !tbaa !6 - %26 = getelementptr inbounds i64, i64* %25, i64 1, !dbg !30 - store i64* %26, i64** %23, align 8, !dbg !30 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_extend, i64 0), !dbg !30 - store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 8), i64** %15, align 8, !dbg !30, !tbaa !72 - %27 = load i64, i64* @rb_cObject, align 8, !dbg !121 - %stackFrame308.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Object#4plus", align 8, !dbg !121 - %28 = tail call noalias nonnull i8* @ruby_xcalloc(i64 noundef 1, i64 noundef 64) #18, !dbg !121 - %29 = bitcast i8* %28 to i16*, !dbg !121 - %30 = load i16, i16* %29, align 8, !dbg !121 - %31 = and i16 %30, -384, !dbg !121 - %32 = or i16 %31, 1, !dbg !121 - store i16 %32, i16* %29, align 8, !dbg !121 - %33 = getelementptr inbounds i8, i8* %28, i64 8, !dbg !121 - %34 = bitcast i8* %33 to i32*, !dbg !121 - store i32 2, i32* %34, align 8, !dbg !121, !tbaa !138 - %35 = getelementptr inbounds i8, i8* %28, i64 12, !dbg !121 - %36 = getelementptr inbounds i8, i8* %28, i64 4, !dbg !121 - %37 = bitcast i8* %36 to i32*, !dbg !121 - tail call void @llvm.memset.p0i8.i64(i8* nonnull align 4 %35, i8 0, i64 20, i1 false) #17, !dbg !121 - store i32 2, i32* %37, align 4, !dbg !121, !tbaa !141 - %rubyId_x.i = load i64, i64* getelementptr inbounds ([21 x i64], [21 x i64]* @sorbet_moduleIDTable, i64 0, i64 11), align 8, !dbg !121, !invariant.load !5 - store i64 %rubyId_x.i, i64* %positional_table.i, align 8, !dbg !121 - %rubyId_y.i = load i64, i64* getelementptr inbounds ([21 x i64], [21 x i64]* @sorbet_moduleIDTable, i64 0, i64 12), align 8, !dbg !121, !invariant.load !5 - %38 = getelementptr i64, i64* %positional_table.i, i32 1, !dbg !121 - store i64 %rubyId_y.i, i64* %38, align 8, !dbg !121 - %39 = tail call noalias nonnull i8* @ruby_xmalloc2(i64 noundef 2, i64 noundef 8) #18, !dbg !121 - call void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture nonnull writeonly align 1 %39, i8* nocapture noundef nonnull readonly align 8 dereferenceable(16) %6, i64 noundef 16, i1 noundef false) #17, !dbg !121 - %40 = getelementptr inbounds i8, i8* %28, i64 32, !dbg !121 - %41 = bitcast i8* %40 to i8**, !dbg !121 - store i8* %39, i8** %41, align 8, !dbg !121, !tbaa !142 - tail call void @sorbet_vm_define_method(i64 %27, i8* noundef getelementptr inbounds ([309 x i8], [309 x i8]* @sorbet_moduleStringTable, i64 0, i64 0), i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)* noundef @"func_Object#4plus", i8* nonnull %28, %struct.rb_iseq_struct* %stackFrame308.i, i1 noundef zeroext false) #17, !dbg !121 - store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 13), i64** %15, align 8, !dbg !121, !tbaa !72 - %stackFrame318.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Object#5minus", align 8, !dbg !122 - %42 = tail call noalias nonnull i8* @ruby_xcalloc(i64 noundef 1, i64 noundef 64) #18, !dbg !122 - %43 = bitcast i8* %42 to i16*, !dbg !122 - %44 = load i16, i16* %43, align 8, !dbg !122 - %45 = and i16 %44, -384, !dbg !122 - %46 = or i16 %45, 1, !dbg !122 - store i16 %46, i16* %43, align 8, !dbg !122 - %47 = getelementptr inbounds i8, i8* %42, i64 8, !dbg !122 - %48 = bitcast i8* %47 to i32*, !dbg !122 - store i32 2, i32* %48, align 8, !dbg !122, !tbaa !138 - %49 = getelementptr inbounds i8, i8* %42, i64 12, !dbg !122 - %50 = getelementptr inbounds i8, i8* %42, i64 4, !dbg !122 - %51 = bitcast i8* %50 to i32*, !dbg !122 - tail call void @llvm.memset.p0i8.i64(i8* nonnull align 4 %49, i8 0, i64 20, i1 false) #17, !dbg !122 - store i32 2, i32* %51, align 4, !dbg !122, !tbaa !141 - store i64 %rubyId_x.i, i64* %positional_table320.i, align 8, !dbg !122 - %52 = getelementptr i64, i64* %positional_table320.i, i32 1, !dbg !122 - store i64 %rubyId_y.i, i64* %52, align 8, !dbg !122 - %53 = tail call noalias nonnull i8* @ruby_xmalloc2(i64 noundef 2, i64 noundef 8) #18, !dbg !122 - call void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture nonnull writeonly align 1 %53, i8* nocapture noundef nonnull readonly align 8 dereferenceable(16) %7, i64 noundef 16, i1 noundef false) #17, !dbg !122 - %54 = getelementptr inbounds i8, i8* %42, i64 32, !dbg !122 - %55 = bitcast i8* %54 to i8**, !dbg !122 - store i8* %53, i8** %55, align 8, !dbg !122, !tbaa !142 - tail call void @sorbet_vm_define_method(i64 %27, i8* getelementptr inbounds ([309 x i8], [309 x i8]* @sorbet_moduleStringTable, i64 0, i64 107), i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)* noundef @"func_Object#5minus", i8* nonnull %42, %struct.rb_iseq_struct* %stackFrame318.i, i1 noundef zeroext false) #17, !dbg !122 - store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 18), i64** %15, align 8, !dbg !122, !tbaa !72 - %stackFrame332.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Object#2lt", align 8, !dbg !123 - %56 = tail call noalias nonnull i8* @ruby_xcalloc(i64 noundef 1, i64 noundef 64) #18, !dbg !123 - %57 = bitcast i8* %56 to i16*, !dbg !123 - %58 = load i16, i16* %57, align 8, !dbg !123 - %59 = and i16 %58, -384, !dbg !123 - %60 = or i16 %59, 1, !dbg !123 - store i16 %60, i16* %57, align 8, !dbg !123 - %61 = getelementptr inbounds i8, i8* %56, i64 8, !dbg !123 - %62 = bitcast i8* %61 to i32*, !dbg !123 - store i32 2, i32* %62, align 8, !dbg !123, !tbaa !138 - %63 = getelementptr inbounds i8, i8* %56, i64 12, !dbg !123 - %64 = getelementptr inbounds i8, i8* %56, i64 4, !dbg !123 - %65 = bitcast i8* %64 to i32*, !dbg !123 - tail call void @llvm.memset.p0i8.i64(i8* nonnull align 4 %63, i8 0, i64 20, i1 false) #17, !dbg !123 - store i32 2, i32* %65, align 4, !dbg !123, !tbaa !141 - store i64 %rubyId_x.i, i64* %positional_table334.i, align 8, !dbg !123 - %66 = getelementptr i64, i64* %positional_table334.i, i32 1, !dbg !123 - store i64 %rubyId_y.i, i64* %66, align 8, !dbg !123 - %67 = tail call noalias nonnull i8* @ruby_xmalloc2(i64 noundef 2, i64 noundef 8) #18, !dbg !123 - call void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture nonnull writeonly align 1 %67, i8* nocapture noundef nonnull readonly align 8 dereferenceable(16) %8, i64 noundef 16, i1 noundef false) #17, !dbg !123 - %68 = getelementptr inbounds i8, i8* %56, i64 32, !dbg !123 - %69 = bitcast i8* %68 to i8**, !dbg !123 - store i8* %67, i8** %69, align 8, !dbg !123, !tbaa !142 - tail call void @sorbet_vm_define_method(i64 %27, i8* getelementptr inbounds ([309 x i8], [309 x i8]* @sorbet_moduleStringTable, i64 0, i64 115), i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)* noundef @"func_Object#2lt", i8* nonnull %56, %struct.rb_iseq_struct* %stackFrame332.i, i1 noundef zeroext false) #17, !dbg !123 - store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 23), i64** %15, align 8, !dbg !123, !tbaa !72 - %stackFrame346.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Object#3lte", align 8, !dbg !124 - %70 = tail call noalias nonnull i8* @ruby_xcalloc(i64 noundef 1, i64 noundef 64) #18, !dbg !124 - %71 = bitcast i8* %70 to i16*, !dbg !124 - %72 = load i16, i16* %71, align 8, !dbg !124 - %73 = and i16 %72, -384, !dbg !124 - %74 = or i16 %73, 1, !dbg !124 - store i16 %74, i16* %71, align 8, !dbg !124 - %75 = getelementptr inbounds i8, i8* %70, i64 8, !dbg !124 - %76 = bitcast i8* %75 to i32*, !dbg !124 - store i32 2, i32* %76, align 8, !dbg !124, !tbaa !138 - %77 = getelementptr inbounds i8, i8* %70, i64 12, !dbg !124 - %78 = getelementptr inbounds i8, i8* %70, i64 4, !dbg !124 - %79 = bitcast i8* %78 to i32*, !dbg !124 - tail call void @llvm.memset.p0i8.i64(i8* nonnull align 4 %77, i8 0, i64 20, i1 false) #17, !dbg !124 - store i32 2, i32* %79, align 4, !dbg !124, !tbaa !141 - store i64 %rubyId_x.i, i64* %positional_table348.i, align 8, !dbg !124 - %80 = getelementptr i64, i64* %positional_table348.i, i32 1, !dbg !124 - store i64 %rubyId_y.i, i64* %80, align 8, !dbg !124 - %81 = tail call noalias nonnull i8* @ruby_xmalloc2(i64 noundef 2, i64 noundef 8) #18, !dbg !124 - call void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture nonnull writeonly align 1 %81, i8* nocapture noundef nonnull readonly align 8 dereferenceable(16) %9, i64 noundef 16, i1 noundef false) #17, !dbg !124 - %82 = getelementptr inbounds i8, i8* %70, i64 32, !dbg !124 - %83 = bitcast i8* %82 to i8**, !dbg !124 - store i8* %81, i8** %83, align 8, !dbg !124, !tbaa !142 - tail call void @sorbet_vm_define_method(i64 %27, i8* getelementptr inbounds ([309 x i8], [309 x i8]* @sorbet_moduleStringTable, i64 0, i64 131), i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)* noundef @"func_Object#3lte", i8* nonnull %70, %struct.rb_iseq_struct* %stackFrame346.i, i1 noundef zeroext false) #17, !dbg !124 - store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 27), i64** %15, align 8, !dbg !124, !tbaa !72 - %84 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !31 - %85 = load i64*, i64** %84, align 8, !dbg !31 - store i64 %2, i64* %85, align 8, !dbg !31, !tbaa !6 - %86 = getelementptr inbounds i64, i64* %85, i64 1, !dbg !31 - %87 = getelementptr inbounds i64, i64* %86, i64 1, !dbg !31 - %88 = bitcast i64* %86 to <2 x i64>*, !dbg !31 - store <2 x i64> , <2 x i64>* %88, align 8, !dbg !31, !tbaa !6 - %89 = getelementptr inbounds i64, i64* %87, i64 1, !dbg !31 - store i64* %89, i64** %84, align 8, !dbg !31 - %send2 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_plus, i64 0), !dbg !31 - %90 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !32 - %91 = load i64*, i64** %90, align 8, !dbg !32 - store i64 %2, i64* %91, align 8, !dbg !32, !tbaa !6 - %92 = getelementptr inbounds i64, i64* %91, i64 1, !dbg !32 - store i64 %send2, i64* %92, align 8, !dbg !32, !tbaa !6 - %93 = getelementptr inbounds i64, i64* %92, i64 1, !dbg !32 - store i64* %93, i64** %90, align 8, !dbg !32 - %send4 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_p, i64 0), !dbg !32 - store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 28), i64** %15, align 8, !dbg !32, !tbaa !72 - %94 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !33 - %95 = load i64*, i64** %94, align 8, !dbg !33 - store i64 %2, i64* %95, align 8, !dbg !33, !tbaa !6 - %96 = getelementptr inbounds i64, i64* %95, i64 1, !dbg !33 - %97 = getelementptr inbounds i64, i64* %96, i64 1, !dbg !33 - %98 = bitcast i64* %96 to <2 x i64>*, !dbg !33 - store <2 x i64> , <2 x i64>* %98, align 8, !dbg !33, !tbaa !6 - %99 = getelementptr inbounds i64, i64* %97, i64 1, !dbg !33 - store i64* %99, i64** %94, align 8, !dbg !33 - %send6 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_minus, i64 0), !dbg !33 - %100 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !34 - %101 = load i64*, i64** %100, align 8, !dbg !34 - store i64 %2, i64* %101, align 8, !dbg !34, !tbaa !6 - %102 = getelementptr inbounds i64, i64* %101, i64 1, !dbg !34 - store i64 %send6, i64* %102, align 8, !dbg !34, !tbaa !6 - %103 = getelementptr inbounds i64, i64* %102, i64 1, !dbg !34 - store i64* %103, i64** %100, align 8, !dbg !34 - %send8 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_p.12, i64 0), !dbg !34 - store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 29), i64** %15, align 8, !dbg !34, !tbaa !72 - %104 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !35 - %105 = load i64*, i64** %104, align 8, !dbg !35 - store i64 %2, i64* %105, align 8, !dbg !35, !tbaa !6 - %106 = getelementptr inbounds i64, i64* %105, i64 1, !dbg !35 - %107 = getelementptr inbounds i64, i64* %106, i64 1, !dbg !35 - %108 = bitcast i64* %106 to <2 x i64>*, !dbg !35 - store <2 x i64> , <2 x i64>* %108, align 8, !dbg !35, !tbaa !6 - %109 = getelementptr inbounds i64, i64* %107, i64 1, !dbg !35 - store i64* %109, i64** %104, align 8, !dbg !35 - %send10 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_lt, i64 0), !dbg !35 - %110 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !36 - %111 = load i64*, i64** %110, align 8, !dbg !36 - store i64 %2, i64* %111, align 8, !dbg !36, !tbaa !6 - %112 = getelementptr inbounds i64, i64* %111, i64 1, !dbg !36 - store i64 %send10, i64* %112, align 8, !dbg !36, !tbaa !6 - %113 = getelementptr inbounds i64, i64* %112, i64 1, !dbg !36 - store i64* %113, i64** %110, align 8, !dbg !36 - %send12 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_p.13, i64 0), !dbg !36 - store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 30), i64** %15, align 8, !dbg !36, !tbaa !72 - %114 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !37 - %115 = load i64*, i64** %114, align 8, !dbg !37 - store i64 %2, i64* %115, align 8, !dbg !37, !tbaa !6 - %116 = getelementptr inbounds i64, i64* %115, i64 1, !dbg !37 - %117 = getelementptr inbounds i64, i64* %116, i64 1, !dbg !37 - %118 = bitcast i64* %116 to <2 x i64>*, !dbg !37 - store <2 x i64> , <2 x i64>* %118, align 8, !dbg !37, !tbaa !6 - %119 = getelementptr inbounds i64, i64* %117, i64 1, !dbg !37 - store i64* %119, i64** %114, align 8, !dbg !37 - %send14 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_lt.14, i64 0), !dbg !37 - %120 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !38 - %121 = load i64*, i64** %120, align 8, !dbg !38 - store i64 %2, i64* %121, align 8, !dbg !38, !tbaa !6 - %122 = getelementptr inbounds i64, i64* %121, i64 1, !dbg !38 - store i64 %send14, i64* %122, align 8, !dbg !38, !tbaa !6 - %123 = getelementptr inbounds i64, i64* %122, i64 1, !dbg !38 - store i64* %123, i64** %120, align 8, !dbg !38 - %send16 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_p.15, i64 0), !dbg !38 - store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 31), i64** %15, align 8, !dbg !38, !tbaa !72 - %124 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !39 - %125 = load i64*, i64** %124, align 8, !dbg !39 - store i64 %2, i64* %125, align 8, !dbg !39, !tbaa !6 - %126 = getelementptr inbounds i64, i64* %125, i64 1, !dbg !39 - %127 = getelementptr inbounds i64, i64* %126, i64 1, !dbg !39 - %128 = bitcast i64* %126 to <2 x i64>*, !dbg !39 - store <2 x i64> , <2 x i64>* %128, align 8, !dbg !39, !tbaa !6 - %129 = getelementptr inbounds i64, i64* %127, i64 1, !dbg !39 - store i64* %129, i64** %124, align 8, !dbg !39 - %send18 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_lte, i64 0), !dbg !39 - %130 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !40 - %131 = load i64*, i64** %130, align 8, !dbg !40 - store i64 %2, i64* %131, align 8, !dbg !40, !tbaa !6 - %132 = getelementptr inbounds i64, i64* %131, i64 1, !dbg !40 - store i64 %send18, i64* %132, align 8, !dbg !40, !tbaa !6 - %133 = getelementptr inbounds i64, i64* %132, i64 1, !dbg !40 - store i64* %133, i64** %130, align 8, !dbg !40 - %send20 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_p.16, i64 0), !dbg !40 - store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 32), i64** %15, align 8, !dbg !40, !tbaa !72 - %134 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !41 - %135 = load i64*, i64** %134, align 8, !dbg !41 - store i64 %2, i64* %135, align 8, !dbg !41, !tbaa !6 - %136 = getelementptr inbounds i64, i64* %135, i64 1, !dbg !41 - %137 = getelementptr inbounds i64, i64* %136, i64 1, !dbg !41 - %138 = bitcast i64* %136 to <2 x i64>*, !dbg !41 - store <2 x i64> , <2 x i64>* %138, align 8, !dbg !41, !tbaa !6 - %139 = getelementptr inbounds i64, i64* %137, i64 1, !dbg !41 - store i64* %139, i64** %134, align 8, !dbg !41 - %send22 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_lte.17, i64 0), !dbg !41 - %140 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !42 - %141 = load i64*, i64** %140, align 8, !dbg !42 - store i64 %2, i64* %141, align 8, !dbg !42, !tbaa !6 - %142 = getelementptr inbounds i64, i64* %141, i64 1, !dbg !42 - store i64 %send22, i64* %142, align 8, !dbg !42, !tbaa !6 - %143 = getelementptr inbounds i64, i64* %142, i64 1, !dbg !42 - store i64* %143, i64** %140, align 8, !dbg !42 - %send24 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_p.18, i64 0), !dbg !42 - store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 33), i64** %15, align 8, !dbg !42, !tbaa !72 - %144 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !43 - %145 = load i64*, i64** %144, align 8, !dbg !43 - store i64 %2, i64* %145, align 8, !dbg !43, !tbaa !6 - %146 = getelementptr inbounds i64, i64* %145, i64 1, !dbg !43 - %147 = getelementptr inbounds i64, i64* %146, i64 1, !dbg !43 - %148 = bitcast i64* %146 to <2 x i64>*, !dbg !43 - store <2 x i64> , <2 x i64>* %148, align 8, !dbg !43, !tbaa !6 - %149 = getelementptr inbounds i64, i64* %147, i64 1, !dbg !43 - store i64* %149, i64** %144, align 8, !dbg !43 - %send26 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_lte.19, i64 0), !dbg !43 - %150 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !44 - %151 = load i64*, i64** %150, align 8, !dbg !44 - store i64 %2, i64* %151, align 8, !dbg !44, !tbaa !6 - %152 = getelementptr inbounds i64, i64* %151, i64 1, !dbg !44 - store i64 %send26, i64* %152, align 8, !dbg !44, !tbaa !6 - %153 = getelementptr inbounds i64, i64* %152, i64 1, !dbg !44 - store i64* %153, i64** %150, align 8, !dbg !44 - %send28 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_p.20, i64 0), !dbg !44 - store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 36), i64** %15, align 8, !dbg !44, !tbaa !72 - %154 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !45 - %155 = load i64*, i64** %154, align 8, !dbg !45 - store i64 %2, i64* %155, align 8, !dbg !45, !tbaa !6 - %156 = getelementptr inbounds i64, i64* %155, i64 1, !dbg !45 - %157 = getelementptr inbounds i64, i64* %156, i64 1, !dbg !45 - %158 = bitcast i64* %156 to <2 x i64>*, !dbg !45 - store <2 x i64> , <2 x i64>* %158, align 8, !dbg !45, !tbaa !6 - %159 = getelementptr inbounds i64, i64* %157, i64 1, !dbg !45 - store i64* %159, i64** %154, align 8, !dbg !45 - %send30 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_plus.21, i64 0), !dbg !45 - %160 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !46 - %161 = load i64*, i64** %160, align 8, !dbg !46 - store i64 %2, i64* %161, align 8, !dbg !46, !tbaa !6 - %162 = getelementptr inbounds i64, i64* %161, i64 1, !dbg !46 - store i64 %send30, i64* %162, align 8, !dbg !46, !tbaa !6 - %163 = getelementptr inbounds i64, i64* %162, i64 1, !dbg !46 - store i64* %163, i64** %160, align 8, !dbg !46 - %send32 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_p.22, i64 0), !dbg !46 - store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 37), i64** %15, align 8, !dbg !46, !tbaa !72 - %rubyStr_8.9.i = load i64, i64* getelementptr inbounds ([13 x i64], [13 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 7), align 8, !dbg !47, !invariant.load !5 - %164 = load i64, i64* @rb_mKernel, align 8, !dbg !47 - %165 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !47 - %166 = load i64*, i64** %165, align 8, !dbg !47 - store i64 %164, i64* %166, align 8, !dbg !47, !tbaa !6 - %167 = getelementptr inbounds i64, i64* %166, i64 1, !dbg !47 - store i64 %rubyStr_8.9.i, i64* %167, align 8, !dbg !47, !tbaa !6 - %168 = getelementptr inbounds i64, i64* %167, i64 1, !dbg !47 - store i64* %168, i64** %165, align 8, !dbg !47 - %send34 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_Rational, i64 0), !dbg !47 - %169 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !48 - %170 = load i64*, i64** %169, align 8, !dbg !48 - store i64 %2, i64* %170, align 8, !dbg !48, !tbaa !6 - %171 = getelementptr inbounds i64, i64* %170, i64 1, !dbg !48 - store i64 36028797018963970, i64* %171, align 8, !dbg !48, !tbaa !6 - %172 = getelementptr inbounds i64, i64* %171, i64 1, !dbg !48 - store i64 %send34, i64* %172, align 8, !dbg !48, !tbaa !6 - %173 = getelementptr inbounds i64, i64* %172, i64 1, !dbg !48 - store i64* %173, i64** %169, align 8, !dbg !48 - %send36 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_plus.23, i64 0), !dbg !48 - %174 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !49 - %175 = load i64*, i64** %174, align 8, !dbg !49 - store i64 %2, i64* %175, align 8, !dbg !49, !tbaa !6 - %176 = getelementptr inbounds i64, i64* %175, i64 1, !dbg !49 - store i64 %send36, i64* %176, align 8, !dbg !49, !tbaa !6 - %177 = getelementptr inbounds i64, i64* %176, i64 1, !dbg !49 - store i64* %177, i64** %174, align 8, !dbg !49 - %send38 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_p.24, i64 0), !dbg !49 - store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 38), i64** %15, align 8, !dbg !49, !tbaa !72 - %rubyStr_5.i = load i64, i64* getelementptr inbounds ([13 x i64], [13 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 8), align 8, !dbg !50, !invariant.load !5 - %178 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !50 - %179 = load i64*, i64** %178, align 8, !dbg !50 - store i64 %164, i64* %179, align 8, !dbg !50, !tbaa !6 - %180 = getelementptr inbounds i64, i64* %179, i64 1, !dbg !50 - store i64 1, i64* %180, align 8, !dbg !50, !tbaa !6 - %181 = getelementptr inbounds i64, i64* %180, i64 1, !dbg !50 - store i64 %rubyStr_5.i, i64* %181, align 8, !dbg !50, !tbaa !6 - %182 = getelementptr inbounds i64, i64* %181, i64 1, !dbg !50 - store i64* %182, i64** %178, align 8, !dbg !50 - %send40 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_Complex, i64 0), !dbg !50 - %183 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !51 - %184 = load i64*, i64** %183, align 8, !dbg !51 - store i64 %2, i64* %184, align 8, !dbg !51, !tbaa !6 - %185 = getelementptr inbounds i64, i64* %184, i64 1, !dbg !51 - store i64 36028797018963970, i64* %185, align 8, !dbg !51, !tbaa !6 - %186 = getelementptr inbounds i64, i64* %185, i64 1, !dbg !51 - store i64 %send40, i64* %186, align 8, !dbg !51, !tbaa !6 - %187 = getelementptr inbounds i64, i64* %186, i64 1, !dbg !51 - store i64* %187, i64** %183, align 8, !dbg !51 - %send42 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_plus.25, i64 0), !dbg !51 - %188 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !52 - %189 = load i64*, i64** %188, align 8, !dbg !52 - store i64 %2, i64* %189, align 8, !dbg !52, !tbaa !6 - %190 = getelementptr inbounds i64, i64* %189, i64 1, !dbg !52 - store i64 %send42, i64* %190, align 8, !dbg !52, !tbaa !6 - %191 = getelementptr inbounds i64, i64* %190, i64 1, !dbg !52 - store i64* %191, i64** %188, align 8, !dbg !52 - %send44 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_p.26, i64 0), !dbg !52 - store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 40), i64** %15, align 8, !dbg !52, !tbaa !72 - %192 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !53 - %193 = load i64*, i64** %192, align 8, !dbg !53 - store i64 %2, i64* %193, align 8, !dbg !53, !tbaa !6 - %194 = getelementptr inbounds i64, i64* %193, i64 1, !dbg !53 - %195 = getelementptr inbounds i64, i64* %194, i64 1, !dbg !53 - %196 = bitcast i64* %194 to <2 x i64>*, !dbg !53 - store <2 x i64> , <2 x i64>* %196, align 8, !dbg !53, !tbaa !6 - %197 = getelementptr inbounds i64, i64* %195, i64 1, !dbg !53 - store i64* %197, i64** %192, align 8, !dbg !53 - %send46 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_minus.27, i64 0), !dbg !53 - %198 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !54 - %199 = load i64*, i64** %198, align 8, !dbg !54 - store i64 %2, i64* %199, align 8, !dbg !54, !tbaa !6 - %200 = getelementptr inbounds i64, i64* %199, i64 1, !dbg !54 - store i64 %send46, i64* %200, align 8, !dbg !54, !tbaa !6 - %201 = getelementptr inbounds i64, i64* %200, i64 1, !dbg !54 - store i64* %201, i64** %198, align 8, !dbg !54 - %send48 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_p.28, i64 0), !dbg !54 - store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 41), i64** %15, align 8, !dbg !54, !tbaa !72 - %rubyStr_15.4.i = load i64, i64* getelementptr inbounds ([13 x i64], [13 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 9), align 8, !dbg !55, !invariant.load !5 - %202 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !55 - %203 = load i64*, i64** %202, align 8, !dbg !55 - store i64 %164, i64* %203, align 8, !dbg !55, !tbaa !6 - %204 = getelementptr inbounds i64, i64* %203, i64 1, !dbg !55 - store i64 %rubyStr_15.4.i, i64* %204, align 8, !dbg !55, !tbaa !6 - %205 = getelementptr inbounds i64, i64* %204, i64 1, !dbg !55 - store i64* %205, i64** %202, align 8, !dbg !55 - %send50 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_Rational.29, i64 0), !dbg !55 - %206 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !56 - %207 = load i64*, i64** %206, align 8, !dbg !56 - store i64 %2, i64* %207, align 8, !dbg !56, !tbaa !6 - %208 = getelementptr inbounds i64, i64* %207, i64 1, !dbg !56 - store i64 199622053483197234, i64* %208, align 8, !dbg !56, !tbaa !6 - %209 = getelementptr inbounds i64, i64* %208, i64 1, !dbg !56 - store i64 %send50, i64* %209, align 8, !dbg !56, !tbaa !6 - %210 = getelementptr inbounds i64, i64* %209, i64 1, !dbg !56 - store i64* %210, i64** %206, align 8, !dbg !56 - %send52 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_minus.30, i64 0), !dbg !56 - %211 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !57 - %212 = load i64*, i64** %211, align 8, !dbg !57 - store i64 %2, i64* %212, align 8, !dbg !57, !tbaa !6 - %213 = getelementptr inbounds i64, i64* %212, i64 1, !dbg !57 - store i64 %send52, i64* %213, align 8, !dbg !57, !tbaa !6 - %214 = getelementptr inbounds i64, i64* %213, i64 1, !dbg !57 - store i64* %214, i64** %211, align 8, !dbg !57 - %send54 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_p.31, i64 0), !dbg !57 - store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 42), i64** %15, align 8, !dbg !57, !tbaa !72 - %rubyStr_18.i = load i64, i64* getelementptr inbounds ([13 x i64], [13 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 10), align 8, !dbg !58, !invariant.load !5 - %215 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !58 - %216 = load i64*, i64** %215, align 8, !dbg !58 - store i64 %164, i64* %216, align 8, !dbg !58, !tbaa !6 - %217 = getelementptr inbounds i64, i64* %216, i64 1, !dbg !58 - store i64 1, i64* %217, align 8, !dbg !58, !tbaa !6 - %218 = getelementptr inbounds i64, i64* %217, i64 1, !dbg !58 - store i64 %rubyStr_18.i, i64* %218, align 8, !dbg !58, !tbaa !6 - %219 = getelementptr inbounds i64, i64* %218, i64 1, !dbg !58 - store i64* %219, i64** %215, align 8, !dbg !58 - %send56 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_Complex.32, i64 0), !dbg !58 - %220 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !59 - %221 = load i64*, i64** %220, align 8, !dbg !59 - store i64 %2, i64* %221, align 8, !dbg !59, !tbaa !6 - %222 = getelementptr inbounds i64, i64* %221, i64 1, !dbg !59 - store i64 199565758487855106, i64* %222, align 8, !dbg !59, !tbaa !6 - %223 = getelementptr inbounds i64, i64* %222, i64 1, !dbg !59 - store i64 %send56, i64* %223, align 8, !dbg !59, !tbaa !6 - %224 = getelementptr inbounds i64, i64* %223, i64 1, !dbg !59 - store i64* %224, i64** %220, align 8, !dbg !59 - %send58 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_minus.33, i64 0), !dbg !59 - %225 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !60 - %226 = load i64*, i64** %225, align 8, !dbg !60 - store i64 %2, i64* %226, align 8, !dbg !60, !tbaa !6 - %227 = getelementptr inbounds i64, i64* %226, i64 1, !dbg !60 - store i64 %send58, i64* %227, align 8, !dbg !60, !tbaa !6 - %228 = getelementptr inbounds i64, i64* %227, i64 1, !dbg !60 - store i64* %228, i64** %225, align 8, !dbg !60 - %send60 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_p.34, i64 0), !dbg !60 - store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 44), i64** %15, align 8, !dbg !60, !tbaa !72 - %229 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !61 - %230 = load i64*, i64** %229, align 8, !dbg !61 - store i64 %2, i64* %230, align 8, !dbg !61, !tbaa !6 - %231 = getelementptr inbounds i64, i64* %230, i64 1, !dbg !61 - %232 = getelementptr inbounds i64, i64* %231, i64 1, !dbg !61 - %233 = bitcast i64* %231 to <2 x i64>*, !dbg !61 - store <2 x i64> , <2 x i64>* %233, align 8, !dbg !61, !tbaa !6 - %234 = getelementptr inbounds i64, i64* %232, i64 1, !dbg !61 - store i64* %234, i64** %229, align 8, !dbg !61 - %send62 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_lt.35, i64 0), !dbg !61 - %235 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !62 - %236 = load i64*, i64** %235, align 8, !dbg !62 - store i64 %2, i64* %236, align 8, !dbg !62, !tbaa !6 - %237 = getelementptr inbounds i64, i64* %236, i64 1, !dbg !62 - store i64 %send62, i64* %237, align 8, !dbg !62, !tbaa !6 - %238 = getelementptr inbounds i64, i64* %237, i64 1, !dbg !62 - store i64* %238, i64** %235, align 8, !dbg !62 - %send64 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_p.36, i64 0), !dbg !62 - store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 45), i64** %15, align 8, !dbg !62, !tbaa !72 - %rubyStr_25.4.i = load i64, i64* getelementptr inbounds ([13 x i64], [13 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 11), align 8, !dbg !63, !invariant.load !5 - %239 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !63 - %240 = load i64*, i64** %239, align 8, !dbg !63 - store i64 %164, i64* %240, align 8, !dbg !63, !tbaa !6 - %241 = getelementptr inbounds i64, i64* %240, i64 1, !dbg !63 - store i64 %rubyStr_25.4.i, i64* %241, align 8, !dbg !63, !tbaa !6 - %242 = getelementptr inbounds i64, i64* %241, i64 1, !dbg !63 - store i64* %242, i64** %239, align 8, !dbg !63 - %send66 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_Rational.37, i64 0), !dbg !63 - %243 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !64 - %244 = load i64*, i64** %243, align 8, !dbg !64 - store i64 %2, i64* %244, align 8, !dbg !64, !tbaa !6 - %245 = getelementptr inbounds i64, i64* %244, i64 1, !dbg !64 - store i64 113040350646999450, i64* %245, align 8, !dbg !64, !tbaa !6 - %246 = getelementptr inbounds i64, i64* %245, i64 1, !dbg !64 - store i64 %send66, i64* %246, align 8, !dbg !64, !tbaa !6 - %247 = getelementptr inbounds i64, i64* %246, i64 1, !dbg !64 - store i64* %247, i64** %243, align 8, !dbg !64 - %send68 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_lt.38, i64 0), !dbg !64 - %248 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !65 - %249 = load i64*, i64** %248, align 8, !dbg !65 - store i64 %2, i64* %249, align 8, !dbg !65, !tbaa !6 - %250 = getelementptr inbounds i64, i64* %249, i64 1, !dbg !65 - store i64 %send68, i64* %250, align 8, !dbg !65, !tbaa !6 - %251 = getelementptr inbounds i64, i64* %250, i64 1, !dbg !65 - store i64* %251, i64** %248, align 8, !dbg !65 - %send70 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_p.39, i64 0), !dbg !65 - store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 47), i64** %15, align 8, !dbg !65, !tbaa !72 - %252 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !66 - %253 = load i64*, i64** %252, align 8, !dbg !66 - store i64 %2, i64* %253, align 8, !dbg !66, !tbaa !6 - %254 = getelementptr inbounds i64, i64* %253, i64 1, !dbg !66 - %255 = getelementptr inbounds i64, i64* %254, i64 1, !dbg !66 - %256 = bitcast i64* %254 to <2 x i64>*, !dbg !66 - store <2 x i64> , <2 x i64>* %256, align 8, !dbg !66, !tbaa !6 - %257 = getelementptr inbounds i64, i64* %255, i64 1, !dbg !66 - store i64* %257, i64** %252, align 8, !dbg !66 - %send72 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_lte.40, i64 0), !dbg !66 - %258 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !67 - %259 = load i64*, i64** %258, align 8, !dbg !67 - store i64 %2, i64* %259, align 8, !dbg !67, !tbaa !6 - %260 = getelementptr inbounds i64, i64* %259, i64 1, !dbg !67 - store i64 %send72, i64* %260, align 8, !dbg !67, !tbaa !6 - %261 = getelementptr inbounds i64, i64* %260, i64 1, !dbg !67 - store i64* %261, i64** %258, align 8, !dbg !67 - %send74 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_p.41, i64 0), !dbg !67 - store i64* getelementptr inbounds ([49 x i64], [49 x i64]* @iseqEncodedArray, i64 0, i64 48), i64** %15, align 8, !dbg !67, !tbaa !72 - %rubyStr_5.923.i = load i64, i64* getelementptr inbounds ([13 x i64], [13 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 12), align 8, !dbg !68, !invariant.load !5 - %262 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !68 - %263 = load i64*, i64** %262, align 8, !dbg !68 - store i64 %164, i64* %263, align 8, !dbg !68, !tbaa !6 - %264 = getelementptr inbounds i64, i64* %263, i64 1, !dbg !68 - store i64 %rubyStr_5.923.i, i64* %264, align 8, !dbg !68, !tbaa !6 - %265 = getelementptr inbounds i64, i64* %264, i64 1, !dbg !68 - store i64* %265, i64** %262, align 8, !dbg !68 - %send76 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_Rational.42, i64 0), !dbg !68 - %266 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !69 - %267 = load i64*, i64** %266, align 8, !dbg !69 - store i64 %2, i64* %267, align 8, !dbg !69, !tbaa !6 - %268 = getelementptr inbounds i64, i64* %267, i64 1, !dbg !69 - store i64 113040350646999450, i64* %268, align 8, !dbg !69, !tbaa !6 - %269 = getelementptr inbounds i64, i64* %268, i64 1, !dbg !69 - store i64 %send76, i64* %269, align 8, !dbg !69, !tbaa !6 - %270 = getelementptr inbounds i64, i64* %269, i64 1, !dbg !69 - store i64* %270, i64** %266, align 8, !dbg !69 - %send78 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_lte.43, i64 0), !dbg !69 - %271 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !70 - %272 = load i64*, i64** %271, align 8, !dbg !70 - store i64 %2, i64* %272, align 8, !dbg !70, !tbaa !6 - %273 = getelementptr inbounds i64, i64* %272, i64 1, !dbg !70 - store i64 %send78, i64* %273, align 8, !dbg !70, !tbaa !6 - %274 = getelementptr inbounds i64, i64* %273, i64 1, !dbg !70 - store i64* %274, i64** %271, align 8, !dbg !70 - %send80 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_p.44, i64 0), !dbg !70 - call void @llvm.lifetime.end.p0i8(i64 16, i8* nonnull %6) - call void @llvm.lifetime.end.p0i8(i64 16, i8* nonnull %7) - call void @llvm.lifetime.end.p0i8(i64 16, i8* nonnull %8) - call void @llvm.lifetime.end.p0i8(i64 16, i8* nonnull %9) - ret void -} - -; Function Attrs: inaccessiblememonly nofree nosync nounwind willreturn -declare void @llvm.experimental.noalias.scope.decl(metadata) #10 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly -declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #11 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #5 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #5 - -; Function Attrs: cold minsize noreturn nounwind sspreq uwtable -define internal fastcc void @"func_Object#2lt.cold.1"(i64 %0) unnamed_addr #12 !dbg !143 { -newFuncRoot: - tail call void @sorbet_cast_failure(i64 %0, i8* getelementptr inbounds ([309 x i8], [309 x i8]* @sorbet_moduleStringTable, i64 0, i64 94), i8* getelementptr inbounds ([309 x i8], [309 x i8]* @sorbet_moduleStringTable, i64 0, i64 120)) #15 - unreachable -} - -; Function Attrs: cold minsize noreturn nounwind sspreq uwtable -define internal fastcc void @"func_Object#2lt.cold.3"(i64 %rawArg_x) unnamed_addr #12 !dbg !145 { -newFuncRoot: - tail call void @sorbet_cast_failure(i64 %rawArg_x, i8* getelementptr inbounds ([309 x i8], [309 x i8]* @sorbet_moduleStringTable, i64 0, i64 48), i8* getelementptr inbounds ([309 x i8], [309 x i8]* @sorbet_moduleStringTable, i64 0, i64 62)) #15, !dbg !146 - unreachable, !dbg !146 -} - -; Function Attrs: nofree nosync nounwind willreturn -declare void @llvm.assume(i1 noundef) #13 - -; Function Attrs: ssp -define linkonce void @const_recompute_T() local_unnamed_addr #8 { - %1 = tail call i64 @sorbet_getConstant(i8* getelementptr inbounds ([309 x i8], [309 x i8]* @sorbet_moduleStringTable, i64 0, i64 206), i64 1) - store i64 %1, i64* @guarded_const_T, align 8 - %2 = load i64, i64* @ruby_vm_global_constant_state, align 8, !tbaa !118 - store i64 %2, i64* @guard_epoch_T, align 8 - ret void -} - -; Function Attrs: ssp -define linkonce void @"const_recompute_T::Boolean"() local_unnamed_addr #8 { - %1 = tail call i64 @sorbet_getConstant(i8* getelementptr inbounds ([309 x i8], [309 x i8]* @sorbet_moduleStringTable, i64 0, i64 120), i64 10) - store i64 %1, i64* @"guarded_const_T::Boolean", align 8 - %2 = load i64, i64* @ruby_vm_global_constant_state, align 8, !tbaa !118 - store i64 %2, i64* @"guard_epoch_T::Boolean", align 8 - ret void -} - -; Function Attrs: ssp -define linkonce void @"const_recompute_T::Sig"() local_unnamed_addr #8 { - %1 = tail call i64 @sorbet_getConstant(i8* getelementptr inbounds ([309 x i8], [309 x i8]* @sorbet_moduleStringTable, i64 0, i64 223), i64 6) - store i64 %1, i64* @"guarded_const_T::Sig", align 8 - %2 = load i64, i64* @ruby_vm_global_constant_state, align 8, !tbaa !118 - store i64 %2, i64* @"guard_epoch_T::Sig", align 8 - ret void -} - -attributes #0 = { "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #1 = { nounwind readnone willreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #2 = { cold noreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #3 = { noreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #4 = { allocsize(0,1) "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #5 = { argmemonly nofree nosync nounwind willreturn } -attributes #6 = { nounwind ssp uwtable "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #7 = { nounwind sspreq uwtable } -attributes #8 = { ssp } -attributes #9 = { sspreq } -attributes #10 = { inaccessiblememonly nofree nosync nounwind willreturn } -attributes #11 = { argmemonly nofree nosync nounwind willreturn writeonly } -attributes #12 = { cold minsize noreturn nounwind sspreq uwtable } -attributes #13 = { nofree nosync nounwind willreturn } -attributes #14 = { noreturn nounwind } -attributes #15 = { noreturn } -attributes #16 = { noinline } -attributes #17 = { nounwind } -attributes #18 = { nounwind allocsize(0,1) } - -!llvm.module.flags = !{!0, !1, !2} -!llvm.dbg.cu = !{!3} - -!0 = !{i32 2, !"Debug Info Version", i32 3} -!1 = !{i32 4, !"cf-protection-return", i32 1} -!2 = !{i32 4, !"cf-protection-branch", i32 1} -!3 = distinct !DICompileUnit(language: DW_LANG_C, file: !4, producer: "Sorbet LLVM", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !5) -!4 = !DIFile(filename: "test/testdata/compiler/float-intrinsics.rb", directory: ".") -!5 = !{} -!6 = !{!7, !7, i64 0} -!7 = !{!"long", !8, i64 0} -!8 = !{!"omnipotent char", !9, i64 0} -!9 = !{!"Simple C/C++ TBAA"} -!10 = !DILocation(line: 14, column: 3, scope: !11) -!11 = distinct !DISubprogram(name: "Object#minus", linkageName: "func_Object#5minus", scope: null, file: !4, line: 13, type: !12, scopeLine: 13, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!12 = !DISubroutineType(types: !13) -!13 = !{!14} -!14 = !DIBasicType(name: "VALUE", size: 64, encoding: DW_ATE_signed) -!15 = !DILocation(line: 7, column: 26, scope: !16) -!16 = distinct !DISubprogram(name: ".", linkageName: "func_.13$block_1", scope: !17, file: !4, line: 5, type: !12, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!17 = distinct !DISubprogram(name: ".", linkageName: "func_.13", scope: null, file: !4, line: 5, type: !12, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!18 = !DILocation(line: 7, column: 6, scope: !16) -!19 = !DILocation(line: 7, column: 45, scope: !16) -!20 = !DILocation(line: 12, column: 26, scope: !21) -!21 = distinct !DISubprogram(name: ".", linkageName: "func_.13$block_2", scope: !17, file: !4, line: 5, type: !12, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!22 = !DILocation(line: 12, column: 6, scope: !21) -!23 = !DILocation(line: 12, column: 45, scope: !21) -!24 = !DILocation(line: 17, column: 26, scope: !25) -!25 = distinct !DISubprogram(name: ".", linkageName: "func_.13$block_3", scope: !17, file: !4, line: 5, type: !12, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!26 = !DILocation(line: 17, column: 6, scope: !25) -!27 = !DILocation(line: 22, column: 26, scope: !28) -!28 = distinct !DISubprogram(name: ".", linkageName: "func_.13$block_4", scope: !17, file: !4, line: 5, type: !12, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!29 = !DILocation(line: 22, column: 6, scope: !28) -!30 = !DILocation(line: 5, column: 1, scope: !17) -!31 = !DILocation(line: 27, column: 3, scope: !17) -!32 = !DILocation(line: 27, column: 1, scope: !17) -!33 = !DILocation(line: 28, column: 3, scope: !17) -!34 = !DILocation(line: 28, column: 1, scope: !17) -!35 = !DILocation(line: 29, column: 3, scope: !17) -!36 = !DILocation(line: 29, column: 1, scope: !17) -!37 = !DILocation(line: 30, column: 3, scope: !17) -!38 = !DILocation(line: 30, column: 1, scope: !17) -!39 = !DILocation(line: 31, column: 3, scope: !17) -!40 = !DILocation(line: 31, column: 1, scope: !17) -!41 = !DILocation(line: 32, column: 3, scope: !17) -!42 = !DILocation(line: 32, column: 1, scope: !17) -!43 = !DILocation(line: 33, column: 3, scope: !17) -!44 = !DILocation(line: 33, column: 1, scope: !17) -!45 = !DILocation(line: 36, column: 3, scope: !17) -!46 = !DILocation(line: 36, column: 1, scope: !17) -!47 = !DILocation(line: 37, column: 13, scope: !17) -!48 = !DILocation(line: 37, column: 3, scope: !17) -!49 = !DILocation(line: 37, column: 1, scope: !17) -!50 = !DILocation(line: 38, column: 13, scope: !17) -!51 = !DILocation(line: 38, column: 3, scope: !17) -!52 = !DILocation(line: 38, column: 1, scope: !17) -!53 = !DILocation(line: 40, column: 3, scope: !17) -!54 = !DILocation(line: 40, column: 1, scope: !17) -!55 = !DILocation(line: 41, column: 15, scope: !17) -!56 = !DILocation(line: 41, column: 3, scope: !17) -!57 = !DILocation(line: 41, column: 1, scope: !17) -!58 = !DILocation(line: 42, column: 15, scope: !17) -!59 = !DILocation(line: 42, column: 3, scope: !17) -!60 = !DILocation(line: 42, column: 1, scope: !17) -!61 = !DILocation(line: 44, column: 3, scope: !17) -!62 = !DILocation(line: 44, column: 1, scope: !17) -!63 = !DILocation(line: 45, column: 12, scope: !17) -!64 = !DILocation(line: 45, column: 3, scope: !17) -!65 = !DILocation(line: 45, column: 1, scope: !17) -!66 = !DILocation(line: 47, column: 3, scope: !17) -!67 = !DILocation(line: 47, column: 1, scope: !17) -!68 = !DILocation(line: 48, column: 13, scope: !17) -!69 = !DILocation(line: 48, column: 3, scope: !17) -!70 = !DILocation(line: 48, column: 1, scope: !17) -!71 = distinct !DISubprogram(name: "Object#plus", linkageName: "func_Object#4plus", scope: null, file: !4, line: 8, type: !12, scopeLine: 8, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!72 = !{!73, !73, i64 0} -!73 = !{!"any pointer", !8, i64 0} -!74 = !DILocation(line: 8, column: 1, scope: !71) -!75 = !{!"branch_weights", i32 4001, i32 4000000} -!76 = !DILocation(line: 8, column: 10, scope: !71) -!77 = !{!78, !7, i64 0} -!78 = !{!"RBasic", !7, i64 0, !7, i64 8} -!79 = !{!"branch_weights", i32 2000, i32 1} -!80 = !DILocation(line: 8, column: 13, scope: !71) -!81 = !DILocation(line: 9, column: 3, scope: !71) -!82 = !{!83} -!83 = distinct !{!83, !84, !"sorbet_int_rb_float_plus: argument 0"} -!84 = distinct !{!84, !"sorbet_int_rb_float_plus"} -!85 = !{!86, !87, i64 40} -!86 = !{!"rb_execution_context_struct", !73, i64 0, !7, i64 8, !73, i64 16, !73, i64 24, !73, i64 32, !87, i64 40, !87, i64 44, !73, i64 48, !73, i64 56, !73, i64 64, !7, i64 72, !7, i64 80, !73, i64 88, !7, i64 96, !73, i64 104, !73, i64 112, !7, i64 120, !7, i64 128, !8, i64 136, !8, i64 137, !7, i64 144, !88, i64 152} -!87 = !{!"int", !8, i64 0} -!88 = !{!"", !73, i64 0, !73, i64 8, !7, i64 16, !8, i64 24} -!89 = !{!86, !87, i64 44} -!90 = !{!86, !73, i64 56} -!91 = !DILocation(line: 13, column: 1, scope: !11) -!92 = !DILocation(line: 13, column: 11, scope: !11) -!93 = !DILocation(line: 13, column: 14, scope: !11) -!94 = distinct !DISubprogram(name: "Object#lt", linkageName: "func_Object#2lt", scope: null, file: !4, line: 18, type: !12, scopeLine: 18, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!95 = !DILocation(line: 18, column: 1, scope: !94) -!96 = !DILocation(line: 18, column: 8, scope: !94) -!97 = !DILocation(line: 18, column: 11, scope: !94) -!98 = !DILocation(line: 19, column: 3, scope: !94) -!99 = !{!100} -!100 = distinct !{!100, !101, !"sorbet_int_flo_lt: argument 0"} -!101 = distinct !{!101, !"sorbet_int_flo_lt"} -!102 = !{!"branch_weights", i32 1, i32 2001, i32 2000} -!103 = !DILocation(line: 0, scope: !94) -!104 = distinct !DISubprogram(name: "Object#lte", linkageName: "func_Object#3lte", scope: null, file: !4, line: 23, type: !12, scopeLine: 23, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!105 = !DILocation(line: 23, column: 1, scope: !104) -!106 = !DILocation(line: 23, column: 9, scope: !104) -!107 = !DILocation(line: 23, column: 12, scope: !104) -!108 = !DILocation(line: 24, column: 3, scope: !104) -!109 = !{!110} -!110 = distinct !{!110, !111, !"sorbet_int_flo_le: argument 0"} -!111 = distinct !{!111, !"sorbet_int_flo_le"} -!112 = !DILocation(line: 0, scope: !104) -!113 = !{!86, !73, i64 16} -!114 = !{!115, !7, i64 24} -!115 = !{!"rb_control_frame_struct", !73, i64 0, !73, i64 8, !73, i64 16, !7, i64 24, !73, i64 32, !73, i64 40, !73, i64 48} -!116 = !{!115, !73, i64 16} -!117 = !{!115, !73, i64 32} -!118 = !{!119, !119, i64 0} -!119 = !{!"long long", !8, i64 0} -!120 = !{!"branch_weights", i32 1, i32 10000} -!121 = !DILocation(line: 8, column: 1, scope: !17) -!122 = !DILocation(line: 13, column: 1, scope: !17) -!123 = !DILocation(line: 18, column: 1, scope: !17) -!124 = !DILocation(line: 23, column: 1, scope: !17) -!125 = !{!126, !7, i64 400} -!126 = !{!"rb_vm_struct", !7, i64 0, !127, i64 8, !73, i64 192, !73, i64 200, !73, i64 208, !119, i64 216, !8, i64 224, !128, i64 264, !128, i64 280, !128, i64 296, !128, i64 312, !7, i64 328, !87, i64 336, !87, i64 340, !87, i64 344, !87, i64 344, !87, i64 344, !87, i64 344, !87, i64 348, !7, i64 352, !8, i64 360, !7, i64 400, !7, i64 408, !7, i64 416, !7, i64 424, !7, i64 432, !7, i64 440, !7, i64 448, !73, i64 456, !73, i64 464, !130, i64 472, !131, i64 992, !73, i64 1016, !73, i64 1024, !87, i64 1032, !87, i64 1036, !128, i64 1040, !8, i64 1056, !7, i64 1096, !7, i64 1104, !7, i64 1112, !7, i64 1120, !7, i64 1128, !87, i64 1136, !73, i64 1144, !73, i64 1152, !73, i64 1160, !73, i64 1168, !73, i64 1176, !73, i64 1184, !87, i64 1192, !132, i64 1200, !8, i64 1232} -!127 = !{!"rb_global_vm_lock_struct", !73, i64 0, !8, i64 8, !128, i64 48, !73, i64 64, !87, i64 72, !8, i64 80, !8, i64 128, !87, i64 176, !87, i64 180} -!128 = !{!"list_head", !129, i64 0} -!129 = !{!"list_node", !73, i64 0, !73, i64 8} -!130 = !{!"", !8, i64 0} -!131 = !{!"rb_hook_list_struct", !73, i64 0, !87, i64 8, !87, i64 12, !87, i64 16} -!132 = !{!"", !7, i64 0, !7, i64 8, !7, i64 16, !7, i64 24} -!133 = !DILocation(line: 0, scope: !17) -!134 = !DILocation(line: 7, column: 1, scope: !17) -!135 = !DILocation(line: 12, column: 1, scope: !17) -!136 = !DILocation(line: 17, column: 1, scope: !17) -!137 = !DILocation(line: 22, column: 1, scope: !17) -!138 = !{!139, !87, i64 8} -!139 = !{!"rb_sorbet_param_struct", !140, i64 0, !87, i64 4, !87, i64 8, !87, i64 12, !87, i64 16, !87, i64 20, !87, i64 24, !87, i64 28, !73, i64 32, !87, i64 40, !87, i64 44, !87, i64 48, !87, i64 52, !73, i64 56} -!140 = !{!"", !87, i64 0, !87, i64 0, !87, i64 0, !87, i64 0, !87, i64 0, !87, i64 0, !87, i64 0, !87, i64 0, !87, i64 1, !87, i64 1} -!141 = !{!139, !87, i64 4} -!142 = !{!139, !73, i64 32} -!143 = distinct !DISubprogram(name: "func_Object#2lt.cold.1", linkageName: "func_Object#2lt.cold.1", scope: null, file: !4, type: !144, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !3, retainedNodes: !5) -!144 = !DISubroutineType(types: !5) -!145 = distinct !DISubprogram(name: "func_Object#2lt.cold.3", linkageName: "func_Object#2lt.cold.3", scope: null, file: !4, type: !144, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !3, retainedNodes: !5) -!146 = !DILocation(line: 18, column: 8, scope: !145) diff --git a/test/testdata/compiler/func_init__1.rb b/test/testdata/compiler/func_init__1.rb index 2e7f75cfa8..f4d2e47b3f 100644 --- a/test/testdata/compiler/func_init__1.rb +++ b/test/testdata/compiler/func_init__1.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true +# typed: false # compiled: false module A; end diff --git a/test/testdata/compiler/globalfields.opt.ll.exp b/test/testdata/compiler/globalfields.opt.ll.exp deleted file mode 100644 index a80ef8b34f..0000000000 --- a/test/testdata/compiler/globalfields.opt.ll.exp +++ /dev/null @@ -1,571 +0,0 @@ -; ModuleID = 'payload' -source_filename = "llvm-link" -target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } -%struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.__pthread_cond_s } -%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } -%union.anon = type { i64 } -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } -%struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } -%struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } -%struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } -%struct.anon.0 = type { i16, [2 x i8] } -%struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } -%struct.rb_iseq_location_struct = type { i64, i64, i64, i64, i32, %struct.rb_code_location_struct } -%struct.rb_code_location_struct = type { %struct.rb_code_position_struct, %struct.rb_code_position_struct } -%struct.rb_code_position_struct = type { i32, i32 } -%struct.iseq_insn_info = type { %struct.iseq_insn_info_entry*, i32*, i32, %struct.succ_index_table* } -%struct.iseq_insn_info_entry = type opaque -%struct.succ_index_table = type opaque -%struct.iseq_catch_table = type opaque -%union.iseq_inline_storage_entry = type { %struct.iseq_inline_cache_entry } -%struct.iseq_inline_cache_entry = type { i64, %struct.rb_cref_struct*, i64 } -%struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } -%struct.rb_scope_visi_struct = type { i8, [3 x i8] } -%struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } -%struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } -%union.anon.13 = type { %struct.rb_method_cfunc_struct } -%struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%union.anon.15 = type { i32 } -%struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.16 = type { i64, i64, i64, i64* } -%union.anon.17 = type { %struct.anon.18 } -%struct.anon.18 = type { i64, i32 } -%struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } -%struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } -%struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } -%struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } -%struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } -%struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } -%struct.__sigset_t = type { [16 x i64] } -%struct.rb_calling_info = type { i64, i64, i32, i32 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } -%union.anon.9 = type { %union.pthread_cond_t } -%struct.rb_unblock_callback = type { void (i8*)*, i8* } -%struct.rb_mutex_struct = type opaque -%struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.10 = type { %struct.anon.11 } -%struct.anon.11 = type { i64, i64, i32 } -%struct.rb_fiber_struct = type opaque -%struct.anon.5 = type { [65 x i64] } -%struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } -%struct.rb_event_hook_struct = type opaque -%struct.rb_postponed_job_struct = type opaque -%struct.list_head = type { %struct.list_node } -%union.pthread_mutex_t = type { %struct.__pthread_mutex_s } -%struct.__pthread_mutex_s = type { i32, i32, i32, i32, i32, i16, i16, %struct.__pthread_internal_list } -%struct.__pthread_internal_list = type { %struct.__pthread_internal_list*, %struct.__pthread_internal_list* } -%struct.rb_objspace = type opaque -%struct.rb_at_exit_list = type { void (%struct.rb_vm_struct*)*, %struct.rb_at_exit_list* } -%struct.st_table = type { i8, i8, i8, i32, %struct.st_hash_type*, i64, i64*, i64, i64, %struct.st_table_entry* } -%struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } -%struct.st_table_entry = type opaque -%struct.rb_builtin_function = type opaque -%struct.anon.6 = type { i64, i64, i64, i64 } -%struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } -%struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } -%struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } -%struct.rb_call_info_with_kwarg = type { %struct.rb_call_info, %struct.rb_call_info_kw_arg* } -%struct.rb_call_info_kw_arg = type { i32, [1 x i64] } -%struct.rb_global_entry = type { %struct.rb_global_variable*, i64 } -%struct.rb_global_variable = type opaque - -@ruby_current_vm_ptr = external local_unnamed_addr global %struct.rb_vm_struct*, align 8 -@ruby_current_execution_context_ptr = external local_unnamed_addr global %struct.rb_execution_context_struct*, align 8 -@ruby_vm_global_constant_state = external local_unnamed_addr global i64, align 8 -@rb_eRuntimeError = external local_unnamed_addr global i64, align 8 -@.str.9 = private unnamed_addr constant [95 x i8] c"sorbet_getBuildSCMRevision: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@.str.10 = private unnamed_addr constant [93 x i8] c"sorbet_getIsReleaseBuild: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@"stackFramePrecomputed_func_.13" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@iseqEncodedArray = internal global [20 x i64] zeroinitializer -@fileLineNumberInfo = internal global %struct.SorbetLineNumberInfo zeroinitializer -@ic_new = internal global %struct.FunctionInlineCache zeroinitializer -@ic_initialize = internal global %struct.FunctionInlineCache zeroinitializer -@ic_read = internal global %struct.FunctionInlineCache zeroinitializer -@ic_puts = internal global %struct.FunctionInlineCache zeroinitializer -@ic_write = internal global %struct.FunctionInlineCache zeroinitializer -@ic_read.1 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_puts.2 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_puts.3 = internal global %struct.FunctionInlineCache zeroinitializer -@"stackFramePrecomputed_func_A#5write" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"stackFramePrecomputed_func_A#4read" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"stackFramePrecomputed_func_A.13" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@sorbet_moduleStringTable = internal unnamed_addr constant [133 x i8] c"\00test/testdata/compiler/globalfields.rb\00A\00Object\00new\00initialize\00read\00puts\00value\00write\00$f\00F\00master\00\00normal\00v\00", align 1 -@sorbet_moduleIDTable = internal unnamed_addr global [10 x i64] zeroinitializer, align 8 -@sorbet_moduleIDDescriptors = internal unnamed_addr constant [10 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 16 }, %struct.rb_code_position_struct { i32 65, i32 3 }, %struct.rb_code_position_struct { i32 69, i32 10 }, %struct.rb_code_position_struct { i32 80, i32 4 }, %struct.rb_code_position_struct { i32 85, i32 4 }, %struct.rb_code_position_struct { i32 96, i32 5 }, %struct.rb_code_position_struct { i32 102, i32 2 }, %struct.rb_code_position_struct { i32 114, i32 9 }, %struct.rb_code_position_struct { i32 124, i32 6 }, %struct.rb_code_position_struct { i32 131, i32 1 }], align 8 -@sorbet_moduleRubyStringTable = internal unnamed_addr global [6 x i64] zeroinitializer, align 8 -@sorbet_moduleRubyStringDescriptors = internal unnamed_addr constant [6 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 16 }, %struct.rb_code_position_struct { i32 17, i32 38 }, %struct.rb_code_position_struct { i32 90, i32 5 }, %struct.rb_code_position_struct { i32 96, i32 5 }, %struct.rb_code_position_struct { i32 80, i32 4 }, %struct.rb_code_position_struct { i32 114, i32 9 }], align 8 -@rb_cObject = external local_unnamed_addr constant i64 -@guard_epoch_A = linkonce local_unnamed_addr global i64 0 -@guarded_const_A = linkonce local_unnamed_addr global i64 0 - -; Function Attrs: noreturn -declare void @sorbet_raiseArity(i32, i32, i32) local_unnamed_addr #0 - -declare %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64, i64, i64, i64, %struct.rb_iseq_struct*, i32, i32, %struct.SorbetLineNumberInfo*, i64*, i32, i32) local_unnamed_addr #1 - -declare void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo*, i64*, i32) local_unnamed_addr #1 - -declare i64 @sorbet_getConstant(i8*, i64) local_unnamed_addr #1 - -declare i64 @sorbet_setConstant(i64, i8*, i64, i64) local_unnamed_addr #1 - -declare i64 @sorbet_readRealpath() local_unnamed_addr #1 - -declare %struct.rb_control_frame_struct* @sorbet_pushStaticInitFrame(i64) local_unnamed_addr #1 - -declare void @sorbet_popFrame() local_unnamed_addr #1 - -declare void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) local_unnamed_addr #1 - -declare i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache*, i64) local_unnamed_addr #1 - -declare void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_iseq_struct*) local_unnamed_addr #1 - -declare void @sorbet_vm_define_method(i64, i8*, i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)*, i8*, %struct.rb_iseq_struct*, i1 zeroext) local_unnamed_addr #1 - -declare void @sorbet_vm_intern_ids(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #1 - -declare void @sorbet_vm_init_string_table(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #1 - -declare i64 @sorbet_maybeAllocateObjectFastPath(i64, %struct.FunctionInlineCache*) local_unnamed_addr #1 - -declare i64 @rb_define_class(i8*, i64) local_unnamed_addr #1 - -declare %struct.rb_global_entry* @rb_global_entry(i64) local_unnamed_addr #1 - -declare i64 @rb_gvar_get(%struct.rb_global_entry*) local_unnamed_addr #1 - -declare i64 @rb_gvar_set(%struct.rb_global_entry*, i64) local_unnamed_addr #1 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #2 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #2 - -; Function Attrs: noreturn -declare void @rb_raise(i64, i8*, ...) local_unnamed_addr #0 - -; Function Attrs: allocsize(0,1) -declare noalias nonnull i8* @ruby_xcalloc(i64, i64) local_unnamed_addr #3 - -; Function Attrs: allocsize(0,1) -declare noalias nonnull i8* @ruby_xmalloc2(i64, i64) local_unnamed_addr #3 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) #2 - -; Function Attrs: nounwind ssp uwtable -define weak i32 @sorbet_getIsReleaseBuild() local_unnamed_addr #4 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([93 x i8], [93 x i8]* @.str.10, i64 0, i64 0)) #10 - unreachable -} - -; Function Attrs: nounwind ssp uwtable -define weak i8* @sorbet_getBuildSCMRevision() local_unnamed_addr #4 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([95 x i8], [95 x i8]* @.str.9, i64 0, i64 0)) #10 - unreachable -} - -define internal fastcc void @sorbet_globalConstructors(i64 %realpath) unnamed_addr { -allocRubyIds: - tail call void @sorbet_vm_intern_ids(i64* noundef getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([10 x %struct.rb_code_position_struct], [10 x %struct.rb_code_position_struct]* @sorbet_moduleIDDescriptors, i32 0, i32 0), i32 noundef 10, i8* noundef getelementptr inbounds ([133 x i8], [133 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_vm_init_string_table(i64* noundef getelementptr inbounds ([6 x i64], [6 x i64]* @sorbet_moduleRubyStringTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([6 x %struct.rb_code_position_struct], [6 x %struct.rb_code_position_struct]* @sorbet_moduleRubyStringDescriptors, i32 0, i32 0), i32 noundef 6, i8* noundef getelementptr inbounds ([133 x i8], [133 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef getelementptr inbounds ([20 x i64], [20 x i64]* @iseqEncodedArray, i32 0, i32 0), i32 noundef 20) - tail call fastcc void @"Constr_stackFramePrecomputed_func_.13"(i64 %realpath) - %rubyId_new = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 1), align 8, !dbg !10, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_new, i64 %rubyId_new, i32 noundef 16, i32 noundef 0, i32 noundef 0), !dbg !10 - %rubyId_initialize = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 2), align 8, !dbg !10, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_initialize, i64 %rubyId_initialize, i32 noundef 20, i32 noundef 0, i32 noundef 0), !dbg !10 - %rubyId_read = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 3), align 8, !dbg !15, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_read, i64 %rubyId_read, i32 noundef 16, i32 noundef 0, i32 noundef 0), !dbg !15 - %rubyId_puts = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 4), align 8, !dbg !16, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts, i64 %rubyId_puts, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !16 - %rubyId_write = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 5), align 8, !dbg !17, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_write, i64 %rubyId_write, i32 noundef 16, i32 noundef 1, i32 noundef 0), !dbg !17 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_read.1, i64 %rubyId_read, i32 noundef 16, i32 noundef 0, i32 noundef 0), !dbg !18 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts.2, i64 %rubyId_puts, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !19 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts.3, i64 %rubyId_puts, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !20 - tail call fastcc void @"Constr_stackFramePrecomputed_func_A#5write"(i64 %realpath) - tail call fastcc void @"Constr_stackFramePrecomputed_func_A#4read"(i64 %realpath) - tail call fastcc void @"Constr_stackFramePrecomputed_func_A.13"(i64 %realpath) - ret void -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_.13"(i64 %realpath) unnamed_addr #5 { -entryInitializers: - %"rubyId_" = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 0), align 8, !invariant.load !5 - %"rubyStr_" = load i64, i64* getelementptr inbounds ([6 x i64], [6 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 0), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/globalfields.rb" = load i64, i64* getelementptr inbounds ([6 x i64], [6 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_", i64 %"rubyId_", i64 %"rubyStr_test/testdata/compiler/globalfields.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 0, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 2) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - ret void -} - -; Function Attrs: sspreq -define void @Init_globalfields() local_unnamed_addr #6 { -entry: - %positional_table.i.i = alloca i64, align 8, !dbg !21 - %realpath = tail call i64 @sorbet_readRealpath() - tail call fastcc void @sorbet_globalConstructors(i64 %realpath) - %0 = load %struct.rb_vm_struct*, %struct.rb_vm_struct** @ruby_current_vm_ptr, align 8, !tbaa !24 - %1 = getelementptr inbounds %struct.rb_vm_struct, %struct.rb_vm_struct* %0, i64 0, i32 18 - %2 = load i64, i64* %1, align 8, !tbaa !26 - %3 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !24 - %4 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %3, i64 0, i32 2 - %5 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %4, align 8, !tbaa !36 - %stackFrame.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - %6 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame.i, %struct.rb_iseq_struct** %6, align 8, !tbaa !39 - %7 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 4 - %8 = load i64*, i64** %7, align 8, !tbaa !41 - %9 = load i64, i64* %8, align 8, !tbaa !6 - %10 = and i64 %9, -33 - store i64 %10, i64* %8, align 8, !tbaa !6 - tail call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %3, %struct.rb_control_frame_struct* %5, %struct.rb_iseq_struct* %stackFrame.i) #11 - %11 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 0 - store i64* getelementptr inbounds ([20 x i64], [20 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %11, align 8, !dbg !42, !tbaa !24 - %12 = load i64, i64* @rb_cObject, align 8, !dbg !43 - %13 = tail call i64 @rb_define_class(i8* getelementptr inbounds ([133 x i8], [133 x i8]* @sorbet_moduleStringTable, i64 0, i64 56), i64 %12) #11, !dbg !43 - %14 = tail call %struct.rb_control_frame_struct* @sorbet_pushStaticInitFrame(i64 %13) #11, !dbg !43 - %15 = bitcast i64* %positional_table.i.i to i8* - call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %15) #11 - %stackFrame.i.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_A.13", align 8 - %16 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !24 - %17 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %16, i64 0, i32 2 - %18 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %17, align 8, !tbaa !36 - %19 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %18, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame.i.i, %struct.rb_iseq_struct** %19, align 8, !tbaa !39 - %20 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %18, i64 0, i32 4 - %21 = load i64*, i64** %20, align 8, !tbaa !41 - %22 = load i64, i64* %21, align 8, !tbaa !6 - %23 = and i64 %22, -33 - store i64 %23, i64* %21, align 8, !tbaa !6 - tail call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %16, %struct.rb_control_frame_struct* %18, %struct.rb_iseq_struct* %stackFrame.i.i) #11 - %24 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %14, i64 0, i32 0 - store i64* getelementptr inbounds ([20 x i64], [20 x i64]* @iseqEncodedArray, i64 0, i64 6), i64** %24, align 8, !dbg !44, !tbaa !24 - %25 = load i64, i64* @guard_epoch_A, align 8, !dbg !21 - %26 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !21, !tbaa !45 - %needTakeSlowPath = icmp ne i64 %25, %26, !dbg !21 - br i1 %needTakeSlowPath, label %27, label %28, !dbg !21, !prof !46 - -27: ; preds = %entry - tail call void @const_recompute_A(), !dbg !21 - br label %28, !dbg !21 - -28: ; preds = %entry, %27 - %29 = load i64, i64* @guarded_const_A, align 8, !dbg !21 - %30 = load i64, i64* @guard_epoch_A, align 8, !dbg !21 - %31 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !21, !tbaa !45 - %guardUpdated = icmp eq i64 %30, %31, !dbg !21 - tail call void @llvm.assume(i1 %guardUpdated), !dbg !21 - %stackFrame17.i.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_A#5write", align 8, !dbg !21 - %32 = tail call noalias nonnull i8* @ruby_xcalloc(i64 noundef 1, i64 noundef 64) #12, !dbg !21 - %33 = bitcast i8* %32 to i16*, !dbg !21 - %34 = load i16, i16* %33, align 8, !dbg !21 - %35 = and i16 %34, -384, !dbg !21 - %36 = or i16 %35, 1, !dbg !21 - store i16 %36, i16* %33, align 8, !dbg !21 - %37 = getelementptr inbounds i8, i8* %32, i64 8, !dbg !21 - %38 = bitcast i8* %37 to i32*, !dbg !21 - store i32 1, i32* %38, align 8, !dbg !21, !tbaa !47 - %39 = getelementptr inbounds i8, i8* %32, i64 12, !dbg !21 - %40 = getelementptr inbounds i8, i8* %32, i64 4, !dbg !21 - %41 = bitcast i8* %40 to i32*, !dbg !21 - tail call void @llvm.memset.p0i8.i64(i8* nonnull align 4 %39, i8 0, i64 20, i1 false) #11, !dbg !21 - store i32 1, i32* %41, align 4, !dbg !21, !tbaa !50 - %rubyId_v.i.i = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 9), align 8, !dbg !21, !invariant.load !5 - store i64 %rubyId_v.i.i, i64* %positional_table.i.i, align 8, !dbg !21 - %42 = tail call noalias nonnull i8* @ruby_xmalloc2(i64 noundef 1, i64 noundef 8) #12, !dbg !21 - call void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture nonnull writeonly align 1 %42, i8* nocapture noundef nonnull readonly align 8 dereferenceable(8) %15, i64 noundef 8, i1 noundef false) #11, !dbg !21 - %43 = getelementptr inbounds i8, i8* %32, i64 32, !dbg !21 - %44 = bitcast i8* %43 to i8**, !dbg !21 - store i8* %42, i8** %44, align 8, !dbg !21, !tbaa !51 - tail call void @sorbet_vm_define_method(i64 %29, i8* getelementptr inbounds ([133 x i8], [133 x i8]* @sorbet_moduleStringTable, i64 0, i64 96), i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)* noundef @"func_A#5write", i8* nonnull %32, %struct.rb_iseq_struct* %stackFrame17.i.i, i1 noundef zeroext false) #11, !dbg !21 - store i64* getelementptr inbounds ([20 x i64], [20 x i64]* @iseqEncodedArray, i64 0, i64 9), i64** %24, align 8, !dbg !21, !tbaa !24 - %stackFrame26.i.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_A#4read", align 8, !dbg !52 - %45 = tail call noalias nonnull i8* @ruby_xcalloc(i64 noundef 1, i64 noundef 64) #12, !dbg !52 - %46 = bitcast i8* %45 to i16*, !dbg !52 - %47 = load i16, i16* %46, align 8, !dbg !52 - %48 = and i16 %47, -384, !dbg !52 - store i16 %48, i16* %46, align 8, !dbg !52 - %49 = getelementptr inbounds i8, i8* %45, i64 4, !dbg !52 - tail call void @llvm.memset.p0i8.i64(i8* nonnull align 4 %49, i8 0, i64 28, i1 false) #11, !dbg !52 - tail call void @sorbet_vm_define_method(i64 %29, i8* getelementptr inbounds ([133 x i8], [133 x i8]* @sorbet_moduleStringTable, i64 0, i64 80), i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)* noundef @"func_A#4read", i8* nonnull %45, %struct.rb_iseq_struct* %stackFrame26.i.i, i1 noundef zeroext false) #11, !dbg !52 - call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %15) #11 - tail call void @sorbet_popFrame() #11, !dbg !43 - store i64* getelementptr inbounds ([20 x i64], [20 x i64]* @iseqEncodedArray, i64 0, i64 14), i64** %11, align 8, !dbg !43, !tbaa !24 - %50 = tail call i64 @sorbet_maybeAllocateObjectFastPath(i64 %29, %struct.FunctionInlineCache* noundef @ic_new) #11, !dbg !10 - %51 = icmp eq i64 %50, 52, !dbg !10 - br i1 %51, label %slowNew.i, label %fastNew.i, !dbg !10 - -slowNew.i: ; preds = %28 - %52 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !10 - %53 = load i64*, i64** %52, align 8, !dbg !10 - store i64 %29, i64* %53, align 8, !dbg !10, !tbaa !6 - %54 = getelementptr inbounds i64, i64* %53, i64 1, !dbg !10 - store i64* %54, i64** %52, align 8, !dbg !10 - %55 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* noundef @ic_new, i64 noundef 0) #11, !dbg !10 - br label %"func_.13.exit", !dbg !10 - -fastNew.i: ; preds = %28 - %56 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !10 - %57 = load i64*, i64** %56, align 8, !dbg !10 - store i64 %50, i64* %57, align 8, !dbg !10, !tbaa !6 - %58 = getelementptr inbounds i64, i64* %57, i64 1, !dbg !10 - store i64* %58, i64** %56, align 8, !dbg !10 - %59 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* noundef @ic_initialize, i64 noundef 0) #11, !dbg !10 - br label %"func_.13.exit", !dbg !10 - -"func_.13.exit": ; preds = %slowNew.i, %fastNew.i - %initializedObject.i = phi i64 [ %55, %slowNew.i ], [ %50, %fastNew.i ], !dbg !10 - store i64* getelementptr inbounds ([20 x i64], [20 x i64]* @iseqEncodedArray, i64 0, i64 15), i64** %11, align 8, !dbg !10, !tbaa !24 - %60 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !15 - %61 = load i64*, i64** %60, align 8, !dbg !15 - store i64 %initializedObject.i, i64* %61, align 8, !dbg !15, !tbaa !6 - %62 = getelementptr inbounds i64, i64* %61, i64 1, !dbg !15 - store i64* %62, i64** %60, align 8, !dbg !15 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_read, i64 0), !dbg !15 - %63 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !16 - %64 = load i64*, i64** %63, align 8, !dbg !16 - store i64 %2, i64* %64, align 8, !dbg !16, !tbaa !6 - %65 = getelementptr inbounds i64, i64* %64, i64 1, !dbg !16 - store i64 %send, i64* %65, align 8, !dbg !16, !tbaa !6 - %66 = getelementptr inbounds i64, i64* %65, i64 1, !dbg !16 - store i64* %66, i64** %63, align 8, !dbg !16 - %send2 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts, i64 0), !dbg !16 - store i64* getelementptr inbounds ([20 x i64], [20 x i64]* @iseqEncodedArray, i64 0, i64 16), i64** %11, align 8, !dbg !16, !tbaa !24 - %rubyStr_value.i = load i64, i64* getelementptr inbounds ([6 x i64], [6 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 2), align 8, !dbg !53, !invariant.load !5 - %67 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !17 - %68 = load i64*, i64** %67, align 8, !dbg !17 - store i64 %initializedObject.i, i64* %68, align 8, !dbg !17, !tbaa !6 - %69 = getelementptr inbounds i64, i64* %68, i64 1, !dbg !17 - store i64 %rubyStr_value.i, i64* %69, align 8, !dbg !17, !tbaa !6 - %70 = getelementptr inbounds i64, i64* %69, i64 1, !dbg !17 - store i64* %70, i64** %67, align 8, !dbg !17 - %send4 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_write, i64 0), !dbg !17 - store i64* getelementptr inbounds ([20 x i64], [20 x i64]* @iseqEncodedArray, i64 0, i64 17), i64** %11, align 8, !dbg !17, !tbaa !24 - %71 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !18 - %72 = load i64*, i64** %71, align 8, !dbg !18 - store i64 %initializedObject.i, i64* %72, align 8, !dbg !18, !tbaa !6 - %73 = getelementptr inbounds i64, i64* %72, i64 1, !dbg !18 - store i64* %73, i64** %71, align 8, !dbg !18 - %send6 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_read.1, i64 0), !dbg !18 - %74 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !19 - %75 = load i64*, i64** %74, align 8, !dbg !19 - store i64 %2, i64* %75, align 8, !dbg !19, !tbaa !6 - %76 = getelementptr inbounds i64, i64* %75, i64 1, !dbg !19 - store i64 %send6, i64* %76, align 8, !dbg !19, !tbaa !6 - %77 = getelementptr inbounds i64, i64* %76, i64 1, !dbg !19 - store i64* %77, i64** %74, align 8, !dbg !19 - %send8 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts.2, i64 0), !dbg !19 - store i64* getelementptr inbounds ([20 x i64], [20 x i64]* @iseqEncodedArray, i64 0, i64 18), i64** %11, align 8, !dbg !19, !tbaa !24 - %"rubyId_$f.i" = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 6), align 8, !dbg !20, !invariant.load !5 - %78 = tail call %struct.rb_global_entry* @rb_global_entry(i64 %"rubyId_$f.i") #11, !dbg !20 - %79 = tail call i64 @rb_gvar_get(%struct.rb_global_entry* %78) #11, !dbg !20 - %80 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !20 - %81 = load i64*, i64** %80, align 8, !dbg !20 - store i64 %2, i64* %81, align 8, !dbg !20, !tbaa !6 - %82 = getelementptr inbounds i64, i64* %81, i64 1, !dbg !20 - store i64 %79, i64* %82, align 8, !dbg !20, !tbaa !6 - %83 = getelementptr inbounds i64, i64* %82, i64 1, !dbg !20 - store i64* %83, i64** %80, align 8, !dbg !20 - %send10 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts.3, i64 0), !dbg !20 - store i64* getelementptr inbounds ([20 x i64], [20 x i64]* @iseqEncodedArray, i64 0, i64 19), i64** %11, align 8, !dbg !20, !tbaa !24 - %84 = tail call i64 @sorbet_setConstant(i64 %12, i8* getelementptr inbounds ([133 x i8], [133 x i8]* @sorbet_moduleStringTable, i64 0, i64 105), i64 noundef 1, i64 noundef 3) #11, !dbg !54 - ret void -} - -; Function Attrs: nounwind sspreq uwtable -define internal i64 @"func_A#5write"(i32 %argc, i64* nocapture readonly %argArray, i64 %selfRaw, %struct.rb_control_frame_struct* nocapture nonnull writeonly align 8 dereferenceable(8) %cfp, i8* nocapture nofree readnone %calling, i8* nocapture nofree readnone %callData) #7 !dbg !55 { -functionEntryInitializers: - %0 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 0 - store i64* getelementptr inbounds ([20 x i64], [20 x i64]* @iseqEncodedArray, i64 0, i64 6), i64** %0, align 8, !tbaa !24 - %tooManyArgs = icmp ugt i32 %argc, 1, !dbg !56 - %tooFewArgs = icmp ult i32 %argc, 1, !dbg !56 - %or.cond = or i1 %tooManyArgs, %tooFewArgs, !dbg !56 - br i1 %or.cond, label %argCountFailBlock, label %fillRequiredArgs, !dbg !56, !prof !57 - -argCountFailBlock: ; preds = %functionEntryInitializers - tail call void @sorbet_raiseArity(i32 %argc, i32 noundef 1, i32 noundef 1) #13, !dbg !56 - unreachable, !dbg !56 - -fillRequiredArgs: ; preds = %functionEntryInitializers - %rawArg_v = load i64, i64* %argArray, align 8, !dbg !56 - store i64* getelementptr inbounds ([20 x i64], [20 x i64]* @iseqEncodedArray, i64 0, i64 7), i64** %0, align 8, !dbg !58, !tbaa !24 - %"rubyId_$f" = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 6), align 8, !dbg !59, !invariant.load !5 - %1 = tail call %struct.rb_global_entry* @rb_global_entry(i64 %"rubyId_$f") #11, !dbg !59 - %2 = tail call i64 @rb_gvar_set(%struct.rb_global_entry* %1, i64 %rawArg_v) #11, !dbg !59 - %3 = tail call %struct.rb_global_entry* @rb_global_entry(i64 %"rubyId_$f") #11, !dbg !60 - %4 = tail call i64 @rb_gvar_get(%struct.rb_global_entry* %3) #11, !dbg !60 - ret i64 %4 -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_A#5write"(i64 %realpath) unnamed_addr #5 { -entryInitializers: - %rubyId_write = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 5), align 8, !invariant.load !5 - %rubyStr_write = load i64, i64* getelementptr inbounds ([6 x i64], [6 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 3), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/globalfields.rb" = load i64, i64* getelementptr inbounds ([6 x i64], [6 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %rubyStr_write, i64 %rubyId_write, i64 %"rubyStr_test/testdata/compiler/globalfields.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 1, i32 noundef 6, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 0) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_A#5write", align 8 - ret void -} - -; Function Attrs: nounwind sspreq uwtable -define internal i64 @"func_A#4read"(i32 %argc, i64* nocapture nofree readnone %argArray, i64 %selfRaw, %struct.rb_control_frame_struct* nocapture nonnull writeonly align 8 dereferenceable(8) %cfp, i8* nocapture nofree readnone %calling, i8* nocapture nofree readnone %callData) #7 !dbg !61 { -functionEntryInitializers: - %0 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 0 - store i64* getelementptr inbounds ([20 x i64], [20 x i64]* @iseqEncodedArray, i64 0, i64 9), i64** %0, align 8, !tbaa !24 - %tooManyArgs = icmp ugt i32 %argc, 0, !dbg !62 - br i1 %tooManyArgs, label %argCountFailBlock, label %fillRequiredArgs, !dbg !62, !prof !63 - -argCountFailBlock: ; preds = %functionEntryInitializers - tail call void @sorbet_raiseArity(i32 %argc, i32 noundef 0, i32 noundef 0) #13, !dbg !62 - unreachable, !dbg !62 - -fillRequiredArgs: ; preds = %functionEntryInitializers - store i64* getelementptr inbounds ([20 x i64], [20 x i64]* @iseqEncodedArray, i64 0, i64 10), i64** %0, align 8, !dbg !64, !tbaa !24 - %"rubyId_$f" = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 6), align 8, !dbg !65, !invariant.load !5 - %1 = tail call %struct.rb_global_entry* @rb_global_entry(i64 %"rubyId_$f") #11, !dbg !65 - %2 = tail call i64 @rb_gvar_get(%struct.rb_global_entry* %1) #11, !dbg !65 - ret i64 %2 -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_A#4read"(i64 %realpath) unnamed_addr #5 { -entryInitializers: - %rubyId_read = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 3), align 8, !invariant.load !5 - %rubyStr_read = load i64, i64* getelementptr inbounds ([6 x i64], [6 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 4), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/globalfields.rb" = load i64, i64* getelementptr inbounds ([6 x i64], [6 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %rubyStr_read, i64 %rubyId_read, i64 %"rubyStr_test/testdata/compiler/globalfields.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 1, i32 noundef 9, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 0) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_A#4read", align 8 - ret void -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_A.13"(i64 %realpath) unnamed_addr #5 { -entryInitializers: - %"rubyId_" = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 7), align 8, !invariant.load !5 - %"rubyStr_" = load i64, i64* getelementptr inbounds ([6 x i64], [6 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 5), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/globalfields.rb" = load i64, i64* getelementptr inbounds ([6 x i64], [6 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_", i64 %"rubyId_", i64 %"rubyStr_test/testdata/compiler/globalfields.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 3, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 4) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_A.13", align 8 - ret void -} - -; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly -declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #8 - -; Function Attrs: nofree nosync nounwind willreturn -declare void @llvm.assume(i1 noundef) #9 - -; Function Attrs: ssp -define linkonce void @const_recompute_A() local_unnamed_addr #5 { - %1 = tail call i64 @sorbet_getConstant(i8* getelementptr inbounds ([133 x i8], [133 x i8]* @sorbet_moduleStringTable, i64 0, i64 56), i64 1) - store i64 %1, i64* @guarded_const_A, align 8 - %2 = load i64, i64* @ruby_vm_global_constant_state, align 8, !tbaa !45 - store i64 %2, i64* @guard_epoch_A, align 8 - ret void -} - -attributes #0 = { noreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #1 = { "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #2 = { argmemonly nofree nosync nounwind willreturn } -attributes #3 = { allocsize(0,1) "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #4 = { nounwind ssp uwtable "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #5 = { ssp } -attributes #6 = { sspreq } -attributes #7 = { nounwind sspreq uwtable } -attributes #8 = { argmemonly nofree nosync nounwind willreturn writeonly } -attributes #9 = { nofree nosync nounwind willreturn } -attributes #10 = { noreturn nounwind } -attributes #11 = { nounwind } -attributes #12 = { nounwind allocsize(0,1) } -attributes #13 = { noreturn } - -!llvm.module.flags = !{!0, !1, !2} -!llvm.dbg.cu = !{!3} - -!0 = !{i32 2, !"Debug Info Version", i32 3} -!1 = !{i32 4, !"cf-protection-return", i32 1} -!2 = !{i32 4, !"cf-protection-branch", i32 1} -!3 = distinct !DICompileUnit(language: DW_LANG_C, file: !4, producer: "Sorbet LLVM", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !5) -!4 = !DIFile(filename: "test/testdata/compiler/globalfields.rb", directory: ".") -!5 = !{} -!6 = !{!7, !7, i64 0} -!7 = !{!"long", !8, i64 0} -!8 = !{!"omnipotent char", !9, i64 0} -!9 = !{!"Simple C/C++ TBAA"} -!10 = !DILocation(line: 14, column: 5, scope: !11) -!11 = distinct !DISubprogram(name: ".", linkageName: "func_.13", scope: null, file: !4, line: 5, type: !12, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!12 = !DISubroutineType(types: !13) -!13 = !{!14} -!14 = !DIBasicType(name: "VALUE", size: 64, encoding: DW_ATE_signed) -!15 = !DILocation(line: 15, column: 6, scope: !11) -!16 = !DILocation(line: 15, column: 1, scope: !11) -!17 = !DILocation(line: 16, column: 1, scope: !11) -!18 = !DILocation(line: 17, column: 6, scope: !11) -!19 = !DILocation(line: 17, column: 1, scope: !11) -!20 = !DILocation(line: 18, column: 1, scope: !11) -!21 = !DILocation(line: 6, column: 3, scope: !22, inlinedAt: !23) -!22 = distinct !DISubprogram(name: "A.", linkageName: "func_A.13L62", scope: null, file: !4, line: 5, type: !12, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!23 = distinct !DILocation(line: 5, column: 1, scope: !11) -!24 = !{!25, !25, i64 0} -!25 = !{!"any pointer", !8, i64 0} -!26 = !{!27, !7, i64 400} -!27 = !{!"rb_vm_struct", !7, i64 0, !28, i64 8, !25, i64 192, !25, i64 200, !25, i64 208, !32, i64 216, !8, i64 224, !29, i64 264, !29, i64 280, !29, i64 296, !29, i64 312, !7, i64 328, !31, i64 336, !31, i64 340, !31, i64 344, !31, i64 344, !31, i64 344, !31, i64 344, !31, i64 348, !7, i64 352, !8, i64 360, !7, i64 400, !7, i64 408, !7, i64 416, !7, i64 424, !7, i64 432, !7, i64 440, !7, i64 448, !25, i64 456, !25, i64 464, !33, i64 472, !34, i64 992, !25, i64 1016, !25, i64 1024, !31, i64 1032, !31, i64 1036, !29, i64 1040, !8, i64 1056, !7, i64 1096, !7, i64 1104, !7, i64 1112, !7, i64 1120, !7, i64 1128, !31, i64 1136, !25, i64 1144, !25, i64 1152, !25, i64 1160, !25, i64 1168, !25, i64 1176, !25, i64 1184, !31, i64 1192, !35, i64 1200, !8, i64 1232} -!28 = !{!"rb_global_vm_lock_struct", !25, i64 0, !8, i64 8, !29, i64 48, !25, i64 64, !31, i64 72, !8, i64 80, !8, i64 128, !31, i64 176, !31, i64 180} -!29 = !{!"list_head", !30, i64 0} -!30 = !{!"list_node", !25, i64 0, !25, i64 8} -!31 = !{!"int", !8, i64 0} -!32 = !{!"long long", !8, i64 0} -!33 = !{!"", !8, i64 0} -!34 = !{!"rb_hook_list_struct", !25, i64 0, !31, i64 8, !31, i64 12, !31, i64 16} -!35 = !{!"", !7, i64 0, !7, i64 8, !7, i64 16, !7, i64 24} -!36 = !{!37, !25, i64 16} -!37 = !{!"rb_execution_context_struct", !25, i64 0, !7, i64 8, !25, i64 16, !25, i64 24, !25, i64 32, !31, i64 40, !31, i64 44, !25, i64 48, !25, i64 56, !25, i64 64, !7, i64 72, !7, i64 80, !25, i64 88, !7, i64 96, !25, i64 104, !25, i64 112, !7, i64 120, !7, i64 128, !8, i64 136, !8, i64 137, !7, i64 144, !38, i64 152} -!38 = !{!"", !25, i64 0, !25, i64 8, !7, i64 16, !8, i64 24} -!39 = !{!40, !25, i64 16} -!40 = !{!"rb_control_frame_struct", !25, i64 0, !25, i64 8, !25, i64 16, !7, i64 24, !25, i64 32, !25, i64 40, !25, i64 48} -!41 = !{!40, !25, i64 32} -!42 = !DILocation(line: 0, scope: !11) -!43 = !DILocation(line: 5, column: 1, scope: !11) -!44 = !DILocation(line: 0, scope: !22, inlinedAt: !23) -!45 = !{!32, !32, i64 0} -!46 = !{!"branch_weights", i32 1, i32 10000} -!47 = !{!48, !31, i64 8} -!48 = !{!"rb_sorbet_param_struct", !49, i64 0, !31, i64 4, !31, i64 8, !31, i64 12, !31, i64 16, !31, i64 20, !31, i64 24, !31, i64 28, !25, i64 32, !31, i64 40, !31, i64 44, !31, i64 48, !31, i64 52, !25, i64 56} -!49 = !{!"", !31, i64 0, !31, i64 0, !31, i64 0, !31, i64 0, !31, i64 0, !31, i64 0, !31, i64 0, !31, i64 0, !31, i64 1, !31, i64 1} -!50 = !{!48, !31, i64 4} -!51 = !{!48, !25, i64 32} -!52 = !DILocation(line: 9, column: 3, scope: !22, inlinedAt: !23) -!53 = !DILocation(line: 16, column: 9, scope: !11) -!54 = !DILocation(line: 19, column: 5, scope: !11) -!55 = distinct !DISubprogram(name: "A#write", linkageName: "func_A#5write", scope: null, file: !4, line: 6, type: !12, scopeLine: 6, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!56 = !DILocation(line: 6, column: 3, scope: !55) -!57 = !{!"branch_weights", i32 4001, i32 4000000} -!58 = !DILocation(line: 6, column: 13, scope: !55) -!59 = !DILocation(line: 7, column: 10, scope: !55) -!60 = !DILocation(line: 7, column: 5, scope: !55) -!61 = distinct !DISubprogram(name: "A#read", linkageName: "func_A#4read", scope: null, file: !4, line: 9, type: !12, scopeLine: 9, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!62 = !DILocation(line: 9, column: 3, scope: !61) -!63 = !{!"branch_weights", i32 1, i32 2000} -!64 = !DILocation(line: 0, scope: !61) -!65 = !DILocation(line: 10, column: 5, scope: !61) diff --git a/test/testdata/compiler/hashes.rb b/test/testdata/compiler/hashes.rb index f585d8d6f3..dfb8331155 100644 --- a/test/testdata/compiler/hashes.rb +++ b/test/testdata/compiler/hashes.rb @@ -1,10 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL puts({a: 1, b: 2}) -# INITIAL-LABEL: define internal i64 @"func_.13 -# INITIAL: call i64 @sorbet_globalConstDupHash -# INITIAL{LITERAL}: } diff --git a/test/testdata/compiler/hello.opt.ll.exp b/test/testdata/compiler/hello.opt.ll.exp deleted file mode 100644 index c787e5e27a..0000000000 --- a/test/testdata/compiler/hello.opt.ll.exp +++ /dev/null @@ -1,229 +0,0 @@ -; ModuleID = 'payload' -source_filename = "llvm-link" -target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } -%struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.__pthread_cond_s } -%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } -%union.anon = type { i64 } -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } -%struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } -%struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } -%struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } -%struct.anon.0 = type { i16, [2 x i8] } -%struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } -%struct.rb_iseq_location_struct = type { i64, i64, i64, i64, i32, %struct.rb_code_location_struct } -%struct.rb_code_location_struct = type { %struct.rb_code_position_struct, %struct.rb_code_position_struct } -%struct.rb_code_position_struct = type { i32, i32 } -%struct.iseq_insn_info = type { %struct.iseq_insn_info_entry*, i32*, i32, %struct.succ_index_table* } -%struct.iseq_insn_info_entry = type opaque -%struct.succ_index_table = type opaque -%struct.iseq_catch_table = type opaque -%union.iseq_inline_storage_entry = type { %struct.iseq_inline_cache_entry } -%struct.iseq_inline_cache_entry = type { i64, %struct.rb_cref_struct*, i64 } -%struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } -%struct.rb_scope_visi_struct = type { i8, [3 x i8] } -%struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } -%struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } -%union.anon.13 = type { %struct.rb_method_cfunc_struct } -%struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%union.anon.15 = type { i32 } -%struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.16 = type { i64, i64, i64, i64* } -%union.anon.17 = type { %struct.anon.18 } -%struct.anon.18 = type { i64, i32 } -%struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } -%struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } -%struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } -%struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } -%struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } -%struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } -%struct.__sigset_t = type { [16 x i64] } -%struct.rb_calling_info = type { i64, i64, i32, i32 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } -%union.anon.9 = type { %union.pthread_cond_t } -%struct.rb_unblock_callback = type { void (i8*)*, i8* } -%struct.rb_mutex_struct = type opaque -%struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.10 = type { %struct.anon.11 } -%struct.anon.11 = type { i64, i64, i32 } -%struct.rb_fiber_struct = type opaque -%struct.anon.5 = type { [65 x i64] } -%struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } -%struct.rb_event_hook_struct = type opaque -%struct.rb_postponed_job_struct = type opaque -%struct.list_head = type { %struct.list_node } -%union.pthread_mutex_t = type { %struct.__pthread_mutex_s } -%struct.__pthread_mutex_s = type { i32, i32, i32, i32, i32, i16, i16, %struct.__pthread_internal_list } -%struct.__pthread_internal_list = type { %struct.__pthread_internal_list*, %struct.__pthread_internal_list* } -%struct.rb_objspace = type opaque -%struct.rb_at_exit_list = type { void (%struct.rb_vm_struct*)*, %struct.rb_at_exit_list* } -%struct.st_table = type { i8, i8, i8, i32, %struct.st_hash_type*, i64, i64*, i64, i64, %struct.st_table_entry* } -%struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } -%struct.st_table_entry = type opaque -%struct.rb_builtin_function = type opaque -%struct.anon.6 = type { i64, i64, i64, i64 } -%struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } -%struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } -%struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } -%struct.rb_call_info_with_kwarg = type { %struct.rb_call_info, %struct.rb_call_info_kw_arg* } -%struct.rb_call_info_kw_arg = type { i32, [1 x i64] } - -@ruby_current_vm_ptr = external local_unnamed_addr global %struct.rb_vm_struct*, align 8 -@ruby_current_execution_context_ptr = external local_unnamed_addr global %struct.rb_execution_context_struct*, align 8 -@rb_eRuntimeError = external local_unnamed_addr global i64, align 8 -@.str.9 = private unnamed_addr constant [95 x i8] c"sorbet_getBuildSCMRevision: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@.str.10 = private unnamed_addr constant [93 x i8] c"sorbet_getIsReleaseBuild: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@"stackFramePrecomputed_func_.13" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@iseqEncodedArray = internal global [5 x i64] zeroinitializer -@fileLineNumberInfo = internal global %struct.SorbetLineNumberInfo zeroinitializer -@ic_puts = internal global %struct.FunctionInlineCache zeroinitializer -@sorbet_moduleStringTable = internal unnamed_addr constant [73 x i8] c"\00test/testdata/compiler/hello.rb\00hello world\00puts\00master\00", align 1 -@sorbet_moduleIDTable = internal unnamed_addr global [2 x i64] zeroinitializer, align 8 -@sorbet_moduleIDDescriptors = internal unnamed_addr constant [2 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 16 }, %struct.rb_code_position_struct { i32 61, i32 4 }], align 8 -@sorbet_moduleRubyStringTable = internal unnamed_addr global [3 x i64] zeroinitializer, align 8 -@sorbet_moduleRubyStringDescriptors = internal unnamed_addr constant [3 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 16 }, %struct.rb_code_position_struct { i32 17, i32 31 }, %struct.rb_code_position_struct { i32 49, i32 11 }], align 8 - -declare %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64, i64, i64, i64, %struct.rb_iseq_struct*, i32, i32, %struct.SorbetLineNumberInfo*, i64*, i32, i32) local_unnamed_addr #0 - -declare void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo*, i64*, i32) local_unnamed_addr #0 - -declare i64 @sorbet_readRealpath() local_unnamed_addr #0 - -declare void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) local_unnamed_addr #0 - -declare i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache*, i64) local_unnamed_addr #0 - -declare void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_iseq_struct*) local_unnamed_addr #0 - -declare void @sorbet_vm_intern_ids(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #0 - -declare void @sorbet_vm_init_string_table(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #0 - -; Function Attrs: noreturn -declare void @rb_raise(i64, i8*, ...) local_unnamed_addr #1 - -; Function Attrs: nounwind ssp uwtable -define weak i32 @sorbet_getIsReleaseBuild() local_unnamed_addr #2 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([93 x i8], [93 x i8]* @.str.10, i64 0, i64 0)) #5 - unreachable -} - -; Function Attrs: nounwind ssp uwtable -define weak i8* @sorbet_getBuildSCMRevision() local_unnamed_addr #2 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([95 x i8], [95 x i8]* @.str.9, i64 0, i64 0)) #5 - unreachable -} - -define internal fastcc void @sorbet_globalConstructors(i64 %realpath) unnamed_addr { -allocRubyIds: - tail call void @sorbet_vm_intern_ids(i64* noundef getelementptr inbounds ([2 x i64], [2 x i64]* @sorbet_moduleIDTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([2 x %struct.rb_code_position_struct], [2 x %struct.rb_code_position_struct]* @sorbet_moduleIDDescriptors, i32 0, i32 0), i32 noundef 2, i8* noundef getelementptr inbounds ([73 x i8], [73 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_vm_init_string_table(i64* noundef getelementptr inbounds ([3 x i64], [3 x i64]* @sorbet_moduleRubyStringTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([3 x %struct.rb_code_position_struct], [3 x %struct.rb_code_position_struct]* @sorbet_moduleRubyStringDescriptors, i32 0, i32 0), i32 noundef 3, i8* noundef getelementptr inbounds ([73 x i8], [73 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef getelementptr inbounds ([5 x i64], [5 x i64]* @iseqEncodedArray, i32 0, i32 0), i32 noundef 5) - tail call fastcc void @"Constr_stackFramePrecomputed_func_.13"(i64 %realpath) - %rubyId_puts = load i64, i64* getelementptr inbounds ([2 x i64], [2 x i64]* @sorbet_moduleIDTable, i64 0, i64 1), align 8, !dbg !10, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts, i64 %rubyId_puts, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !10 - ret void -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_.13"(i64 %realpath) unnamed_addr #3 { -entryInitializers: - %"rubyId_" = load i64, i64* getelementptr inbounds ([2 x i64], [2 x i64]* @sorbet_moduleIDTable, i64 0, i64 0), align 8, !invariant.load !5 - %"rubyStr_" = load i64, i64* getelementptr inbounds ([3 x i64], [3 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 0), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/hello.rb" = load i64, i64* getelementptr inbounds ([3 x i64], [3 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_", i64 %"rubyId_", i64 %"rubyStr_test/testdata/compiler/hello.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 0, i32 noundef 4, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 2) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - ret void -} - -; Function Attrs: sspreq -define void @Init_hello() local_unnamed_addr #4 { -entry: - %realpath = tail call i64 @sorbet_readRealpath() - tail call fastcc void @sorbet_globalConstructors(i64 %realpath) - %0 = load %struct.rb_vm_struct*, %struct.rb_vm_struct** @ruby_current_vm_ptr, align 8, !tbaa !15 - %1 = getelementptr inbounds %struct.rb_vm_struct, %struct.rb_vm_struct* %0, i64 0, i32 18 - %2 = load i64, i64* %1, align 8, !tbaa !17 - %3 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !15 - %4 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %3, i64 0, i32 2 - %5 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %4, align 8, !tbaa !27 - %stackFrame.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - %6 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame.i, %struct.rb_iseq_struct** %6, align 8, !tbaa !30 - %7 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 4 - %8 = load i64*, i64** %7, align 8, !tbaa !32 - %9 = load i64, i64* %8, align 8, !tbaa !6 - %10 = and i64 %9, -33 - store i64 %10, i64* %8, align 8, !tbaa !6 - tail call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %3, %struct.rb_control_frame_struct* %5, %struct.rb_iseq_struct* %stackFrame.i) #6 - %11 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 0 - store i64* getelementptr inbounds ([5 x i64], [5 x i64]* @iseqEncodedArray, i64 0, i64 4), i64** %11, align 8, !dbg !33, !tbaa !15 - %"rubyStr_hello world.i" = load i64, i64* getelementptr inbounds ([3 x i64], [3 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 2), align 8, !dbg !34, !invariant.load !5 - %12 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !10 - %13 = load i64*, i64** %12, align 8, !dbg !10 - store i64 %2, i64* %13, align 8, !dbg !10, !tbaa !6 - %14 = getelementptr inbounds i64, i64* %13, i64 1, !dbg !10 - store i64 %"rubyStr_hello world.i", i64* %14, align 8, !dbg !10, !tbaa !6 - %15 = getelementptr inbounds i64, i64* %14, i64 1, !dbg !10 - store i64* %15, i64** %12, align 8, !dbg !10 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts, i64 0), !dbg !10 - ret void -} - -attributes #0 = { "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #1 = { noreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #2 = { nounwind ssp uwtable "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #3 = { ssp } -attributes #4 = { sspreq } -attributes #5 = { noreturn nounwind } -attributes #6 = { nounwind } - -!llvm.module.flags = !{!0, !1, !2} -!llvm.dbg.cu = !{!3} - -!0 = !{i32 2, !"Debug Info Version", i32 3} -!1 = !{i32 4, !"cf-protection-return", i32 1} -!2 = !{i32 4, !"cf-protection-branch", i32 1} -!3 = distinct !DICompileUnit(language: DW_LANG_C, file: !4, producer: "Sorbet LLVM", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !5) -!4 = !DIFile(filename: "test/testdata/compiler/hello.rb", directory: ".") -!5 = !{} -!6 = !{!7, !7, i64 0} -!7 = !{!"long", !8, i64 0} -!8 = !{!"omnipotent char", !9, i64 0} -!9 = !{!"Simple C/C++ TBAA"} -!10 = !DILocation(line: 4, column: 1, scope: !11) -!11 = distinct !DISubprogram(name: ".", linkageName: "func_.13", scope: null, file: !4, line: 4, type: !12, scopeLine: 4, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!12 = !DISubroutineType(types: !13) -!13 = !{!14} -!14 = !DIBasicType(name: "VALUE", size: 64, encoding: DW_ATE_signed) -!15 = !{!16, !16, i64 0} -!16 = !{!"any pointer", !8, i64 0} -!17 = !{!18, !7, i64 400} -!18 = !{!"rb_vm_struct", !7, i64 0, !19, i64 8, !16, i64 192, !16, i64 200, !16, i64 208, !23, i64 216, !8, i64 224, !20, i64 264, !20, i64 280, !20, i64 296, !20, i64 312, !7, i64 328, !22, i64 336, !22, i64 340, !22, i64 344, !22, i64 344, !22, i64 344, !22, i64 344, !22, i64 348, !7, i64 352, !8, i64 360, !7, i64 400, !7, i64 408, !7, i64 416, !7, i64 424, !7, i64 432, !7, i64 440, !7, i64 448, !16, i64 456, !16, i64 464, !24, i64 472, !25, i64 992, !16, i64 1016, !16, i64 1024, !22, i64 1032, !22, i64 1036, !20, i64 1040, !8, i64 1056, !7, i64 1096, !7, i64 1104, !7, i64 1112, !7, i64 1120, !7, i64 1128, !22, i64 1136, !16, i64 1144, !16, i64 1152, !16, i64 1160, !16, i64 1168, !16, i64 1176, !16, i64 1184, !22, i64 1192, !26, i64 1200, !8, i64 1232} -!19 = !{!"rb_global_vm_lock_struct", !16, i64 0, !8, i64 8, !20, i64 48, !16, i64 64, !22, i64 72, !8, i64 80, !8, i64 128, !22, i64 176, !22, i64 180} -!20 = !{!"list_head", !21, i64 0} -!21 = !{!"list_node", !16, i64 0, !16, i64 8} -!22 = !{!"int", !8, i64 0} -!23 = !{!"long long", !8, i64 0} -!24 = !{!"", !8, i64 0} -!25 = !{!"rb_hook_list_struct", !16, i64 0, !22, i64 8, !22, i64 12, !22, i64 16} -!26 = !{!"", !7, i64 0, !7, i64 8, !7, i64 16, !7, i64 24} -!27 = !{!28, !16, i64 16} -!28 = !{!"rb_execution_context_struct", !16, i64 0, !7, i64 8, !16, i64 16, !16, i64 24, !16, i64 32, !22, i64 40, !22, i64 44, !16, i64 48, !16, i64 56, !16, i64 64, !7, i64 72, !7, i64 80, !16, i64 88, !7, i64 96, !16, i64 104, !16, i64 112, !7, i64 120, !7, i64 128, !8, i64 136, !8, i64 137, !7, i64 144, !29, i64 152} -!29 = !{!"", !16, i64 0, !16, i64 8, !7, i64 16, !8, i64 24} -!30 = !{!31, !16, i64 16} -!31 = !{!"rb_control_frame_struct", !16, i64 0, !16, i64 8, !16, i64 16, !7, i64 24, !16, i64 32, !16, i64 40, !16, i64 48} -!32 = !{!31, !16, i64 32} -!33 = !DILocation(line: 0, scope: !11) -!34 = !DILocation(line: 4, column: 6, scope: !11) diff --git a/test/testdata/compiler/impl_abstract_via_extend.rb b/test/testdata/compiler/impl_abstract_via_extend.rb index 49aa619797..af37592a54 100644 --- a/test/testdata/compiler/impl_abstract_via_extend.rb +++ b/test/testdata/compiler/impl_abstract_via_extend.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true # typed: strict # compiled: true -# run_filecheck: INITIAL module IFoo extend T::Helpers @@ -12,8 +11,5 @@ module IFoo def foo; end end -# INITIAL{LITERAL}-LABEL: define i64 @"func_IFoo#foo" -# INITIAL: call i64 @sorbet_callSuper -# INITIAL{LITERAL}: } require_relative './impl_abstract_via_extend__1' diff --git a/test/testdata/compiler/impl_abstract_via_include.rb b/test/testdata/compiler/impl_abstract_via_include.rb index 4622d893fe..2f9f2a9079 100644 --- a/test/testdata/compiler/impl_abstract_via_include.rb +++ b/test/testdata/compiler/impl_abstract_via_include.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL module IFoo extend T::Sig @@ -12,9 +11,6 @@ module IFoo def foo; end end -# INITIAL{LITERAL}-LABEL: define i64 @"func_IFoo#foo" -# INITIAL: call i64 @sorbet_callSuper -# INITIAL{LITERAL}: } module FooImpl extend T::Sig diff --git a/test/testdata/compiler/instance_variable_fastpaths.rb b/test/testdata/compiler/instance_variable_fastpaths.rb index c569eaf2b7..0aa6fd1edb 100644 --- a/test/testdata/compiler/instance_variable_fastpaths.rb +++ b/test/testdata/compiler/instance_variable_fastpaths.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL class A def initialize(x) @@ -12,9 +11,6 @@ def fast_get instance_variable_get(:@x) end -# INITIAL-LABEL: define internal i64 @"func_A#8fast_get" -# INITIAL: call i64 @sorbet_vm_instance_variable_get -# INITIAL{LITERAL}: } # Can't really test too many or too few args because those won't get past Sorbet. @@ -22,25 +18,16 @@ def non_constant_arg_get(sym) instance_variable_get(sym) end -# INITIAL-LABEL: define internal i64 @"func_A#20non_constant_arg_get" -# INITIAL-NOT: call i64 @sorbet_vm_instance_variable_get -# INITIAL{LITERAL}: } def fast_set(x) instance_variable_set(:@x, x) end -# INITIAL-LABEL: define internal i64 @"func_A#8fast_set" -# INITIAL: call i64 @sorbet_vm_instance_variable_set -# INITIAL{LITERAL}: } def non_constant_arg_set(sym, value) instance_variable_set(sym, value) end -# INITIAL-LABEL: define internal i64 @"func_A#20non_constant_arg_set" -# INITIAL-NOT: call i64 @sorbet_vm_instance_variable_set -# INITIAL{LITERAL}: } end @@ -65,17 +52,11 @@ def instance_variable_get "nothing" end -# INITIAL-LABEL: define internal i64 @"func_FewArgs#21instance_variable_get" -# INITIAL-NOT: call i64 @sorbet_vm_instance_variable_get -# INITIAL{LITERAL}: } def instance_variable_set "nada" end -# INITIAL-LABEL: define internal i64 @"func_FewArgs#21instance_variable_set" -# INITIAL-NOT: call i64 @sorbet_vm_instance_variable_set -# INITIAL{LITERAL}: } end @@ -88,17 +69,11 @@ def instance_variable_get(x, y) x + y end -# INITIAL-LABEL: define internal i64 @"func_ManyArgs#21instance_variable_get" -# INITIAL-NOT: call i64 @sorbet_vm_instance_variable_get -# INITIAL{LITERAL}: } def instance_variable_set(x, y, z) x + y + z end -# INITIAL-LABEL: define internal i64 @"func_ManyArgs#21instance_variable_set" -# INITIAL-NOT: call i64 @sorbet_vm_instance_variable_set -# INITIAL{LITERAL}: } end diff --git a/test/testdata/compiler/instancefields.rb b/test/testdata/compiler/instancefields.rb index 3a169de946..c9431b4486 100644 --- a/test/testdata/compiler/instancefields.rb +++ b/test/testdata/compiler/instancefields.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL -# run_filecheck: OPT class A def write(v) @@ -13,21 +11,9 @@ def read end end -# INITIAL-LABEL: define internal i64 @"func_A#5write" -# INITIAL: call void @sorbet_instanceVariableSet -# INITIAL{LITERAL}: } -# INITIAL-LABEL: define internal i64 @"func_A#4read" -# INITIAL: call i64 @sorbet_instanceVariableGet -# INITIAL{LITERAL}: } -# OPT-LABEL: define internal i64 @"func_A#5write" -# OPT: call void @sorbet_vm_setivar -# OPT{LITERAL}: } -# OPT-LABEL: define internal i64 @"func_A#4read" -# OPT: call i64 @sorbet_vm_getivar -# OPT{LITERAL}: } a = A.new b = A.new diff --git a/test/testdata/compiler/intrinsics/array_concat.rb b/test/testdata/compiler/intrinsics/array_concat.rb index 92e2cb474a..cb14731ee4 100644 --- a/test/testdata/compiler/intrinsics/array_concat.rb +++ b/test/testdata/compiler/intrinsics/array_concat.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL def test_array_concat p ([1, 2, 3].concat) @@ -14,11 +13,3 @@ def test_array_concat test_array_concat -# INITIAL-LABEL: define internal i64 @"func_Object#17test_array_concat" -# INITIAL: call i64 @sorbet_int_rb_ary_concat_multi( -# INITIAL: call i64 @sorbet_int_rb_ary_concat_multi( -# INITIAL: call i64 @sorbet_int_rb_ary_concat_multi( -# INITIAL: call i64 @sorbet_int_rb_ary_concat_multi( -# INITIAL: call i64 @sorbet_int_rb_ary_concat_multi( -# INITIAL: call i64 @sorbet_int_rb_ary_concat_multi( -# INITIAL{LITERAL}: } diff --git a/test/testdata/compiler/intrinsics/array_diff.rb b/test/testdata/compiler/intrinsics/array_diff.rb index dab04c8fcf..6058313b29 100644 --- a/test/testdata/compiler/intrinsics/array_diff.rb +++ b/test/testdata/compiler/intrinsics/array_diff.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL def test_array_diff p ([] - [1,2,3]) @@ -14,11 +13,3 @@ def test_array_diff test_array_diff -# INITIAL-LABEL: define internal i64 @"func_Object#15test_array_diff" -# INITIAL: call i64 @sorbet_int_rb_ary_diff( -# INITIAL: call i64 @sorbet_int_rb_ary_diff( -# INITIAL: call i64 @sorbet_int_rb_ary_diff( -# INITIAL: call i64 @sorbet_int_rb_ary_diff( -# INITIAL: call i64 @sorbet_int_rb_ary_diff( -# INITIAL: call i64 @sorbet_int_rb_ary_diff( -# INITIAL{LITERAL}: } diff --git a/test/testdata/compiler/intrinsics/array_each_blk.rb b/test/testdata/compiler/intrinsics/array_each_blk.rb index 5477567481..f9f506d263 100644 --- a/test/testdata/compiler/intrinsics/array_each_blk.rb +++ b/test/testdata/compiler/intrinsics/array_each_blk.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL result = [1, 2, 3].each do |x| puts "-- #{x} --" @@ -15,6 +14,3 @@ end p result - -# INITIAL-COUNT-2: call i64 @sorbet_callIntrinsicInlineBlock -# INITIAL-NOT: call i64 @sorbet_callIntrinsicInlineBlock diff --git a/test/testdata/compiler/intrinsics/array_each_with_object/with_block.rb b/test/testdata/compiler/intrinsics/array_each_with_object/with_block.rb index 0ac65ec86e..6cc03c7289 100644 --- a/test/testdata/compiler/intrinsics/array_each_with_object/with_block.rb +++ b/test/testdata/compiler/intrinsics/array_each_with_object/with_block.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL a = T.let([1, 2, 3, 4, 5], T::Array[Integer]) @@ -25,6 +24,3 @@ p result p state - -# INITIAL-COUNT-2: call i64 @sorbet_callIntrinsicInlineBlock -# INITIAL-NOT: call i64 @sorbet_callIntrinsicInlineBlock diff --git a/test/testdata/compiler/intrinsics/array_first.rb b/test/testdata/compiler/intrinsics/array_first.rb index fd824cbe17..a8365bb994 100644 --- a/test/testdata/compiler/intrinsics/array_first.rb +++ b/test/testdata/compiler/intrinsics/array_first.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL def test_array_first p [].first @@ -15,12 +14,3 @@ def test_array_first test_array_first -# INITIAL-LABEL: define internal i64 @"func_Object#16test_array_first" -# INITIAL: call i64 @sorbet_int_rb_ary_first( -# INITIAL: call i64 @sorbet_int_rb_ary_first( -# INITIAL: call i64 @sorbet_int_rb_ary_first( -# INITIAL: call i64 @sorbet_int_rb_ary_first( -# INITIAL: call i64 @sorbet_int_rb_ary_first( -# INITIAL: call i64 @sorbet_int_rb_ary_first( -# INITIAL: call i64 @sorbet_int_rb_ary_first( -# INITIAL{LITERAL}: } diff --git a/test/testdata/compiler/intrinsics/array_flatten.rb b/test/testdata/compiler/intrinsics/array_flatten.rb index 4e06a9c5c1..ede7b36b62 100644 --- a/test/testdata/compiler/intrinsics/array_flatten.rb +++ b/test/testdata/compiler/intrinsics/array_flatten.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL def test_array_flatten p ([].flatten) @@ -17,14 +16,3 @@ def test_array_flatten test_array_flatten -# INITIAL-LABEL: define internal i64 @"func_Object#18test_array_flatten" -# INITIAL: call i64 @sorbet_int_rb_ary_flatten( -# INITIAL: call i64 @sorbet_int_rb_ary_flatten( -# INITIAL: call i64 @sorbet_int_rb_ary_flatten( -# INITIAL: call i64 @sorbet_int_rb_ary_flatten( -# INITIAL: call i64 @sorbet_int_rb_ary_flatten( -# INITIAL: call i64 @sorbet_int_rb_ary_flatten( -# INITIAL: call i64 @sorbet_int_rb_ary_flatten( -# INITIAL: call i64 @sorbet_int_rb_ary_flatten( -# INITIAL: call i64 @sorbet_int_rb_ary_flatten( -# INITIAL{LITERAL}: } diff --git a/test/testdata/compiler/intrinsics/array_join.rb b/test/testdata/compiler/intrinsics/array_join.rb index fbda7ebf26..a1c5693207 100644 --- a/test/testdata/compiler/intrinsics/array_join.rb +++ b/test/testdata/compiler/intrinsics/array_join.rb @@ -1,14 +1,8 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL puts ["ha","ho","hey"].join("---") puts [1,2,"hey"].join(",") puts [1,2,3].join -# INITIAL-LABEL: define internal i64 @"func_.13 -# INITIAL: call i64 @sorbet_int_rb_ary_join_m( -# INITIAL: call i64 @sorbet_int_rb_ary_join_m( -# INITIAL: call i64 @sorbet_int_rb_ary_join_m( -# INITIAL{LITERAL}: } diff --git a/test/testdata/compiler/intrinsics/array_map_bang_blk.rb b/test/testdata/compiler/intrinsics/array_map_bang_blk.rb index 521c28dacc..aaedfc30ef 100644 --- a/test/testdata/compiler/intrinsics/array_map_bang_blk.rb +++ b/test/testdata/compiler/intrinsics/array_map_bang_blk.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL result = [1, 2, 3] @@ -10,9 +9,6 @@ x + 1 end -# INITIAL-LABEL: define internal i64 @"func_.13 -# INITIAL: call i64 @sorbet_callIntrinsicInlineBlock_noBreak(i64 (i64)* @forward_sorbet_rb_array_collect_bang -# INITIAL{LITERAL}: } p result diff --git a/test/testdata/compiler/intrinsics/array_map_blk.rb b/test/testdata/compiler/intrinsics/array_map_blk.rb index 0cfce3bc17..814f3db4ba 100644 --- a/test/testdata/compiler/intrinsics/array_map_blk.rb +++ b/test/testdata/compiler/intrinsics/array_map_blk.rb @@ -1,25 +1,15 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL -# run_filecheck: OPT # Break is only used in the second call to `Array#map`, so we should see that the first version uses the `noBreak` # variant of `sorbet_callIntrinsicInlineBlock`, and the second does not. -# INITIAL-LABEL: define internal i64 @"func_.13 -# INITIAL: call i64 @sorbet_callIntrinsicInlineBlock_noBreak(i64 (i64)* @forward_sorbet_rb_array_collect -# INITIAL: call i64 @sorbet_callIntrinsicInlineBlock(i64 (i64)* @forward_sorbet_rb_array_collect -# INITIAL{LITERAL}: } # Additionally, in the optimized output, we should see exactly one call to `sorbet_rb_iterate` (from the use of # `sorbet_callIntrinsicInlineBlock`) in the Init function, as the root static init will be inlined there. -# OPT-LABEL: define void @Init_array_map_blk() -# OPT: call i64 @sorbet_rb_iterate( -# OPT-NOT: call i64 @sorbet_rb_iterate( -# OPT{LITERAL}: } result = [1, 2, 3].map do |x| puts "-- #{x} --" diff --git a/test/testdata/compiler/intrinsics/array_map_blk_break.rb b/test/testdata/compiler/intrinsics/array_map_blk_break.rb index c1ace79cd2..e75c84e7e9 100644 --- a/test/testdata/compiler/intrinsics/array_map_blk_break.rb +++ b/test/testdata/compiler/intrinsics/array_map_blk_break.rb @@ -1,11 +1,9 @@ # typed: true # compiled: true # frozen_string_literal: true -# run_filecheck: LOWERED class NotArray def size - # LOWERED-label: define internal i64 @"func_NotArray#size" "hello there" end end @@ -13,29 +11,22 @@ def size # Verify that breaking will prevent the type assertion on the result of the call # to `Array#map` def main1 - # LOWERED-LABEL: define internal i64 @"func_Object#5main1" - xs = [1,2,3] ys = xs.map{|a| break NotArray.new} - # LOWERED-NOT: @llvm.assume - # LOWERED: call i1 @sorbet_i_isa_Array puts ys.size end # Verify that not breaking will permit the type assertion on the result of the # call to `Array#map` def main2 - # LOWERED-LABEL: define internal i64 @"func_Object#5main2" - xs = [1,2,3] ys = xs.map{|a| NotArray.new} # The call to @llvm.assume will disappear by this point in the lowered output, # and that's what will cause the isa_Array call to stick around. - # LOWERED-NOT: call i1 @sorbet_i_isa_Array puts ys.size end diff --git a/test/testdata/compiler/intrinsics/array_push.rb b/test/testdata/compiler/intrinsics/array_push.rb index cef63056c4..2990b70b1f 100644 --- a/test/testdata/compiler/intrinsics/array_push.rb +++ b/test/testdata/compiler/intrinsics/array_push.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL def test_array_push p ([1, 2, 3].push) @@ -18,11 +17,3 @@ def test_array_push test_array_push -# INITIAL-LABEL: define internal i64 @"func_Object#15test_array_push" -# INITIAL: call i64 @sorbet_int_rb_ary_push_m( -# INITIAL: call i64 @sorbet_int_rb_ary_push_m( -# INITIAL: call i64 @sorbet_int_rb_ary_push_m( -# INITIAL: call i64 @sorbet_int_rb_ary_push_m( -# INITIAL: call i64 @sorbet_int_rb_ary_push_m( -# INITIAL: call i64 @sorbet_int_rb_ary_push_m( -# INITIAL{LITERAL}: } diff --git a/test/testdata/compiler/intrinsics/array_reject_bang.rb b/test/testdata/compiler/intrinsics/array_reject_bang.rb index a26fe57dd0..f79fb0b49b 100644 --- a/test/testdata/compiler/intrinsics/array_reject_bang.rb +++ b/test/testdata/compiler/intrinsics/array_reject_bang.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL def test_blk arr = [1, 2, 3, 4, 5] @@ -13,9 +12,6 @@ def test_blk test_blk -# INITIAL-LABEL: define internal i64 @"func_Object#8test_blk"( -# INITIAL: call i64 @sorbet_callIntrinsicInlineBlock_noBreak(i64 (i64)* @forward_sorbet_rb_array_reject_bang_withBlock -# INITIAL{LITERAL}: } def test_blk_raise arr = [1, 2, 3, 4, 5] @@ -36,9 +32,6 @@ def test_blk_raise test_blk_raise -# INITIAL-LABEL: define internal i64 @"func_Object#14test_blk_raise"( -# INITIAL: call i64 @sorbet_callIntrinsicInlineBlock_noBreak(i64 (i64)* @forward_sorbet_rb_array_reject_bang_withBlock -# INITIAL{LITERAL}: } def test_blk_break arr = [1, 2, 3, 4, 5] @@ -55,9 +48,6 @@ def test_blk_break test_blk_break -# INITIAL-LABEL: define internal i64 @"func_Object#14test_blk_break"( -# INITIAL: call i64 @sorbet_callIntrinsicInlineBlock(i64 (i64)* @forward_sorbet_rb_array_reject_bang_withBlock -# INITIAL{LITERAL}: } def test_no_blk arr = [1, 2, 3, 4, 5] @@ -74,6 +64,3 @@ def test_no_blk test_no_blk -# INITIAL-LABEL: define internal i64 @"func_Object#11test_no_blk"( -# INITIAL: call i64 @sorbet_rb_array_reject_bang( -# INITIAL{LITERAL}: } diff --git a/test/testdata/compiler/intrinsics/array_reject_blk.rb b/test/testdata/compiler/intrinsics/array_reject_blk.rb index 3e87861230..14a8636f65 100644 --- a/test/testdata/compiler/intrinsics/array_reject_blk.rb +++ b/test/testdata/compiler/intrinsics/array_reject_blk.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL result = [1, 2, 3, 4, 5, 6].reject do |x| x.even? @@ -13,10 +12,6 @@ break "ope" end) -# INITIAL-LABEL: define internal i64 @"func_.13 -# INITIAL: call i64 @sorbet_callIntrinsicInlineBlock_noBreak(i64 (i64)* @forward_sorbet_rb_array_reject_withBlock -# INITIAL: call i64 @sorbet_callIntrinsicInlineBlock(i64 (i64)* @forward_sorbet_rb_array_reject_withBlock -# INITIAL{LITERAL}: } def foo(&blk) result = [1, 2, 3, 4, 5, 6].reject(&blk) diff --git a/test/testdata/compiler/intrinsics/array_reject_no_blk.rb b/test/testdata/compiler/intrinsics/array_reject_no_blk.rb index 809010e513..66a3b79d18 100644 --- a/test/testdata/compiler/intrinsics/array_reject_no_blk.rb +++ b/test/testdata/compiler/intrinsics/array_reject_no_blk.rb @@ -1,13 +1,9 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL enumerator = ['zero', 'one', 'two'].reject -# INITIAL-LABEL: define internal i64 @"func_.13 -# INITIAL: call i64 @sorbet_rb_array_reject( -# INITIAL{LITERAL}: } puts enumerator.class diff --git a/test/testdata/compiler/intrinsics/array_size.rb b/test/testdata/compiler/intrinsics/array_size.rb index 27e2ae8d50..a397dbeb87 100644 --- a/test/testdata/compiler/intrinsics/array_size.rb +++ b/test/testdata/compiler/intrinsics/array_size.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL extend T::Sig @@ -10,18 +9,12 @@ def ary_length(ary) ary.length end -# INITIAL-LABEL: "func_Object#10ary_length" -# INITIAL: call i64 @sorbet_rb_array_len -# INITIAL{LITERAL}: } sig {params(ary: T::Array[Integer]).returns(Integer)} def ary_size(ary) ary.size end -# INITIAL-LABEL: "func_Object#8ary_size" -# INITIAL: call i64 @sorbet_rb_array_len -# INITIAL{LITERAL}: } p ary_length([1, 2, 3]) p ary_size([1, 2, 3]) diff --git a/test/testdata/compiler/intrinsics/array_uniq.rb b/test/testdata/compiler/intrinsics/array_uniq.rb index a95143a13c..2b2f6db65e 100644 --- a/test/testdata/compiler/intrinsics/array_uniq.rb +++ b/test/testdata/compiler/intrinsics/array_uniq.rb @@ -1,17 +1,12 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL def no_block p ["yo","yo","yah","ah","oh","yo","yah","oh","oh","eh"].uniq p [1, 2, 3, 2, 3, 4, 1, 1, 5, 8, 6].uniq end -# INITIAL-LABEL: define internal i64 @"func_Object#8no_block" -# INITIAL: call i64 @sorbet_rb_array_uniq( -# INITIAL: call i64 @sorbet_rb_array_uniq( -# INITIAL{LITERAL}: } no_block @@ -22,11 +17,5 @@ def with_block p [1, 2, 3, 4].uniq { break "ope" } end -# INITIAL-LABEL: define internal i64 @"func_Object#10with_block" -# INITIAL: call i64 @sorbet_callIntrinsicInlineBlock_noBreak(i64 (i64)* @forward_sorbet_rb_array_uniq_withBlock -# INITIAL: call i64 @sorbet_callIntrinsicInlineBlock_noBreak(i64 (i64)* @forward_sorbet_rb_array_uniq_withBlock -# INITIAL: call i64 @sorbet_callIntrinsicInlineBlock_noBreak(i64 (i64)* @forward_sorbet_rb_array_uniq_withBlock -# INITIAL: call i64 @sorbet_callIntrinsicInlineBlock(i64 (i64)* @forward_sorbet_rb_array_uniq_withBlock -# INITIAL{LITERAL}: } with_block diff --git a/test/testdata/compiler/intrinsics/bang.opt.ll.exp b/test/testdata/compiler/intrinsics/bang.opt.ll.exp deleted file mode 100644 index ea541341c9..0000000000 --- a/test/testdata/compiler/intrinsics/bang.opt.ll.exp +++ /dev/null @@ -1,608 +0,0 @@ -; ModuleID = 'payload' -source_filename = "llvm-link" -target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } -%struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } -%struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } -%struct.anon.0 = type { i16, [2 x i8] } -%struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } -%struct.rb_iseq_location_struct = type { i64, i64, i64, i64, i32, %struct.rb_code_location_struct } -%struct.rb_code_location_struct = type { %struct.rb_code_position_struct, %struct.rb_code_position_struct } -%struct.rb_code_position_struct = type { i32, i32 } -%struct.iseq_insn_info = type { %struct.iseq_insn_info_entry*, i32*, i32, %struct.succ_index_table* } -%struct.iseq_insn_info_entry = type opaque -%struct.succ_index_table = type opaque -%struct.iseq_catch_table = type opaque -%union.iseq_inline_storage_entry = type { %struct.iseq_inline_cache_entry } -%struct.iseq_inline_cache_entry = type { i64, %struct.rb_cref_struct*, i64 } -%struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } -%struct.rb_scope_visi_struct = type { i8, [3 x i8] } -%struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } -%struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } -%union.anon.13 = type { %struct.rb_method_cfunc_struct } -%struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%struct.rb_calling_info = type { i64, i64, i32, i32 } -%union.anon.15 = type { i32 } -%struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.16 = type { i64, i64, i64, i64* } -%union.anon.17 = type { %struct.anon.18 } -%struct.anon.18 = type { i64, i32 } -%struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } -%struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } -%struct.rb_fiber_struct = type opaque -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } -%struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } -%struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.__pthread_cond_s } -%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } -%union.anon = type { i64 } -%struct.anon.5 = type { [65 x i64] } -%struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } -%struct.rb_event_hook_struct = type opaque -%struct.rb_postponed_job_struct = type opaque -%struct.list_head = type { %struct.list_node } -%struct.rb_objspace = type opaque -%struct.rb_at_exit_list = type { void (%struct.rb_vm_struct*)*, %struct.rb_at_exit_list* } -%struct.rb_builtin_function = type opaque -%struct.anon.6 = type { i64, i64, i64, i64 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } -%union.anon.9 = type { %union.pthread_cond_t } -%union.pthread_mutex_t = type { %struct.__pthread_mutex_s } -%struct.__pthread_mutex_s = type { i32, i32, i32, i32, i32, i16, i16, %struct.__pthread_internal_list } -%struct.__pthread_internal_list = type { %struct.__pthread_internal_list*, %struct.__pthread_internal_list* } -%struct.rb_unblock_callback = type { void (i8*)*, i8* } -%struct.rb_mutex_struct = type opaque -%struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.10 = type { %struct.anon.11 } -%struct.anon.11 = type { i64, i64, i32 } -%struct.st_table = type { i8, i8, i8, i32, %struct.st_hash_type*, i64, i64*, i64, i64, %struct.st_table_entry* } -%struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } -%struct.st_table_entry = type opaque -%struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } -%struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } -%struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } -%struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } -%struct.__sigset_t = type { [16 x i64] } -%struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } -%struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } -%struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } -%struct.rb_call_info_with_kwarg = type { %struct.rb_call_info, %struct.rb_call_info_kw_arg* } -%struct.rb_call_info_kw_arg = type { i32, [1 x i64] } - -@ruby_current_execution_context_ptr = external local_unnamed_addr global %struct.rb_execution_context_struct*, align 8 -@ruby_vm_global_constant_state = external local_unnamed_addr global i64, align 8 -@rb_eRuntimeError = external local_unnamed_addr global i64, align 8 -@.str.9 = private unnamed_addr constant [95 x i8] c"sorbet_getBuildSCMRevision: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@.str.10 = private unnamed_addr constant [93 x i8] c"sorbet_getIsReleaseBuild: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@"stackFramePrecomputed_func_.13" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@iseqEncodedArray = internal global [23 x i64] zeroinitializer -@fileLineNumberInfo = internal global %struct.SorbetLineNumberInfo zeroinitializer -@ic_test = internal global %struct.FunctionInlineCache zeroinitializer -@"stackFramePrecomputed_func_Bad#1!" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@ic_puts = internal global %struct.FunctionInlineCache zeroinitializer -@"stackFramePrecomputed_func_Bad.13" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@stackFramePrecomputed_func_Main.4test = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"ic_!" = internal global %struct.FunctionInlineCache zeroinitializer -@ic_puts.1 = internal global %struct.FunctionInlineCache zeroinitializer -@"ic_!.2" = internal global %struct.FunctionInlineCache zeroinitializer -@ic_puts.3 = internal global %struct.FunctionInlineCache zeroinitializer -@"ic_!.4" = internal global %struct.FunctionInlineCache zeroinitializer -@ic_puts.5 = internal global %struct.FunctionInlineCache zeroinitializer -@"ic_!.6" = internal global %struct.FunctionInlineCache zeroinitializer -@ic_puts.7 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_new = internal global %struct.FunctionInlineCache zeroinitializer -@ic_initialize = internal global %struct.FunctionInlineCache zeroinitializer -@"ic_!.8" = internal global %struct.FunctionInlineCache zeroinitializer -@ic_puts.9 = internal global %struct.FunctionInlineCache zeroinitializer -@"stackFramePrecomputed_func_Main.13" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@sorbet_moduleStringTable = internal unnamed_addr constant [166 x i8] c"\00test/testdata/compiler/intrinsics/bang.rb\00Bad\00Object\00Main\00test\00master\00!\00bad bang overload\00puts\00\00normal\00hello\00new\00initialize\00\00", align 1 -@sorbet_moduleIDTable = internal unnamed_addr global [9 x i64] zeroinitializer, align 8 -@sorbet_moduleIDDescriptors = internal unnamed_addr constant [9 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 16 }, %struct.rb_code_position_struct { i32 75, i32 4 }, %struct.rb_code_position_struct { i32 87, i32 1 }, %struct.rb_code_position_struct { i32 107, i32 4 }, %struct.rb_code_position_struct { i32 112, i32 11 }, %struct.rb_code_position_struct { i32 124, i32 6 }, %struct.rb_code_position_struct { i32 137, i32 3 }, %struct.rb_code_position_struct { i32 141, i32 10 }, %struct.rb_code_position_struct { i32 152, i32 13 }], align 8 -@sorbet_moduleRubyStringTable = internal unnamed_addr global [8 x i64] zeroinitializer, align 8 -@sorbet_moduleRubyStringDescriptors = internal unnamed_addr constant [8 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 16 }, %struct.rb_code_position_struct { i32 17, i32 41 }, %struct.rb_code_position_struct { i32 87, i32 1 }, %struct.rb_code_position_struct { i32 89, i32 17 }, %struct.rb_code_position_struct { i32 112, i32 11 }, %struct.rb_code_position_struct { i32 75, i32 4 }, %struct.rb_code_position_struct { i32 131, i32 5 }, %struct.rb_code_position_struct { i32 152, i32 13 }], align 8 -@rb_cObject = external local_unnamed_addr constant i64 -@guard_epoch_Bad = linkonce local_unnamed_addr global i64 0 -@guarded_const_Bad = linkonce local_unnamed_addr global i64 0 -@guard_epoch_Main = linkonce local_unnamed_addr global i64 0 -@guarded_const_Main = linkonce local_unnamed_addr global i64 0 - -; Function Attrs: noreturn -declare void @sorbet_raiseArity(i32, i32, i32) local_unnamed_addr #0 - -declare %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64, i64, i64, i64, %struct.rb_iseq_struct*, i32, i32, %struct.SorbetLineNumberInfo*, i64*, i32, i32) local_unnamed_addr #1 - -declare void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo*, i64*, i32) local_unnamed_addr #1 - -declare i64 @sorbet_getConstant(i8*, i64) local_unnamed_addr #1 - -declare i64 @sorbet_readRealpath() local_unnamed_addr #1 - -declare %struct.rb_control_frame_struct* @sorbet_pushStaticInitFrame(i64) local_unnamed_addr #1 - -declare void @sorbet_popFrame() local_unnamed_addr #1 - -declare void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) local_unnamed_addr #1 - -declare i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache*, i64) local_unnamed_addr #1 - -declare void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_iseq_struct*) local_unnamed_addr #1 - -declare void @sorbet_vm_define_method(i64, i8*, i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)*, i8*, %struct.rb_iseq_struct*, i1 zeroext) local_unnamed_addr #1 - -declare void @sorbet_vm_intern_ids(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #1 - -declare void @sorbet_vm_init_string_table(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #1 - -declare i64 @sorbet_maybeAllocateObjectFastPath(i64, %struct.FunctionInlineCache*) local_unnamed_addr #1 - -declare i64 @sorbet_vm_bang(%struct.FunctionInlineCache*, %struct.rb_control_frame_struct*, i64) local_unnamed_addr #1 - -declare i64 @rb_define_module(i8*) local_unnamed_addr #1 - -declare i64 @rb_define_class(i8*, i64) local_unnamed_addr #1 - -; Function Attrs: noreturn -declare void @rb_raise(i64, i8*, ...) local_unnamed_addr #0 - -; Function Attrs: allocsize(0,1) -declare noalias nonnull i8* @ruby_xcalloc(i64, i64) local_unnamed_addr #2 - -; Function Attrs: nounwind ssp uwtable -define weak i32 @sorbet_getIsReleaseBuild() local_unnamed_addr #3 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([93 x i8], [93 x i8]* @.str.10, i64 0, i64 0)) #9 - unreachable -} - -; Function Attrs: nounwind ssp uwtable -define weak i8* @sorbet_getBuildSCMRevision() local_unnamed_addr #3 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([95 x i8], [95 x i8]* @.str.9, i64 0, i64 0)) #9 - unreachable -} - -define internal fastcc void @sorbet_globalConstructors(i64 %realpath) unnamed_addr { -allocRubyIds: - tail call void @sorbet_vm_intern_ids(i64* noundef getelementptr inbounds ([9 x i64], [9 x i64]* @sorbet_moduleIDTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([9 x %struct.rb_code_position_struct], [9 x %struct.rb_code_position_struct]* @sorbet_moduleIDDescriptors, i32 0, i32 0), i32 noundef 9, i8* noundef getelementptr inbounds ([166 x i8], [166 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_vm_init_string_table(i64* noundef getelementptr inbounds ([8 x i64], [8 x i64]* @sorbet_moduleRubyStringTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([8 x %struct.rb_code_position_struct], [8 x %struct.rb_code_position_struct]* @sorbet_moduleRubyStringDescriptors, i32 0, i32 0), i32 noundef 8, i8* noundef getelementptr inbounds ([166 x i8], [166 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef getelementptr inbounds ([23 x i64], [23 x i64]* @iseqEncodedArray, i32 0, i32 0), i32 noundef 23) - tail call fastcc void @"Constr_stackFramePrecomputed_func_.13"(i64 %realpath) - %rubyId_test = load i64, i64* getelementptr inbounds ([9 x i64], [9 x i64]* @sorbet_moduleIDTable, i64 0, i64 1), align 8, !dbg !10, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_test, i64 %rubyId_test, i32 noundef 16, i32 noundef 0, i32 noundef 0), !dbg !10 - tail call fastcc void @"Constr_stackFramePrecomputed_func_Bad#1!"(i64 %realpath) - %rubyId_puts = load i64, i64* getelementptr inbounds ([9 x i64], [9 x i64]* @sorbet_moduleIDTable, i64 0, i64 3), align 8, !dbg !15, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts, i64 %rubyId_puts, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !15 - tail call fastcc void @"Constr_stackFramePrecomputed_func_Bad.13"(i64 %realpath) - tail call fastcc void @Constr_stackFramePrecomputed_func_Main.4test(i64 %realpath) - %"rubyId_!" = load i64, i64* getelementptr inbounds ([9 x i64], [9 x i64]* @sorbet_moduleIDTable, i64 0, i64 2), align 8, !dbg !17, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @"ic_!", i64 %"rubyId_!", i32 noundef 16, i32 noundef 0, i32 noundef 0), !dbg !17 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts.1, i64 %rubyId_puts, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !19 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @"ic_!.2", i64 %"rubyId_!", i32 noundef 16, i32 noundef 0, i32 noundef 0), !dbg !20 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts.3, i64 %rubyId_puts, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !21 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @"ic_!.4", i64 %"rubyId_!", i32 noundef 16, i32 noundef 0, i32 noundef 0), !dbg !22 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts.5, i64 %rubyId_puts, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !23 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @"ic_!.6", i64 %"rubyId_!", i32 noundef 16, i32 noundef 0, i32 noundef 0), !dbg !24 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts.7, i64 %rubyId_puts, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !25 - %rubyId_new = load i64, i64* getelementptr inbounds ([9 x i64], [9 x i64]* @sorbet_moduleIDTable, i64 0, i64 6), align 8, !dbg !26, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_new, i64 %rubyId_new, i32 noundef 16, i32 noundef 0, i32 noundef 0), !dbg !26 - %rubyId_initialize = load i64, i64* getelementptr inbounds ([9 x i64], [9 x i64]* @sorbet_moduleIDTable, i64 0, i64 7), align 8, !dbg !26, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_initialize, i64 %rubyId_initialize, i32 noundef 20, i32 noundef 0, i32 noundef 0), !dbg !26 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @"ic_!.8", i64 %"rubyId_!", i32 noundef 16, i32 noundef 0, i32 noundef 0), !dbg !27 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts.9, i64 %rubyId_puts, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !28 - tail call fastcc void @"Constr_stackFramePrecomputed_func_Main.13"(i64 %realpath) - ret void -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_.13"(i64 %realpath) unnamed_addr #4 { -entryInitializers: - %"rubyId_" = load i64, i64* getelementptr inbounds ([9 x i64], [9 x i64]* @sorbet_moduleIDTable, i64 0, i64 0), align 8, !invariant.load !5 - %"rubyStr_" = load i64, i64* getelementptr inbounds ([8 x i64], [8 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 0), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/intrinsics/bang.rb" = load i64, i64* getelementptr inbounds ([8 x i64], [8 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_", i64 %"rubyId_", i64 %"rubyStr_test/testdata/compiler/intrinsics/bang.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 0, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 2) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - ret void -} - -; Function Attrs: sspreq -define void @Init_bang() local_unnamed_addr #5 { -entry: - %realpath = tail call i64 @sorbet_readRealpath() - tail call fastcc void @sorbet_globalConstructors(i64 %realpath) - %0 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !29 - %1 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %0, i64 0, i32 2 - %2 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %1, align 8, !tbaa !31 - %stackFrame.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - %3 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame.i, %struct.rb_iseq_struct** %3, align 8, !tbaa !35 - %4 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 4 - %5 = load i64*, i64** %4, align 8, !tbaa !37 - %6 = load i64, i64* %5, align 8, !tbaa !6 - %7 = and i64 %6, -33 - store i64 %7, i64* %5, align 8, !tbaa !6 - tail call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %0, %struct.rb_control_frame_struct* %2, %struct.rb_iseq_struct* %stackFrame.i) #10 - %8 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 0 - store i64* getelementptr inbounds ([23 x i64], [23 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %8, align 8, !dbg !38, !tbaa !29 - %9 = load i64, i64* @rb_cObject, align 8, !dbg !39 - %10 = tail call i64 @rb_define_class(i8* getelementptr inbounds ([166 x i8], [166 x i8]* @sorbet_moduleStringTable, i64 0, i64 59), i64 %9) #10, !dbg !39 - %11 = tail call %struct.rb_control_frame_struct* @sorbet_pushStaticInitFrame(i64 %10) #10, !dbg !39 - %stackFrame.i1.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Bad.13", align 8 - %12 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !29 - %13 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %12, i64 0, i32 2 - %14 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %13, align 8, !tbaa !31 - %15 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %14, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame.i1.i, %struct.rb_iseq_struct** %15, align 8, !tbaa !35 - %16 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %14, i64 0, i32 4 - %17 = load i64*, i64** %16, align 8, !tbaa !37 - %18 = load i64, i64* %17, align 8, !tbaa !6 - %19 = and i64 %18, -33 - store i64 %19, i64* %17, align 8, !tbaa !6 - tail call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %12, %struct.rb_control_frame_struct* %14, %struct.rb_iseq_struct* %stackFrame.i1.i) #10 - %20 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %11, i64 0, i32 0 - store i64* getelementptr inbounds ([23 x i64], [23 x i64]* @iseqEncodedArray, i64 0, i64 6), i64** %20, align 8, !dbg !40, !tbaa !29 - %21 = load i64, i64* @guard_epoch_Bad, align 8, !dbg !43 - %22 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !43, !tbaa !44 - %needTakeSlowPath = icmp ne i64 %21, %22, !dbg !43 - br i1 %needTakeSlowPath, label %23, label %24, !dbg !43, !prof !46 - -23: ; preds = %entry - tail call void @const_recompute_Bad(), !dbg !43 - br label %24, !dbg !43 - -24: ; preds = %entry, %23 - %25 = load i64, i64* @guarded_const_Bad, align 8, !dbg !43 - %26 = load i64, i64* @guard_epoch_Bad, align 8, !dbg !43 - %27 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !43, !tbaa !44 - %guardUpdated = icmp eq i64 %26, %27, !dbg !43 - tail call void @llvm.assume(i1 %guardUpdated), !dbg !43 - %stackFrame7.i2.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Bad#1!", align 8, !dbg !43 - %28 = tail call noalias nonnull i8* @ruby_xcalloc(i64 noundef 1, i64 noundef 64) #11, !dbg !43 - %29 = bitcast i8* %28 to i16*, !dbg !43 - %30 = load i16, i16* %29, align 8, !dbg !43 - %31 = and i16 %30, -384, !dbg !43 - store i16 %31, i16* %29, align 8, !dbg !43 - %32 = getelementptr inbounds i8, i8* %28, i64 4, !dbg !43 - tail call void @llvm.memset.p0i8.i64(i8* nonnull align 4 %32, i8 0, i64 28, i1 false) #10, !dbg !43 - tail call void @sorbet_vm_define_method(i64 %25, i8* getelementptr inbounds ([166 x i8], [166 x i8]* @sorbet_moduleStringTable, i64 0, i64 87), i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)* noundef @"func_Bad#1!", i8* nonnull %28, %struct.rb_iseq_struct* %stackFrame7.i2.i, i1 noundef zeroext false) #10, !dbg !43 - tail call void @sorbet_popFrame() #10, !dbg !39 - store i64* getelementptr inbounds ([23 x i64], [23 x i64]* @iseqEncodedArray, i64 0, i64 12), i64** %8, align 8, !dbg !39, !tbaa !29 - %33 = tail call i64 @rb_define_module(i8* getelementptr inbounds ([166 x i8], [166 x i8]* @sorbet_moduleStringTable, i64 0, i64 70)) #10, !dbg !47 - %34 = tail call %struct.rb_control_frame_struct* @sorbet_pushStaticInitFrame(i64 %33) #10, !dbg !47 - %stackFrame.i.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Main.13", align 8 - %35 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !29 - %36 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %35, i64 0, i32 2 - %37 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %36, align 8, !tbaa !31 - %38 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %37, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame.i.i, %struct.rb_iseq_struct** %38, align 8, !tbaa !35 - %39 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %37, i64 0, i32 4 - %40 = load i64*, i64** %39, align 8, !tbaa !37 - %41 = load i64, i64* %40, align 8, !tbaa !6 - %42 = and i64 %41, -33 - store i64 %42, i64* %40, align 8, !tbaa !6 - tail call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %35, %struct.rb_control_frame_struct* %37, %struct.rb_iseq_struct* %stackFrame.i.i) #10 - %43 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %34, i64 0, i32 0 - store i64* getelementptr inbounds ([23 x i64], [23 x i64]* @iseqEncodedArray, i64 0, i64 13), i64** %43, align 8, !dbg !48, !tbaa !29 - %44 = load i64, i64* @guard_epoch_Main, align 8, !dbg !51 - %45 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !51, !tbaa !44 - %needTakeSlowPath1 = icmp ne i64 %44, %45, !dbg !51 - br i1 %needTakeSlowPath1, label %46, label %47, !dbg !51, !prof !46 - -46: ; preds = %24 - tail call void @const_recompute_Main(), !dbg !51 - br label %47, !dbg !51 - -47: ; preds = %24, %46 - %48 = load i64, i64* @guarded_const_Main, align 8, !dbg !51 - %49 = load i64, i64* @guard_epoch_Main, align 8, !dbg !51 - %50 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !51, !tbaa !44 - %guardUpdated2 = icmp eq i64 %49, %50, !dbg !51 - tail call void @llvm.assume(i1 %guardUpdated2), !dbg !51 - %stackFrame7.i.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @stackFramePrecomputed_func_Main.4test, align 8, !dbg !51 - %51 = tail call noalias nonnull i8* @ruby_xcalloc(i64 noundef 1, i64 noundef 64) #11, !dbg !51 - %52 = bitcast i8* %51 to i16*, !dbg !51 - %53 = load i16, i16* %52, align 8, !dbg !51 - %54 = and i16 %53, -384, !dbg !51 - store i16 %54, i16* %52, align 8, !dbg !51 - %55 = getelementptr inbounds i8, i8* %51, i64 4, !dbg !51 - tail call void @llvm.memset.p0i8.i64(i8* nonnull align 4 %55, i8 0, i64 28, i1 false) #10, !dbg !51 - tail call void @sorbet_vm_define_method(i64 %48, i8* getelementptr inbounds ([166 x i8], [166 x i8]* @sorbet_moduleStringTable, i64 0, i64 75), i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)* noundef @func_Main.4test, i8* nonnull %51, %struct.rb_iseq_struct* %stackFrame7.i.i, i1 noundef zeroext true) #10, !dbg !51 - tail call void @sorbet_popFrame() #10, !dbg !47 - store i64* getelementptr inbounds ([23 x i64], [23 x i64]* @iseqEncodedArray, i64 0, i64 22), i64** %8, align 8, !dbg !47, !tbaa !29 - %56 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 1, !dbg !10 - %57 = load i64*, i64** %56, align 8, !dbg !10 - store i64 %48, i64* %57, align 8, !dbg !10, !tbaa !6 - %58 = getelementptr inbounds i64, i64* %57, i64 1, !dbg !10 - store i64* %58, i64** %56, align 8, !dbg !10 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_test, i64 0), !dbg !10 - ret void -} - -; Function Attrs: nounwind sspreq uwtable -define internal noundef i64 @"func_Bad#1!"(i32 %argc, i64* nocapture nofree readnone %argArray, i64 %selfRaw, %struct.rb_control_frame_struct* nonnull align 8 dereferenceable(8) %cfp, i8* nocapture nofree readnone %calling, i8* nocapture nofree readnone %callData) #6 !dbg !16 { -functionEntryInitializers: - %0 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 0 - store i64* getelementptr inbounds ([23 x i64], [23 x i64]* @iseqEncodedArray, i64 0, i64 6), i64** %0, align 8, !tbaa !29 - %tooManyArgs = icmp ugt i32 %argc, 0, !dbg !52 - br i1 %tooManyArgs, label %argCountFailBlock, label %fillRequiredArgs, !dbg !52, !prof !53 - -argCountFailBlock: ; preds = %functionEntryInitializers - tail call void @sorbet_raiseArity(i32 %argc, i32 noundef 0, i32 noundef 0) #12, !dbg !52 - unreachable, !dbg !52 - -fillRequiredArgs: ; preds = %functionEntryInitializers - store i64* getelementptr inbounds ([23 x i64], [23 x i64]* @iseqEncodedArray, i64 0, i64 7), i64** %0, align 8, !dbg !54, !tbaa !29 - %"rubyStr_bad bang overload" = load i64, i64* getelementptr inbounds ([8 x i64], [8 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 3), align 8, !dbg !55, !invariant.load !5 - %1 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 1, !dbg !15 - %2 = load i64*, i64** %1, align 8, !dbg !15 - store i64 %selfRaw, i64* %2, align 8, !dbg !15, !tbaa !6 - %3 = getelementptr inbounds i64, i64* %2, i64 1, !dbg !15 - store i64 %"rubyStr_bad bang overload", i64* %3, align 8, !dbg !15, !tbaa !6 - %4 = getelementptr inbounds i64, i64* %3, i64 1, !dbg !15 - store i64* %4, i64** %1, align 8, !dbg !15 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts, i64 0), !dbg !15 - store i64* getelementptr inbounds ([23 x i64], [23 x i64]* @iseqEncodedArray, i64 0, i64 8), i64** %0, align 8, !dbg !15, !tbaa !29 - ret i64 20 -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_Bad#1!"(i64 %realpath) unnamed_addr #4 { -entryInitializers: - %"rubyId_!" = load i64, i64* getelementptr inbounds ([9 x i64], [9 x i64]* @sorbet_moduleIDTable, i64 0, i64 2), align 8, !invariant.load !5 - %"rubyStr_!" = load i64, i64* getelementptr inbounds ([8 x i64], [8 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 2), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/intrinsics/bang.rb" = load i64, i64* getelementptr inbounds ([8 x i64], [8 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_!", i64 %"rubyId_!", i64 %"rubyStr_test/testdata/compiler/intrinsics/bang.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 1, i32 noundef 6, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 2) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Bad#1!", align 8 - ret void -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_Bad.13"(i64 %realpath) unnamed_addr #4 { -entryInitializers: - %"rubyId_" = load i64, i64* getelementptr inbounds ([9 x i64], [9 x i64]* @sorbet_moduleIDTable, i64 0, i64 4), align 8, !invariant.load !5 - %"rubyStr_" = load i64, i64* getelementptr inbounds ([8 x i64], [8 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 4), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/intrinsics/bang.rb" = load i64, i64* getelementptr inbounds ([8 x i64], [8 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_", i64 %"rubyId_", i64 %"rubyStr_test/testdata/compiler/intrinsics/bang.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 3, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 4) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Bad.13", align 8 - ret void -} - -; Function Attrs: nounwind sspreq uwtable -define internal i64 @func_Main.4test(i32 %argc, i64* nocapture nofree readnone %argArray, i64 %selfRaw, %struct.rb_control_frame_struct* nonnull align 8 dereferenceable(8) %cfp, i8* nocapture nofree readnone %calling, i8* nocapture nofree readnone %callData) #6 !dbg !18 { -functionEntryInitializers: - %0 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 0 - store i64* getelementptr inbounds ([23 x i64], [23 x i64]* @iseqEncodedArray, i64 0, i64 13), i64** %0, align 8, !tbaa !29 - %tooManyArgs = icmp ugt i32 %argc, 0, !dbg !56 - br i1 %tooManyArgs, label %argCountFailBlock, label %fillRequiredArgs, !dbg !56, !prof !53 - -argCountFailBlock: ; preds = %functionEntryInitializers - tail call void @sorbet_raiseArity(i32 %argc, i32 noundef 0, i32 noundef 0) #12, !dbg !56 - unreachable, !dbg !56 - -fillRequiredArgs: ; preds = %functionEntryInitializers - store i64* getelementptr inbounds ([23 x i64], [23 x i64]* @iseqEncodedArray, i64 0, i64 14), i64** %0, align 8, !dbg !57, !tbaa !29 - %1 = tail call i64 @sorbet_vm_bang(%struct.FunctionInlineCache* noundef @"ic_!", %struct.rb_control_frame_struct* nonnull align 8 dereferenceable(8) %cfp, i64 noundef 20), !dbg !17 - %2 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 1, !dbg !19 - %3 = load i64*, i64** %2, align 8, !dbg !19 - store i64 %selfRaw, i64* %3, align 8, !dbg !19, !tbaa !6 - %4 = getelementptr inbounds i64, i64* %3, i64 1, !dbg !19 - store i64 %1, i64* %4, align 8, !dbg !19, !tbaa !6 - %5 = getelementptr inbounds i64, i64* %4, i64 1, !dbg !19 - store i64* %5, i64** %2, align 8, !dbg !19 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts.1, i64 0), !dbg !19 - store i64* getelementptr inbounds ([23 x i64], [23 x i64]* @iseqEncodedArray, i64 0, i64 15), i64** %0, align 8, !dbg !19, !tbaa !29 - %6 = tail call i64 @sorbet_vm_bang(%struct.FunctionInlineCache* noundef @"ic_!.2", %struct.rb_control_frame_struct* nonnull %cfp, i64 noundef 0), !dbg !20 - %7 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 1, !dbg !21 - %8 = load i64*, i64** %7, align 8, !dbg !21 - store i64 %selfRaw, i64* %8, align 8, !dbg !21, !tbaa !6 - %9 = getelementptr inbounds i64, i64* %8, i64 1, !dbg !21 - store i64 %6, i64* %9, align 8, !dbg !21, !tbaa !6 - %10 = getelementptr inbounds i64, i64* %9, i64 1, !dbg !21 - store i64* %10, i64** %7, align 8, !dbg !21 - %send98 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts.3, i64 0), !dbg !21 - store i64* getelementptr inbounds ([23 x i64], [23 x i64]* @iseqEncodedArray, i64 0, i64 16), i64** %0, align 8, !dbg !21, !tbaa !29 - %11 = tail call i64 @sorbet_vm_bang(%struct.FunctionInlineCache* noundef @"ic_!.4", %struct.rb_control_frame_struct* nonnull %cfp, i64 noundef 8), !dbg !22 - %12 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 1, !dbg !23 - %13 = load i64*, i64** %12, align 8, !dbg !23 - store i64 %selfRaw, i64* %13, align 8, !dbg !23, !tbaa !6 - %14 = getelementptr inbounds i64, i64* %13, i64 1, !dbg !23 - store i64 %11, i64* %14, align 8, !dbg !23, !tbaa !6 - %15 = getelementptr inbounds i64, i64* %14, i64 1, !dbg !23 - store i64* %15, i64** %12, align 8, !dbg !23 - %send100 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts.5, i64 0), !dbg !23 - store i64* getelementptr inbounds ([23 x i64], [23 x i64]* @iseqEncodedArray, i64 0, i64 17), i64** %0, align 8, !dbg !23, !tbaa !29 - %rubyStr_hello = load i64, i64* getelementptr inbounds ([8 x i64], [8 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 6), align 8, !dbg !58, !invariant.load !5 - %16 = tail call i64 @sorbet_vm_bang(%struct.FunctionInlineCache* noundef @"ic_!.6", %struct.rb_control_frame_struct* nonnull %cfp, i64 %rubyStr_hello), !dbg !24 - %17 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 1, !dbg !25 - %18 = load i64*, i64** %17, align 8, !dbg !25 - store i64 %selfRaw, i64* %18, align 8, !dbg !25, !tbaa !6 - %19 = getelementptr inbounds i64, i64* %18, i64 1, !dbg !25 - store i64 %16, i64* %19, align 8, !dbg !25, !tbaa !6 - %20 = getelementptr inbounds i64, i64* %19, i64 1, !dbg !25 - store i64* %20, i64** %17, align 8, !dbg !25 - %send102 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts.7, i64 0), !dbg !25 - store i64* getelementptr inbounds ([23 x i64], [23 x i64]* @iseqEncodedArray, i64 0, i64 18), i64** %0, align 8, !dbg !25, !tbaa !29 - %21 = load i64, i64* @guard_epoch_Bad, align 8, !dbg !26 - %22 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !26, !tbaa !44 - %needTakeSlowPath = icmp ne i64 %21, %22, !dbg !26 - br i1 %needTakeSlowPath, label %23, label %24, !dbg !26, !prof !46 - -23: ; preds = %fillRequiredArgs - tail call void @const_recompute_Bad(), !dbg !26 - br label %24, !dbg !26 - -24: ; preds = %fillRequiredArgs, %23 - %25 = load i64, i64* @guarded_const_Bad, align 8, !dbg !26 - %26 = load i64, i64* @guard_epoch_Bad, align 8, !dbg !26 - %27 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !26, !tbaa !44 - %guardUpdated = icmp eq i64 %26, %27, !dbg !26 - tail call void @llvm.assume(i1 %guardUpdated), !dbg !26 - %28 = tail call i64 @sorbet_maybeAllocateObjectFastPath(i64 %25, %struct.FunctionInlineCache* noundef @ic_new), !dbg !26 - %29 = icmp eq i64 %28, 52, !dbg !26 - br i1 %29, label %slowNew, label %fastNew, !dbg !26 - -slowNew: ; preds = %24 - %30 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 1, !dbg !26 - %31 = load i64*, i64** %30, align 8, !dbg !26 - store i64 %25, i64* %31, align 8, !dbg !26, !tbaa !6 - %32 = getelementptr inbounds i64, i64* %31, i64 1, !dbg !26 - store i64* %32, i64** %30, align 8, !dbg !26 - %33 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* noundef @ic_new, i64 noundef 0), !dbg !26 - br label %afterNew, !dbg !26 - -fastNew: ; preds = %24 - %34 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 1, !dbg !26 - %35 = load i64*, i64** %34, align 8, !dbg !26 - store i64 %28, i64* %35, align 8, !dbg !26, !tbaa !6 - %36 = getelementptr inbounds i64, i64* %35, i64 1, !dbg !26 - store i64* %36, i64** %34, align 8, !dbg !26 - %37 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* noundef @ic_initialize, i64 noundef 0), !dbg !26 - br label %afterNew, !dbg !26 - -afterNew: ; preds = %fastNew, %slowNew - %initializedObject = phi i64 [ %33, %slowNew ], [ %28, %fastNew ], !dbg !26 - %38 = tail call i64 @sorbet_vm_bang(%struct.FunctionInlineCache* noundef @"ic_!.8", %struct.rb_control_frame_struct* nonnull %cfp, i64 %initializedObject), !dbg !27 - %39 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 1, !dbg !28 - %40 = load i64*, i64** %39, align 8, !dbg !28 - store i64 %selfRaw, i64* %40, align 8, !dbg !28, !tbaa !6 - %41 = getelementptr inbounds i64, i64* %40, i64 1, !dbg !28 - store i64 %38, i64* %41, align 8, !dbg !28, !tbaa !6 - %42 = getelementptr inbounds i64, i64* %41, i64 1, !dbg !28 - store i64* %42, i64** %39, align 8, !dbg !28 - %send104 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts.9, i64 0), !dbg !28 - ret i64 %send104 -} - -; Function Attrs: ssp -define internal fastcc void @Constr_stackFramePrecomputed_func_Main.4test(i64 %realpath) unnamed_addr #4 { -entryInitializers: - %rubyId_test = load i64, i64* getelementptr inbounds ([9 x i64], [9 x i64]* @sorbet_moduleIDTable, i64 0, i64 1), align 8, !invariant.load !5 - %rubyStr_test = load i64, i64* getelementptr inbounds ([8 x i64], [8 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 5), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/intrinsics/bang.rb" = load i64, i64* getelementptr inbounds ([8 x i64], [8 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %rubyStr_test, i64 %rubyId_test, i64 %"rubyStr_test/testdata/compiler/intrinsics/bang.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 1, i32 noundef 13, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 2) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @stackFramePrecomputed_func_Main.4test, align 8 - ret void -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_Main.13"(i64 %realpath) unnamed_addr #4 { -entryInitializers: - %"rubyId_" = load i64, i64* getelementptr inbounds ([9 x i64], [9 x i64]* @sorbet_moduleIDTable, i64 0, i64 8), align 8, !invariant.load !5 - %"rubyStr_" = load i64, i64* getelementptr inbounds ([8 x i64], [8 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 7), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/intrinsics/bang.rb" = load i64, i64* getelementptr inbounds ([8 x i64], [8 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_", i64 %"rubyId_", i64 %"rubyStr_test/testdata/compiler/intrinsics/bang.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 3, i32 noundef 12, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 4) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Main.13", align 8 - ret void -} - -; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly -declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #7 - -; Function Attrs: nofree nosync nounwind willreturn -declare void @llvm.assume(i1 noundef) #8 - -; Function Attrs: ssp -define linkonce void @const_recompute_Bad() local_unnamed_addr #4 { - %1 = tail call i64 @sorbet_getConstant(i8* getelementptr inbounds ([166 x i8], [166 x i8]* @sorbet_moduleStringTable, i64 0, i64 59), i64 3) - store i64 %1, i64* @guarded_const_Bad, align 8 - %2 = load i64, i64* @ruby_vm_global_constant_state, align 8, !tbaa !44 - store i64 %2, i64* @guard_epoch_Bad, align 8 - ret void -} - -; Function Attrs: ssp -define linkonce void @const_recompute_Main() local_unnamed_addr #4 { - %1 = tail call i64 @sorbet_getConstant(i8* getelementptr inbounds ([166 x i8], [166 x i8]* @sorbet_moduleStringTable, i64 0, i64 70), i64 4) - store i64 %1, i64* @guarded_const_Main, align 8 - %2 = load i64, i64* @ruby_vm_global_constant_state, align 8, !tbaa !44 - store i64 %2, i64* @guard_epoch_Main, align 8 - ret void -} - -attributes #0 = { noreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #1 = { "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #2 = { allocsize(0,1) "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #3 = { nounwind ssp uwtable "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #4 = { ssp } -attributes #5 = { sspreq } -attributes #6 = { nounwind sspreq uwtable } -attributes #7 = { argmemonly nofree nosync nounwind willreturn writeonly } -attributes #8 = { nofree nosync nounwind willreturn } -attributes #9 = { noreturn nounwind } -attributes #10 = { nounwind } -attributes #11 = { nounwind allocsize(0,1) } -attributes #12 = { noreturn } - -!llvm.module.flags = !{!0, !1, !2} -!llvm.dbg.cu = !{!3} - -!0 = !{i32 2, !"Debug Info Version", i32 3} -!1 = !{i32 4, !"cf-protection-return", i32 1} -!2 = !{i32 4, !"cf-protection-branch", i32 1} -!3 = distinct !DICompileUnit(language: DW_LANG_C, file: !4, producer: "Sorbet LLVM", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !5) -!4 = !DIFile(filename: "test/testdata/compiler/intrinsics/bang.rb", directory: ".") -!5 = !{} -!6 = !{!7, !7, i64 0} -!7 = !{!"long", !8, i64 0} -!8 = !{!"omnipotent char", !9, i64 0} -!9 = !{!"Simple C/C++ TBAA"} -!10 = !DILocation(line: 22, column: 1, scope: !11) -!11 = distinct !DISubprogram(name: ".", linkageName: "func_.13", scope: null, file: !4, line: 5, type: !12, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!12 = !DISubroutineType(types: !13) -!13 = !{!14} -!14 = !DIBasicType(name: "VALUE", size: 64, encoding: DW_ATE_signed) -!15 = !DILocation(line: 7, column: 5, scope: !16) -!16 = distinct !DISubprogram(name: "Bad#!", linkageName: "func_Bad#1!", scope: null, file: !4, line: 6, type: !12, scopeLine: 6, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!17 = !DILocation(line: 14, column: 10, scope: !18) -!18 = distinct !DISubprogram(name: "Main.test", linkageName: "func_Main.4test", scope: null, file: !4, line: 13, type: !12, scopeLine: 13, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!19 = !DILocation(line: 14, column: 5, scope: !18) -!20 = !DILocation(line: 15, column: 10, scope: !18) -!21 = !DILocation(line: 15, column: 5, scope: !18) -!22 = !DILocation(line: 16, column: 10, scope: !18) -!23 = !DILocation(line: 16, column: 5, scope: !18) -!24 = !DILocation(line: 17, column: 10, scope: !18) -!25 = !DILocation(line: 17, column: 5, scope: !18) -!26 = !DILocation(line: 18, column: 11, scope: !18) -!27 = !DILocation(line: 18, column: 10, scope: !18) -!28 = !DILocation(line: 18, column: 5, scope: !18) -!29 = !{!30, !30, i64 0} -!30 = !{!"any pointer", !8, i64 0} -!31 = !{!32, !30, i64 16} -!32 = !{!"rb_execution_context_struct", !30, i64 0, !7, i64 8, !30, i64 16, !30, i64 24, !30, i64 32, !33, i64 40, !33, i64 44, !30, i64 48, !30, i64 56, !30, i64 64, !7, i64 72, !7, i64 80, !30, i64 88, !7, i64 96, !30, i64 104, !30, i64 112, !7, i64 120, !7, i64 128, !8, i64 136, !8, i64 137, !7, i64 144, !34, i64 152} -!33 = !{!"int", !8, i64 0} -!34 = !{!"", !30, i64 0, !30, i64 8, !7, i64 16, !8, i64 24} -!35 = !{!36, !30, i64 16} -!36 = !{!"rb_control_frame_struct", !30, i64 0, !30, i64 8, !30, i64 16, !7, i64 24, !30, i64 32, !30, i64 40, !30, i64 48} -!37 = !{!36, !30, i64 32} -!38 = !DILocation(line: 0, scope: !11) -!39 = !DILocation(line: 5, column: 1, scope: !11) -!40 = !DILocation(line: 0, scope: !41, inlinedAt: !42) -!41 = distinct !DISubprogram(name: "Bad.", linkageName: "func_Bad.13L62", scope: null, file: !4, line: 5, type: !12, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!42 = distinct !DILocation(line: 5, column: 1, scope: !11) -!43 = !DILocation(line: 6, column: 3, scope: !41, inlinedAt: !42) -!44 = !{!45, !45, i64 0} -!45 = !{!"long long", !8, i64 0} -!46 = !{!"branch_weights", i32 1, i32 10000} -!47 = !DILocation(line: 12, column: 1, scope: !11) -!48 = !DILocation(line: 0, scope: !49, inlinedAt: !50) -!49 = distinct !DISubprogram(name: "Main.", linkageName: "func_Main.13L129", scope: null, file: !4, line: 12, type: !12, scopeLine: 12, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!50 = distinct !DILocation(line: 12, column: 1, scope: !11) -!51 = !DILocation(line: 13, column: 3, scope: !49, inlinedAt: !50) -!52 = !DILocation(line: 6, column: 3, scope: !16) -!53 = !{!"branch_weights", i32 1, i32 2000} -!54 = !DILocation(line: 0, scope: !16) -!55 = !DILocation(line: 7, column: 10, scope: !16) -!56 = !DILocation(line: 13, column: 3, scope: !18) -!57 = !DILocation(line: 0, scope: !18) -!58 = !DILocation(line: 17, column: 11, scope: !18) diff --git a/test/testdata/compiler/intrinsics/eqeq.rb b/test/testdata/compiler/intrinsics/eqeq.rb index c85ce49ec6..c31f72f3fd 100644 --- a/test/testdata/compiler/intrinsics/eqeq.rb +++ b/test/testdata/compiler/intrinsics/eqeq.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL extend T::Sig @@ -16,23 +15,11 @@ def do_eqeq(x, y) x == y end -# INITIAL-LABEL: @"func_Object#7do_eqeq" -# INITIAL-NOT: call i64 @sorbet_vm_eqeq -# INITIAL: call i64{{.*}}@sorbet_i_send -# INITIAL-NOT: call i64 @sorbet_vm_eqeq -# INITIAL: call i64 @sorbet_rb_int_equal -# INITIAL-NOT: call i64 @sorbet_vm_eqeq -# INITIAL{LITERAL}: } def do_eqeq_untyped(x, y) x == y end -# INITIAL-LABEL: @"func_Object#15do_eqeq_untyped" -# INITIAL-NOT: call i64 @sorbet_rb_int_equal -# INITIAL: call i64 @sorbet_vm_eqeq -# INITIAL-NOT: call i64 @sorbet_rb_int_equal -# INITIAL{LITERAL}: } p do_eqeq(4, 5) p do_eqeq(8, 9.0) diff --git a/test/testdata/compiler/intrinsics/geq.rb b/test/testdata/compiler/intrinsics/geq.rb index 42dba4e26e..d4768e20d4 100644 --- a/test/testdata/compiler/intrinsics/geq.rb +++ b/test/testdata/compiler/intrinsics/geq.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL extend T::Sig @@ -16,23 +15,11 @@ def do_geq(x, y) x >= y end -# INITIAL-LABEL: @"func_Object#6do_geq" -# INITIAL-NOT: call i64 @sorbet_vm_geq -# INITIAL: call i64{{.*}}@sorbet_i_send -# INITIAL-NOT: call i64 @sorbet_vm_geq -# INITIAL: call i64 @sorbet_rb_int_ge -# INITIAL-NOT: call i64 @sorbet_vm_geq -# INITIAL{LITERAL}: } def do_geq_untyped(x, y) x >= y end -# INITIAL-LABEL: @"func_Object#14do_geq_untyped" -# INITIAL-NOT: call i64 @sorbet_rb_int_ge -# INITIAL: call i64 @sorbet_vm_geq -# INITIAL-NOT: call i64 @sorbet_rb_int_ge -# INITIAL{LITERAL}: } p do_geq(4, 5) p do_geq(8, 9.0) diff --git a/test/testdata/compiler/intrinsics/gt.rb b/test/testdata/compiler/intrinsics/gt.rb index 8c04c1ade3..1be545d0c7 100644 --- a/test/testdata/compiler/intrinsics/gt.rb +++ b/test/testdata/compiler/intrinsics/gt.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL extend T::Sig @@ -16,23 +15,11 @@ def do_gt(x, y) x > y end -# INITIAL-LABEL: @"func_Object#5do_gt" -# INITIAL-NOT: call i64 @sorbet_vm_gt -# INITIAL: call i64{{.*}}@sorbet_i_send -# INITIAL-NOT: call i64 @sorbet_vm_gt -# INITIAL: call i64 @sorbet_rb_int_gt -# INITIAL-NOT: call i64 @sorbet_vm_gt -# INITIAL{LITERAL}: } def do_gt_untyped(x, y) x > y end -# INITIAL-LABEL: @"func_Object#13do_gt_untyped" -# INITIAL-NOT: call i64 @sorbet_rb_int_gt -# INITIAL: call i64 @sorbet_vm_gt -# INITIAL-NOT: call i64 @sorbet_rb_int_gt -# INITIAL{LITERAL}: } p do_gt(4, 5) p do_gt(8, 9.0) diff --git a/test/testdata/compiler/intrinsics/hash_any_blk_1arg.rb b/test/testdata/compiler/intrinsics/hash_any_blk_1arg.rb index ca993c59bb..b6591525ef 100644 --- a/test/testdata/compiler/intrinsics/hash_any_blk_1arg.rb +++ b/test/testdata/compiler/intrinsics/hash_any_blk_1arg.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL h = {a: 1, b: 2, c: :sym} @@ -17,5 +16,3 @@ end p result - -# INITIAL-COUNT-2: call i64 @sorbet_inlineIntrinsicEnv_apply(i64 %0, i64 (i64, i64, i32, i64*, i64 (i64, i64, i32, i64*, i64)*, %struct.rb_captured_block*, i64, i32)* @sorbet_rb_hash_any_withBlock diff --git a/test/testdata/compiler/intrinsics/hash_any_blk_2arg.rb b/test/testdata/compiler/intrinsics/hash_any_blk_2arg.rb index c83ec551b3..c5da4de571 100644 --- a/test/testdata/compiler/intrinsics/hash_any_blk_2arg.rb +++ b/test/testdata/compiler/intrinsics/hash_any_blk_2arg.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL h = {a: 1, b: 2, c: :sym} @@ -24,5 +23,3 @@ end p result - -# INITIAL-COUNT-3: call i64 @sorbet_inlineIntrinsicEnv_apply(i64 %0, i64 (i64, i64, i32, i64*, i64 (i64, i64, i32, i64*, i64)*, %struct.rb_captured_block*, i64, i32)* @sorbet_rb_hash_any_withBlock diff --git a/test/testdata/compiler/intrinsics/hash_aref.rb b/test/testdata/compiler/intrinsics/hash_aref.rb index e434010301..4328e4f374 100644 --- a/test/testdata/compiler/intrinsics/hash_aref.rb +++ b/test/testdata/compiler/intrinsics/hash_aref.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL extend T::Sig @@ -19,13 +18,6 @@ def do_aref(x) # Try and ensure that we inline for the hash case and don't call the vm-like # fastpath, since we have type information. -# INITIAL-LABEL: @"func_Object#7do_aref" -# INITIAL-NOT: call i64 @sorbet_vm_aref -# INITIAL: call i64{{.*}}@sorbet_i_send -# INITIAL-NOT: call i64 @sorbet_vm_aref -# INITIAL: call i64 @sorbet_rb_hash_square_br -# INITIAL-NOT: call i64 @sorbet_vm_aref -# INITIAL{LITERAL}: } def do_aref_notype(x) x[:foo] @@ -33,11 +25,6 @@ def do_aref_notype(x) # Make sure we call our vm-like fastpath for untyped args. -# INITIAL-LABEL: @"func_Object#14do_aref_notype" -# INITIAL-NOT: call i64 @sorbet_rb_hash_square_br -# INITIAL: call i64 @sorbet_vm_aref -# INITIAL-NOT: call i64 @sorbet_rb_hash_square_br -# INITIAL{LITERAL}: } hash = {foo: 627} p hash diff --git a/test/testdata/compiler/intrinsics/hash_delete.rb b/test/testdata/compiler/intrinsics/hash_delete.rb index ed38f702df..97cd79d966 100644 --- a/test/testdata/compiler/intrinsics/hash_delete.rb +++ b/test/testdata/compiler/intrinsics/hash_delete.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL extend T::Sig @@ -10,9 +9,6 @@ def one_arg(hash, key) hash.delete(key) end -# INITIAL-LABEL: define internal i64 @"func_Object#7one_arg" -# INITIAL: call i64 @sorbet_rb_hash_delete_m -# INITIAL{LITERAL}: } sig {params(hash: T::Hash[T.untyped, T.untyped], key: T.untyped, blk: T.untyped).returns(T.untyped)} def block_arg(hash, key, &blk) @@ -21,10 +17,6 @@ def block_arg(hash, key, &blk) end end -# INITIAL-LABEL: define internal i64 @"func_Object#9block_arg" -# INITIAL-NOT: call i64 @sorbet_rb_hash_delete_m -# INITIAL: call i64 @sorbet_callIntrinsicInlineBlock_noBreak(i64 (i64)* @forward_sorbet_rb_hash_delete_m_withBlock -# INITIAL{LITERAL}: } h = {key: 5, otherkey: 99} p one_arg(h, :key) diff --git a/test/testdata/compiler/intrinsics/hash_dig.rb b/test/testdata/compiler/intrinsics/hash_dig.rb index 1b2b93f764..19f98a0dfb 100644 --- a/test/testdata/compiler/intrinsics/hash_dig.rb +++ b/test/testdata/compiler/intrinsics/hash_dig.rb @@ -1,16 +1,11 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL module M extend T::Sig - # INITIAL-LABEL: @func_M.7dig_x_y - # INITIAL: call i64 @sorbet_int_rb_hash_dig - # INITIAL-NOT: sorbet_i_send - # INITIAL{LITERAL}: } sig {params(x: T::Hash[Symbol, T.untyped]).returns(T.untyped)} def self.dig_x_y(x) x.dig(:x, :y) diff --git a/test/testdata/compiler/intrinsics/hash_each_blk_2arg.rb b/test/testdata/compiler/intrinsics/hash_each_blk_2arg.rb index 0fbbc16ca7..a1425f56b6 100644 --- a/test/testdata/compiler/intrinsics/hash_each_blk_2arg.rb +++ b/test/testdata/compiler/intrinsics/hash_each_blk_2arg.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL # "Normal" block result = {a: 1, b: 2, c: :sym}.each do |k, v| @@ -20,6 +19,3 @@ end p result - -# INITIAL-COUNT-2: call i64 @sorbet_callIntrinsicInlineBlock -# INITIAL-NOT: call i64 @sorbet_callIntrinsicInlineBlock diff --git a/test/testdata/compiler/intrinsics/hash_each_with_object/with_block.rb b/test/testdata/compiler/intrinsics/hash_each_with_object/with_block.rb index a2f2b8db90..cec0c02d4a 100644 --- a/test/testdata/compiler/intrinsics/hash_each_with_object/with_block.rb +++ b/test/testdata/compiler/intrinsics/hash_each_with_object/with_block.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL h = T.let({a: 1, b: 1, c: 2, d: 3, e: 5, f: 8}, T::Hash[Symbol, Integer]) @@ -35,6 +34,3 @@ p result p obj - -# INITIAL-COUNT-3: call i64 @sorbet_callIntrinsicInlineBlock -# INITIAL-NOT: call i64 @sorbet_callIntrinsicInlineBlock diff --git a/test/testdata/compiler/intrinsics/hash_empty_p.rb b/test/testdata/compiler/intrinsics/hash_empty_p.rb index cc9cf51d7a..31cfa77fef 100644 --- a/test/testdata/compiler/intrinsics/hash_empty_p.rb +++ b/test/testdata/compiler/intrinsics/hash_empty_p.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL def test_empty_p h = {} @@ -11,7 +10,3 @@ def test_empty_p p (h.empty?) end -# INITIAL-LABEL: define internal i64 @"func_Object#12test_empty_p" -# INITIAL: call i64 @sorbet_rb_hash_empty_p( -# INITIAL: call i64 @sorbet_rb_hash_empty_p( -# INITIAL{LITERAL}: } diff --git a/test/testdata/compiler/intrinsics/hash_fetch.rb b/test/testdata/compiler/intrinsics/hash_fetch.rb index 5fc5ae7165..e53ccc2d1a 100644 --- a/test/testdata/compiler/intrinsics/hash_fetch.rb +++ b/test/testdata/compiler/intrinsics/hash_fetch.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL extend T::Sig @@ -10,18 +9,12 @@ def one_arg(hash, key) hash.fetch(key) end -# INITIAL-LABEL: define internal i64 @"func_Object#7one_arg" -# INITIAL: call i64 @sorbet_rb_hash_fetch_m -# INITIAL{LITERAL}: } sig {params(hash: T::Hash[T.untyped, T.untyped], key: T.untyped, default: T.untyped).returns(T.untyped)} def two_arg(hash, key, default) hash.fetch(key, default) end -# INITIAL-LABEL: define internal i64 @"func_Object#7two_arg" -# INITIAL: call i64 @sorbet_rb_hash_fetch_m -# INITIAL{LITERAL}: } sig {params(hash: T::Hash[T.untyped, T.untyped], key: T.untyped, blk: T.untyped).returns(T.untyped)} def block_arg(hash, key, &blk) @@ -30,10 +23,6 @@ def block_arg(hash, key, &blk) end end -# INITIAL-LABEL: define internal i64 @"func_Object#9block_arg" -# INITIAL: call i64 @sorbet_callIntrinsicInlineBlock_noBreak({{.*sorbet_rb_hash_fetch_m_withBlock}} -# INITIAL-NOT: call i64 @sorbet_rb_hash_fetch_m -# INITIAL{LITERAL}: } p one_arg({key: 5}, :key) p two_arg({key: 7}, :key, :default) diff --git a/test/testdata/compiler/intrinsics/hash_keys_values.rb b/test/testdata/compiler/intrinsics/hash_keys_values.rb index 006efd8966..b022b90e37 100644 --- a/test/testdata/compiler/intrinsics/hash_keys_values.rb +++ b/test/testdata/compiler/intrinsics/hash_keys_values.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL extend T::Sig @@ -10,18 +9,12 @@ def keys(x) x.keys end -# INITIAL-LABEL: define internal i64 @"func_Object#4keys" -# INITIAL: call i64 @sorbet_rb_hash_keys -# INITIAL{LITERAL}: } sig {params(x: T::Hash[T.untyped, T.untyped]).returns(T::Array[T.untyped])} def values(x) x.values end -# INITIAL-LABEL: define internal i64 @"func_Object#6values" -# INITIAL: call i64 @sorbet_rb_hash_values -# INITIAL{LITERAL}: } p keys({a: 1, b: 2}) p values({a: 1, b: 2}) diff --git a/test/testdata/compiler/intrinsics/hash_merge.rb b/test/testdata/compiler/intrinsics/hash_merge.rb index 1722f14b2e..2844b50722 100644 --- a/test/testdata/compiler/intrinsics/hash_merge.rb +++ b/test/testdata/compiler/intrinsics/hash_merge.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL def no_block h1 = {x: "foo", q: "bar", z: 33} @@ -11,9 +10,6 @@ def no_block p h1 end -# INITIAL-LABEL: define internal i64 @"func_Object#8no_block"( -# INITIAL: call i64 @sorbet_rb_hash_merge( -# INITIAL{LITERAL}: } def with_block h1 = {x: "foo", q: "bar", z: 33} @@ -25,9 +21,6 @@ def with_block p h1 end -# INITIAL-LABEL: define internal i64 @"func_Object#10with_block"( -# INITIAL: call i64 @sorbet_callIntrinsicInlineBlock_noBreak(i64 (i64)* @forward_sorbet_rb_hash_merge_withBlock -# INITIAL{LITERAL}: } with_block @@ -48,8 +41,5 @@ def with_block_raise p h1 end -# INITIAL-LABEL: define internal i64 @"func_Object#16with_block_raise"( -# INITIAL: call i64 @sorbet_callIntrinsicInlineBlock_noBreak(i64 (i64)* @forward_sorbet_rb_hash_merge_withBlock -# INITIAL{LITERAL}: } with_block_raise \ No newline at end of file diff --git a/test/testdata/compiler/intrinsics/hash_merge_bang.rb b/test/testdata/compiler/intrinsics/hash_merge_bang.rb index e9868dda7e..d1c3430816 100644 --- a/test/testdata/compiler/intrinsics/hash_merge_bang.rb +++ b/test/testdata/compiler/intrinsics/hash_merge_bang.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL def no_block h1 = {x: "foo", q: "bar", z: 33} @@ -11,9 +10,6 @@ def no_block p h1 end -# INITIAL-LABEL: define internal i64 @"func_Object#8no_block"( -# INITIAL: call i64 @sorbet_rb_hash_update( -# INITIAL{LITERAL}: } no_block @@ -27,9 +23,6 @@ def with_block p h1 end -# INITIAL-LABEL: define internal i64 @"func_Object#10with_block"( -# INITIAL: call i64 @sorbet_callIntrinsicInlineBlock_noBreak(i64 (i64)* @forward_sorbet_rb_hash_update_withBlock -# INITIAL{LITERAL}: } with_block @@ -50,8 +43,5 @@ def with_block_raise p h1 end -# INITIAL-LABEL: define internal i64 @"func_Object#16with_block_raise"( -# INITIAL: call i64 @sorbet_callIntrinsicInlineBlock_noBreak(i64 (i64)* @forward_sorbet_rb_hash_update_withBlock -# INITIAL{LITERAL}: } with_block_raise \ No newline at end of file diff --git a/test/testdata/compiler/intrinsics/hash_select.rb b/test/testdata/compiler/intrinsics/hash_select.rb index c41174b9b1..6e2edc57c6 100644 --- a/test/testdata/compiler/intrinsics/hash_select.rb +++ b/test/testdata/compiler/intrinsics/hash_select.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL def no_block h1 = {q: 99, z: 48, t: 97, p: 100} @@ -14,9 +13,6 @@ def no_block no_block -# INITIAL-LABEL: define internal i64 @"func_Object#8no_block"( -# INITIAL: call i64 @sorbet_rb_hash_select( -# INITIAL{LITERAL}: } def with_block h1 = {q: 99, z: 48, t: 97, p: 100} @@ -28,6 +24,3 @@ def with_block with_block -# INITIAL-LABEL: define internal i64 @"func_Object#10with_block"( -# INITIAL: call i64 @sorbet_callIntrinsicInlineBlock_noBreak(i64 (i64)* @forward_sorbet_rb_hash_select_withBlock -# INITIAL{LITERAL}: } diff --git a/test/testdata/compiler/intrinsics/hash_transform_values.rb b/test/testdata/compiler/intrinsics/hash_transform_values.rb index 17d6f3c15d..09fc747b2f 100644 --- a/test/testdata/compiler/intrinsics/hash_transform_values.rb +++ b/test/testdata/compiler/intrinsics/hash_transform_values.rb @@ -1,14 +1,9 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL module M - # INITIAL-LABEL: define internal i64 @func_M.10test_block( - # INITIAL: call i64 @sorbet_callIntrinsicInlineBlock_noBreak({{.*}}@forward_sorbet_rb_hash_transform_values_withBlock - # INITIAL-NOT: @sorbet_i_send - # INITIAL{LITERAL}: } def self.test_block() {a: 1, b: 2, c: 3}.transform_values do |a| p " #{a}" @@ -16,10 +11,6 @@ def self.test_block() end end - # INITIAL-LABEL: define internal i64 @func_M.9test_enum( - # INITIAL: call i64 @sorbet_rb_hash_transform_values( - # INITIAL-NOT: @sorbet_i_send - # INITIAL{LITERAL}: } def self.test_enum() {a: 1, b: 2, c: 3}.transform_values end diff --git a/test/testdata/compiler/intrinsics/identities.rb b/test/testdata/compiler/intrinsics/identities.rb index 9878a6eb4e..32a6487d98 100644 --- a/test/testdata/compiler/intrinsics/identities.rb +++ b/test/testdata/compiler/intrinsics/identities.rb @@ -1,86 +1,50 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: OPT module M extend T::Sig - # OPT-LABEL: define internal i64 @func_M.12array_to_ary - # OPT-NOT: sorbet_callFuncWithCache - # OPT{LITERAL}: } sig {params(x: T::Array[Integer]).returns(T::Array[Integer])} def self.array_to_ary(x) x.to_ary end - # OPT-LABEL: define internal i64 @func_M.12hash_to_hash - # OPT-NOT: sorbet_callFuncWithCache - # OPT{LITERAL}: } sig {params(x: T::Hash[T.untyped, T.untyped]).returns(T::Hash[T.untyped, T.untyped])} def self.hash_to_hash(x) x.to_hash end - # OPT-LABEL: define internal i64 @func_M.9hash_to_h - # OPT: sorbet_rb_hash_to_h - # OPT: sorbet_callFuncWithCache - # OPT-NOT: sorbet_callFuncWithCache - # OPT{LITERAL}: } sig {params(x: T::Hash[T.untyped, T.untyped]).returns(T::Hash[T.untyped, T.untyped])} def self.hash_to_h(x) x.to_h end - # OPT-LABEL: define internal i64 @func_M.19hash_to_h_withBlock - # OPT: sorbet_callFuncWithCache - # OPT-NOT: sorbet_callFuncWithCache - # OPT-CHECK: call void @rb_hash_foreach(i64 %rawArg_x,a.*func_M.19hash_to_h_withBlock\$block_1 - # OPT{LITERAL}: } sig {params(x: T::Hash[T.untyped, T.untyped]).returns(T::Hash[T.untyped, T.untyped])} def self.hash_to_h_withBlock(x) x.to_h {|k,v| [v,k]} end - # OPT-LABEL: define internal i64 @func_M.12integer_to_i - # OPT-NOT: sorbet_callFuncWithCache - # OPT{LITERAL}: } sig {params(x: Integer).returns(Integer)} def self.integer_to_i(x) x.to_i end - # OPT-LABEL: define internal i64 @func_M.14integer_to_int - # OPT-NOT: sorbet_callFuncWithCache - # OPT{LITERAL}: } sig {params(x: Integer).returns(Integer)} def self.integer_to_int(x) x.to_int end - # OPT-LABEL: define internal i64 @func_M.13symbol_to_sym - # OPT-NOT: sorbet_callFuncWithCache - # OPT{LITERAL}: } sig {params(x: Symbol).returns(Symbol)} def self.symbol_to_sym(x) x.to_sym end - # OPT-LABEL: define internal i64 @func_M.11string_to_s - # OPT: sorbet_rb_str_to_s - # OPT: sorbet_callFuncWithCache - # OPT-NOT: sorbet_callFuncWithCache - # OPT{LITERAL}: } sig {params(x: String).returns(String)} def self.string_to_s(x) x.to_s end - # OPT-LABEL: define internal i64 @func_M.10array_to_a - # OPT: sorbet_rb_ary_to_a - # OPT: sorbet_callFuncWithCache - # OPT-NOT: sorbet_callFuncWithCache - # OPT{LITERAL}: } sig {params(x: T::Array[T.untyped]).returns(T::Array[T.untyped])} def self.array_to_a(x) x.to_a diff --git a/test/testdata/compiler/intrinsics/int_minus.rb b/test/testdata/compiler/intrinsics/int_minus.rb index e8bc97b292..8d09176bf6 100644 --- a/test/testdata/compiler/intrinsics/int_minus.rb +++ b/test/testdata/compiler/intrinsics/int_minus.rb @@ -15,4 +15,4 @@ puts(1 - T.unsafe(-Float::INFINITY)) # T_BIGNUM, * -puts(T.cast(100*100, Integer) - 1) +puts(T.cast(100**100, Integer) - 1) diff --git a/test/testdata/compiler/intrinsics/int_plus.rb b/test/testdata/compiler/intrinsics/int_plus.rb index e4f3de5048..e053ead166 100644 --- a/test/testdata/compiler/intrinsics/int_plus.rb +++ b/test/testdata/compiler/intrinsics/int_plus.rb @@ -15,4 +15,4 @@ puts(1 + T.unsafe(-Float::INFINITY)) # T_BIGNUM, * -puts(T.cast(100*100, Integer) + 1) +puts(T.cast(100**100, Integer) + 1) diff --git a/test/testdata/compiler/intrinsics/int_to_f.rb b/test/testdata/compiler/intrinsics/int_to_f.rb index 3954d1e7ec..fb7e51de97 100644 --- a/test/testdata/compiler/intrinsics/int_to_f.rb +++ b/test/testdata/compiler/intrinsics/int_to_f.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: OPT def test_int_to_f puts(-128.to_f) @@ -14,13 +13,5 @@ def test_int_to_f puts(9_007_199_254_740_992.to_f.to_i) end -# OPT-LABEL: define internal i64 @"func_Object#13test_int_to_f" -# OPT: tail call i64 @sorbet_int_to_f( -# OPT: tail call i64 @sorbet_int_to_f( -# OPT: tail call i64 @sorbet_int_to_f( -# OPT: tail call i64 @sorbet_int_to_f( -# OPT: tail call i64 @sorbet_int_to_f( -# OPT: tail call i64 @sorbet_int_to_f( -# OPT{LITERAL}: } test_int_to_f diff --git a/test/testdata/compiler/intrinsics/is_a.rb b/test/testdata/compiler/intrinsics/is_a.rb index 33598905f9..41980755b0 100644 --- a/test/testdata/compiler/intrinsics/is_a.rb +++ b/test/testdata/compiler/intrinsics/is_a.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL class Normal def f(x, y) @@ -13,13 +12,7 @@ def g(x, y) end end -# INITIAL-LABEL: define internal i64 @"func_Normal#1f" -# INITIAL: call i64 @sorbet_vm_isa_p -# INITIAL{LITERAL}: } -# INITIAL-LABEL: define internal i64 @"func_Normal#1g" -# INITIAL: call i64 @sorbet_vm_isa_p -# INITIAL{LITERAL}: } class Override def is_a?(x) diff --git a/test/testdata/compiler/intrinsics/kernel_freeze.rb b/test/testdata/compiler/intrinsics/kernel_freeze.rb index 1dd5789a5f..e0e89b846b 100644 --- a/test/testdata/compiler/intrinsics/kernel_freeze.rb +++ b/test/testdata/compiler/intrinsics/kernel_freeze.rb @@ -1,15 +1,10 @@ # frozen_string_literal: true # compiled: true # typed: true -# run_filecheck: OPT module Main extend T::Sig - # OPT-LABEL: @func_Main.7example - # OPT: sorbet_vm_freeze - # OPT-NOT: sorbet_callFuncWithCache - # OPT{LITERAL}: } sig {params(x: T::Array[Integer]).void} def self.example(x) x.freeze diff --git a/test/testdata/compiler/intrinsics/leq.rb b/test/testdata/compiler/intrinsics/leq.rb index afa33551f7..8d64a2ae5c 100644 --- a/test/testdata/compiler/intrinsics/leq.rb +++ b/test/testdata/compiler/intrinsics/leq.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL extend T::Sig @@ -16,23 +15,11 @@ def do_leq(x, y) x <= y end -# INITIAL-LABEL: @"func_Object#6do_leq" -# INITIAL-NOT: call i64 @sorbet_vm_leq -# INITIAL: call i64{{.*}}@sorbet_i_send -# INITIAL-NOT: call i64 @sorbet_vm_leq -# INITIAL: call i64 @sorbet_rb_int_le -# INITIAL-NOT: call i64 @sorbet_vm_leq -# INITIAL{LITERAL}: } def do_leq_untyped(x, y) x <= y end -# INITIAL-LABEL: @"func_Object#14do_leq_untyped" -# INITIAL-NOT: call i64 @sorbet_rb_int_le -# INITIAL: call i64 @sorbet_vm_leq -# INITIAL-NOT: call i64 @sorbet_rb_int_le -# INITIAL{LITERAL}: } p do_leq(4, 5) p do_leq(8, 9.0) diff --git a/test/testdata/compiler/intrinsics/lt.rb b/test/testdata/compiler/intrinsics/lt.rb index 42b60d260e..c8de60ef50 100644 --- a/test/testdata/compiler/intrinsics/lt.rb +++ b/test/testdata/compiler/intrinsics/lt.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL extend T::Sig @@ -16,23 +15,11 @@ def do_lt(x, y) x < y end -# INITIAL-LABEL: @"func_Object#5do_lt" -# INITIAL-NOT: call i64 @sorbet_vm_lt -# INITIAL: call i64{{.*}}@sorbet_i_send -# INITIAL-NOT: call i64 @sorbet_vm_lt -# INITIAL: call i64 @sorbet_rb_int_lt -# INITIAL-NOT: call i64 @sorbet_vm_lt -# INITIAL{LITERAL}: } def do_lt_untyped(x, y) x < y end -# INITIAL-LABEL: @"func_Object#13do_lt_untyped" -# INITIAL-NOT: call i64 @sorbet_rb_int_lt -# INITIAL: call i64 @sorbet_vm_lt -# INITIAL-NOT: call i64 @sorbet_rb_int_lt -# INITIAL{LITERAL}: } p do_lt(4, 5) p do_lt(8, 9.0) diff --git a/test/testdata/compiler/intrinsics/minus.rb b/test/testdata/compiler/intrinsics/minus.rb index 5cc7d7d911..24066e4b5b 100644 --- a/test/testdata/compiler/intrinsics/minus.rb +++ b/test/testdata/compiler/intrinsics/minus.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL extend T::Sig @@ -16,23 +15,11 @@ def do_minus(x, y) x - y end -# INITIAL-LABEL: @"func_Object#8do_minus" -# INITIAL-NOT: call i64 @sorbet_vm_minus -# INITIAL: call i64{{.*}}@sorbet_i_send -# INITIAL-NOT: call i64 @sorbet_vm_minus -# INITIAL: call i64 @sorbet_rb_int_minus -# INITIAL-NOT: call i64 @sorbet_vm_minus -# INITIAL{LITERAL}: } def do_minus_untyped(x, y) x - y end -# INITIAL-LABEL: @"func_Object#16do_minus_untyped" -# INITIAL-NOT: call i64 @sorbet_rb_int_minus -# INITIAL: call i64 @sorbet_vm_minus -# INITIAL-NOT: call i64 @sorbet_rb_int_minus -# INITIAL{LITERAL}: } p do_minus(4, 5) p do_minus(8, 9.0) diff --git a/test/testdata/compiler/intrinsics/neq.rb b/test/testdata/compiler/intrinsics/neq.rb index db3ef95d4a..bb9ef023d9 100644 --- a/test/testdata/compiler/intrinsics/neq.rb +++ b/test/testdata/compiler/intrinsics/neq.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL extend T::Sig @@ -16,23 +15,11 @@ def do_neq(x, y) x != y end -# INITIAL-LABEL: @"func_Object#6do_neq" -# INITIAL-NOT: call i64 @sorbet_vm_neq -# INITIAL: call i64{{.*}}@sorbet_i_send -# INITIAL-NOT: call i64 @sorbet_vm_neq -# INITIAL: call i64 @sorbet_rb_int_neq -# INITIAL-NOT: call i64 @sorbet_vm_neq -# INITIAL{LITERAL}: } def do_neq_untyped(x, y) x != y end -# INITIAL-LABEL: @"func_Object#14do_neq_untyped" -# INITIAL-NOT: call i64 @sorbet_rb_int_neq -# INITIAL: call i64 @sorbet_vm_neq -# INITIAL-NOT: call i64 @sorbet_rb_int_neq -# INITIAL{LITERAL}: } p do_neq(4, 5) p do_neq(8, 9.0) diff --git a/test/testdata/compiler/intrinsics/plus.rb b/test/testdata/compiler/intrinsics/plus.rb index f911b68f79..d82af34929 100644 --- a/test/testdata/compiler/intrinsics/plus.rb +++ b/test/testdata/compiler/intrinsics/plus.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL extend T::Sig @@ -16,23 +15,11 @@ def do_plus(x, y) x + y end -# INITIAL-LABEL: @"func_Object#7do_plus" -# INITIAL-NOT: call i64 @sorbet_vm_plus -# INITIAL: call i64{{.*}}@sorbet_i_send -# INITIAL-NOT: call i64 @sorbet_vm_plus -# INITIAL: call i64 @sorbet_rb_int_plus -# INITIAL-NOT: call i64 @sorbet_vm_plus -# INITIAL{LITERAL}: } def do_plus_untyped(x, y) x + y end -# INITIAL-LABEL: @"func_Object#15do_plus_untyped" -# INITIAL-NOT: call i64 @sorbet_rb_int_plus -# INITIAL: call i64 @sorbet_vm_plus -# INITIAL-NOT: call i64 @sorbet_rb_int_plus -# INITIAL{LITERAL}: } p do_plus(4, 5) p do_plus(8, 9.0) diff --git a/test/testdata/compiler/intrinsics/str_eqeq.rb b/test/testdata/compiler/intrinsics/str_eqeq.rb index 248f53e924..2151dbafbd 100644 --- a/test/testdata/compiler/intrinsics/str_eqeq.rb +++ b/test/testdata/compiler/intrinsics/str_eqeq.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL -# run_filecheck: OPT extend T::Sig @@ -11,15 +9,7 @@ def streq(str, obj) str == obj end -# INITIAL-LABEL: "func_Object#5streq" -# INITIAL: call i64 @sorbet_int_rb_str_equal -# INITIAL{LITERAL}: } -# OPT-LABEL: "func_Object#5streq" -# OPT-NOT: call i64 @sorbet_int_rb_str_equal -# OPT: call i64 @rb_str_equal -# OPT-NOT: call i64 @sorbet_int_rb_str_equal -# OPT{LITERAL}: } p streq("str", 1) p streq("str", "str") diff --git a/test/testdata/compiler/intrinsics/str_eqeqeq.rb b/test/testdata/compiler/intrinsics/str_eqeqeq.rb index 4dd6e25f49..ac03406035 100644 --- a/test/testdata/compiler/intrinsics/str_eqeqeq.rb +++ b/test/testdata/compiler/intrinsics/str_eqeqeq.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL -# run_filecheck: OPT extend T::Sig @@ -11,15 +9,7 @@ def streq(str, obj) str === obj end -# INITIAL-LABEL: "func_Object#5streq" -# INITIAL: call i64 @sorbet_int_rb_str_equal -# INITIAL{LITERAL}: } -# OPT-LABEL: "func_Object#5streq" -# OPT-NOT: call i64 @sorbet_int_rb_str_equal -# OPT: call i64 @rb_str_equal -# OPT-NOT: call i64 @sorbet_int_rb_str_equal -# OPT{LITERAL}: } p streq("str", 1) p streq("str", "str") diff --git a/test/testdata/compiler/intrinsics/str_uplus.rb b/test/testdata/compiler/intrinsics/str_uplus.rb index eab1623ecc..a60557bab7 100644 --- a/test/testdata/compiler/intrinsics/str_uplus.rb +++ b/test/testdata/compiler/intrinsics/str_uplus.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL OPT extend T::Sig @@ -11,15 +10,7 @@ def str_uplus s end -# INITIAL-LABEL: @"func_Object#9str_uplus" -# INITIAL: call i64 @sorbet_int_str_uplus # This is sort of a hack to see if we eliminate the exit type test, which we should. -# INITIAL: typeTestSuccess: -# INITIAL{LITERAL}: } -# OPT-LABEL: @"func_Object#9str_uplus" -# OPT: call i64 @sorbet_vm_str_uplus -# OPT-NOT: typeTestSuccess: -# OPT{LITERAL}: } p str_uplus() diff --git a/test/testdata/compiler/intrinsics/string_square_br.rb b/test/testdata/compiler/intrinsics/string_square_br.rb index 9ed5794e14..d9e3379104 100644 --- a/test/testdata/compiler/intrinsics/string_square_br.rb +++ b/test/testdata/compiler/intrinsics/string_square_br.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL extend T::Sig @@ -10,18 +9,12 @@ def string_brackets(str, i) str[i] end -# INITIAL-LABEL: @"func_Object#15string_brackets" -# INITIAL: call i64 @sorbet_int_rb_str_aref_m -# INITIAL{LITERAL}: } sig {params(str: String, i: T.untyped).returns(T.nilable(String))} def string_slice(str, i) str.slice(i) end -# INITIAL-LABEL: @"func_Object#12string_slice" -# INITIAL: call i64 @sorbet_int_rb_str_aref_m -# INITIAL{LITERAL}: } p string_brackets("123", 1) p string_slice("123", 1) diff --git a/test/testdata/compiler/intrinsics/string_start_with.rb b/test/testdata/compiler/intrinsics/string_start_with.rb index 00d5ed9f98..73bcd5fe31 100644 --- a/test/testdata/compiler/intrinsics/string_start_with.rb +++ b/test/testdata/compiler/intrinsics/string_start_with.rb @@ -1,27 +1,19 @@ # frozen_string_literal: true # compiled: true # typed: true -# run_filecheck: OPT x = "hello, world" -# OPT: sorbet_int_rb_str_start_with p x.start_with?() -# OPT: sorbet_int_rb_str_start_with p x.start_with?("he") -# OPT: sorbet_int_rb_str_start_with p x.start_with?("no") -# OPT: sorbet_int_rb_str_start_with p x.start_with?("no", "he") -# OPT: sorbet_int_rb_str_start_with p x.start_with?(/hel+o/) -# OPT: sorbet_int_rb_str_start_with p x.start_with?(/no/, /hel+o/) -# OPT: sorbet_int_rb_str_start_with p x.start_with?("no", /hel+o/) diff --git a/test/testdata/compiler/intrinsics/sym_eqeq.rb b/test/testdata/compiler/intrinsics/sym_eqeq.rb index 04e9c9b86b..2431d0a789 100644 --- a/test/testdata/compiler/intrinsics/sym_eqeq.rb +++ b/test/testdata/compiler/intrinsics/sym_eqeq.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL extend T::Sig @@ -10,9 +9,6 @@ def symeq(sym, obj) sym == obj end -# INITIAL-LABEL: "func_Object#5symeq" -# INITIAL: call i64 @sorbet_rb_sym_equal -# INITIAL{LITERAL}: } p symeq(:sym, 1) p symeq(:sym, :sym) diff --git a/test/testdata/compiler/intrinsics/sym_eqeqeq.rb b/test/testdata/compiler/intrinsics/sym_eqeqeq.rb index eb6d6a45b8..76298cb8d8 100644 --- a/test/testdata/compiler/intrinsics/sym_eqeqeq.rb +++ b/test/testdata/compiler/intrinsics/sym_eqeqeq.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL extend T::Sig @@ -10,9 +9,6 @@ def symeq(sym, obj) sym === obj end -# INITIAL-LABEL: "func_Object#5symeq" -# INITIAL: call i64 @sorbet_rb_sym_equal -# INITIAL{LITERAL}: } p symeq(:sym, 1) p symeq(:sym, :sym) diff --git a/test/testdata/compiler/intrinsics/t_must.opt.ll.exp b/test/testdata/compiler/intrinsics/t_must.opt.ll.exp deleted file mode 100644 index 8d9f38bd68..0000000000 --- a/test/testdata/compiler/intrinsics/t_must.opt.ll.exp +++ /dev/null @@ -1,1030 +0,0 @@ -; ModuleID = 'payload' -source_filename = "llvm-link" -target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } -%struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } -%struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } -%struct.anon.0 = type { i16, [2 x i8] } -%struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } -%struct.rb_iseq_location_struct = type { i64, i64, i64, i64, i32, %struct.rb_code_location_struct } -%struct.rb_code_location_struct = type { %struct.rb_code_position_struct, %struct.rb_code_position_struct } -%struct.rb_code_position_struct = type { i32, i32 } -%struct.iseq_insn_info = type { %struct.iseq_insn_info_entry*, i32*, i32, %struct.succ_index_table* } -%struct.iseq_insn_info_entry = type opaque -%struct.succ_index_table = type opaque -%struct.iseq_catch_table = type opaque -%union.iseq_inline_storage_entry = type { %struct.iseq_inline_cache_entry } -%struct.iseq_inline_cache_entry = type { i64, %struct.rb_cref_struct*, i64 } -%struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } -%struct.rb_scope_visi_struct = type { i8, [3 x i8] } -%struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } -%struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } -%union.anon.13 = type { %struct.rb_method_cfunc_struct } -%struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%struct.rb_calling_info = type { i64, i64, i32, i32 } -%union.anon.15 = type { i32 } -%struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.16 = type { i64, i64, i64, i64* } -%union.anon.17 = type { %struct.anon.18 } -%struct.anon.18 = type { i64, i32 } -%struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } -%struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } -%struct.rb_fiber_struct = type opaque -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } -%struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } -%struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.__pthread_cond_s } -%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } -%union.anon = type { i64 } -%struct.anon.5 = type { [65 x i64] } -%struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } -%struct.rb_event_hook_struct = type opaque -%struct.rb_postponed_job_struct = type opaque -%struct.list_head = type { %struct.list_node } -%struct.rb_objspace = type opaque -%struct.rb_at_exit_list = type { void (%struct.rb_vm_struct*)*, %struct.rb_at_exit_list* } -%struct.rb_builtin_function = type opaque -%struct.anon.6 = type { i64, i64, i64, i64 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } -%union.anon.9 = type { %union.pthread_cond_t } -%union.pthread_mutex_t = type { %struct.__pthread_mutex_s } -%struct.__pthread_mutex_s = type { i32, i32, i32, i32, i32, i16, i16, %struct.__pthread_internal_list } -%struct.__pthread_internal_list = type { %struct.__pthread_internal_list*, %struct.__pthread_internal_list* } -%struct.rb_unblock_callback = type { void (i8*)*, i8* } -%struct.rb_mutex_struct = type opaque -%struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.10 = type { %struct.anon.11 } -%struct.anon.11 = type { i64, i64, i32 } -%struct.st_table = type { i8, i8, i8, i32, %struct.st_hash_type*, i64, i64*, i64, i64, %struct.st_table_entry* } -%struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } -%struct.st_table_entry = type opaque -%struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } -%struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } -%struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } -%struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } -%struct.__sigset_t = type { [16 x i64] } -%struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } -%struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } -%struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } -%struct.rb_call_info_with_kwarg = type { %struct.rb_call_info, %struct.rb_call_info_kw_arg* } -%struct.rb_call_info_kw_arg = type { i32, [1 x i64] } - -@ruby_current_execution_context_ptr = external local_unnamed_addr global %struct.rb_execution_context_struct*, align 8 -@ruby_vm_global_constant_state = external local_unnamed_addr global i64, align 8 -@rb_eTypeError = external local_unnamed_addr global i64, align 8 -@.str.1 = private unnamed_addr constant [25 x i8] c"Passed `nil` into T.must\00", align 1 -@sorbet_getTRetry.retry = internal constant [25 x i8] c"T::Private::Retry::RETRY\00", align 16 -@rb_eRuntimeError = external local_unnamed_addr global i64, align 8 -@.str.9 = private unnamed_addr constant [95 x i8] c"sorbet_getBuildSCMRevision: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@.str.10 = private unnamed_addr constant [93 x i8] c"sorbet_getIsReleaseBuild: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@"stackFramePrecomputed_func_.13" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@iseqEncodedArray = internal global [28 x i64] zeroinitializer -@fileLineNumberInfo = internal global %struct.SorbetLineNumberInfo zeroinitializer -@ic_test_known_nil = internal global %struct.FunctionInlineCache zeroinitializer -@ic_test_nilable_arg = internal global %struct.FunctionInlineCache zeroinitializer -@ic_test_nilable_arg.1 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_test_nilable_arg.2 = internal global %struct.FunctionInlineCache zeroinitializer -@stackFramePrecomputed_func_Test.14test_known_nil = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"stackFramePrecomputed_func_Test.14test_known_nil$block_2" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"stackFramePrecomputed_func_Test.14test_known_nil$block_3" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"" = internal unnamed_addr global i64 0 -@"ic_===" = internal global %struct.FunctionInlineCache zeroinitializer -@ic_puts = internal global %struct.FunctionInlineCache zeroinitializer -@stackFramePrecomputed_func_Test.16test_nilable_arg = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"stackFramePrecomputed_func_Test.16test_nilable_arg$block_2" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"stackFramePrecomputed_func_Test.16test_nilable_arg$block_3" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@ic_puts.3 = internal global %struct.FunctionInlineCache zeroinitializer -@"ic_===.4" = internal global %struct.FunctionInlineCache zeroinitializer -@ic_puts.5 = internal global %struct.FunctionInlineCache zeroinitializer -@"stackFramePrecomputed_func_Test.13" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@sorbet_moduleStringTable = internal unnamed_addr constant [363 x i8] c"\00test/testdata/compiler/intrinsics/t_must.rb\00Test\00test_known_nil\00test_nilable_arg\00master\00\00\00\00\00rescue in test_known_nil\00ensure in test_known_nil\00T\00must\00StandardError\00Module\00===\00puts\00arg\00rescue in test_nilable_arg\00ensure in test_nilable_arg\00 wasn't nil\00\00\00normal\00", align 1 -@sorbet_moduleIDTable = internal unnamed_addr global [18 x i64] zeroinitializer, align 8 -@sorbet_moduleIDDescriptors = internal unnamed_addr constant [18 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 16 }, %struct.rb_code_position_struct { i32 66, i32 14 }, %struct.rb_code_position_struct { i32 81, i32 16 }, %struct.rb_code_position_struct { i32 105, i32 16 }, %struct.rb_code_position_struct { i32 122, i32 7 }, %struct.rb_code_position_struct { i32 130, i32 18 }, %struct.rb_code_position_struct { i32 149, i32 14 }, %struct.rb_code_position_struct { i32 164, i32 24 }, %struct.rb_code_position_struct { i32 189, i32 24 }, %struct.rb_code_position_struct { i32 216, i32 4 }, %struct.rb_code_position_struct { i32 242, i32 3 }, %struct.rb_code_position_struct { i32 246, i32 4 }, %struct.rb_code_position_struct { i32 251, i32 3 }, %struct.rb_code_position_struct { i32 255, i32 26 }, %struct.rb_code_position_struct { i32 282, i32 26 }, %struct.rb_code_position_struct { i32 321, i32 20 }, %struct.rb_code_position_struct { i32 342, i32 13 }, %struct.rb_code_position_struct { i32 356, i32 6 }], align 8 -@sorbet_moduleRubyStringTable = internal unnamed_addr global [10 x i64] zeroinitializer, align 8 -@sorbet_moduleRubyStringDescriptors = internal unnamed_addr constant [10 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 16 }, %struct.rb_code_position_struct { i32 17, i32 43 }, %struct.rb_code_position_struct { i32 66, i32 14 }, %struct.rb_code_position_struct { i32 164, i32 24 }, %struct.rb_code_position_struct { i32 189, i32 24 }, %struct.rb_code_position_struct { i32 81, i32 16 }, %struct.rb_code_position_struct { i32 255, i32 26 }, %struct.rb_code_position_struct { i32 282, i32 26 }, %struct.rb_code_position_struct { i32 309, i32 11 }, %struct.rb_code_position_struct { i32 342, i32 13 }], align 8 -@guard_epoch_Test = linkonce local_unnamed_addr global i64 0 -@guarded_const_Test = linkonce local_unnamed_addr global i64 0 -@rb_eStandardError = external local_unnamed_addr constant i64 -@rb_cModule = external local_unnamed_addr constant i64 - -; Function Attrs: nounwind readnone willreturn -declare i64 @rb_obj_is_kind_of(i64, i64) local_unnamed_addr #0 - -; Function Attrs: noreturn -declare void @sorbet_raiseArity(i32, i32, i32) local_unnamed_addr #1 - -declare %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64, i64, i64, i64, %struct.rb_iseq_struct*, i32, i32, %struct.SorbetLineNumberInfo*, i64*, i32, i32) local_unnamed_addr #2 - -declare void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo*, i64*, i32) local_unnamed_addr #2 - -declare i64 @sorbet_getConstant(i8*, i64) local_unnamed_addr #2 - -declare i64 @sorbet_readRealpath() local_unnamed_addr #2 - -declare %struct.rb_control_frame_struct* @sorbet_pushStaticInitFrame(i64) local_unnamed_addr #2 - -declare void @sorbet_popFrame() local_unnamed_addr #2 - -declare void @sorbet_vm_env_write_slowpath(i64*, i32, i64) local_unnamed_addr #2 - -declare void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) local_unnamed_addr #2 - -declare i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache*, i64) local_unnamed_addr #2 - -declare void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_iseq_struct*) local_unnamed_addr #2 - -declare void @sorbet_setExceptionStackFrame(%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_iseq_struct*) local_unnamed_addr #2 - -declare i64 @sorbet_stringInterpolate(i64, i64, i32, i64*, i64 (i64, i64, i32, i64*, i64)*, i64) local_unnamed_addr #2 - -declare void @sorbet_vm_define_method(i64, i8*, i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)*, i8*, %struct.rb_iseq_struct*, i1 zeroext) local_unnamed_addr #2 - -declare void @sorbet_vm_intern_ids(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #2 - -declare void @sorbet_vm_init_string_table(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #2 - -declare i64 @sorbet_run_exception_handling(%struct.rb_execution_context_struct*, i64 (i64**, i64, %struct.rb_control_frame_struct*)*, i64**, i64, %struct.rb_control_frame_struct*, i64 (i64**, i64, %struct.rb_control_frame_struct*)*, i64 (i64**, i64, %struct.rb_control_frame_struct*)*, i64 (i64**, i64, %struct.rb_control_frame_struct*)*, i64, i64, i64) local_unnamed_addr #2 - -declare i64 @rb_define_module(i8*) local_unnamed_addr #2 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #3 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #3 - -; Function Attrs: noreturn -declare void @rb_raise(i64, i8*, ...) local_unnamed_addr #1 - -; Function Attrs: inaccessiblememonly nofree nosync nounwind willreturn -declare void @llvm.experimental.noalias.scope.decl(metadata) #4 - -declare i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct*, i32) local_unnamed_addr #2 - -; Function Attrs: allocsize(0,1) -declare noalias nonnull i8* @ruby_xcalloc(i64, i64) local_unnamed_addr #5 - -; Function Attrs: allocsize(0,1) -declare noalias nonnull i8* @ruby_xmalloc2(i64, i64) local_unnamed_addr #5 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) #3 - -; Function Attrs: nounwind ssp uwtable -define weak i32 @sorbet_getIsReleaseBuild() local_unnamed_addr #6 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([93 x i8], [93 x i8]* @.str.10, i64 0, i64 0)) #14 - unreachable -} - -; Function Attrs: nounwind ssp uwtable -define weak i8* @sorbet_getBuildSCMRevision() local_unnamed_addr #6 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([95 x i8], [95 x i8]* @.str.9, i64 0, i64 0)) #14 - unreachable -} - -; Function Attrs: sspreq -define void @Init_t_must() local_unnamed_addr #7 { -entry: - %positional_table.i.i = alloca i64, align 8, !dbg !10 - %locals.i22.i = alloca i64, i32 0, align 8 - %locals.i12.i = alloca i64, i32 5, align 8 - %locals.i7.i = alloca i64, i32 4, align 8 - %locals.i.i = alloca i64, i32 0, align 8 - %realpath = tail call i64 @sorbet_readRealpath() - tail call void @sorbet_vm_intern_ids(i64* noundef getelementptr inbounds ([18 x i64], [18 x i64]* @sorbet_moduleIDTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([18 x %struct.rb_code_position_struct], [18 x %struct.rb_code_position_struct]* @sorbet_moduleIDDescriptors, i32 0, i32 0), i32 noundef 18, i8* noundef getelementptr inbounds ([363 x i8], [363 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_vm_init_string_table(i64* noundef getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleRubyStringTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([10 x %struct.rb_code_position_struct], [10 x %struct.rb_code_position_struct]* @sorbet_moduleRubyStringDescriptors, i32 0, i32 0), i32 noundef 10, i8* noundef getelementptr inbounds ([363 x i8], [363 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i32 0, i32 0), i32 noundef 28) - %"rubyId_.i.i" = load i64, i64* getelementptr inbounds ([18 x i64], [18 x i64]* @sorbet_moduleIDTable, i64 0, i64 0), align 8, !invariant.load !5 - %"rubyStr_.i.i" = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 0), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/intrinsics/t_must.rb.i.i" = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_.i.i", i64 %"rubyId_.i.i", i64 %"rubyStr_test/testdata/compiler/intrinsics/t_must.rb.i.i", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 0, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals.i.i, i32 noundef 0, i32 noundef 2) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - %rubyId_test_known_nil.i = load i64, i64* getelementptr inbounds ([18 x i64], [18 x i64]* @sorbet_moduleIDTable, i64 0, i64 1), align 8, !dbg !17, !invariant.load !5 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_test_known_nil, i64 %rubyId_test_known_nil.i, i32 noundef 16, i32 noundef 0, i32 noundef 0), !dbg !17 - %rubyId_test_nilable_arg.i = load i64, i64* getelementptr inbounds ([18 x i64], [18 x i64]* @sorbet_moduleIDTable, i64 0, i64 2), align 8, !dbg !18, !invariant.load !5 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_test_nilable_arg, i64 %rubyId_test_nilable_arg.i, i32 noundef 16, i32 noundef 1, i32 noundef 0), !dbg !18 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_test_nilable_arg.1, i64 %rubyId_test_nilable_arg.i, i32 noundef 16, i32 noundef 1, i32 noundef 0), !dbg !19 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_test_nilable_arg.2, i64 %rubyId_test_nilable_arg.i, i32 noundef 16, i32 noundef 1, i32 noundef 0), !dbg !20 - %1 = bitcast i64* %locals.i7.i to i8* - call void @llvm.lifetime.start.p0i8(i64 32, i8* nonnull %1) - %rubyStr_test_known_nil.i.i = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 2), align 8, !invariant.load !5 - %"rubyId_.i.i" = load i64, i64* getelementptr inbounds ([18 x i64], [18 x i64]* @sorbet_moduleIDTable, i64 0, i64 3), align 8, !invariant.load !5 - store i64 %"rubyId_.i.i", i64* %locals.i7.i, align 8 - %"rubyId_.i.i" = load i64, i64* getelementptr inbounds ([18 x i64], [18 x i64]* @sorbet_moduleIDTable, i64 0, i64 4), align 8, !invariant.load !5 - %2 = getelementptr i64, i64* %locals.i7.i, i32 1 - store i64 %"rubyId_.i.i", i64* %2, align 8 - %"rubyId_.i.i" = load i64, i64* getelementptr inbounds ([18 x i64], [18 x i64]* @sorbet_moduleIDTable, i64 0, i64 5), align 8, !invariant.load !5 - %3 = getelementptr i64, i64* %locals.i7.i, i32 2 - store i64 %"rubyId_.i.i", i64* %3, align 8 - %"rubyId_.i.i" = load i64, i64* getelementptr inbounds ([18 x i64], [18 x i64]* @sorbet_moduleIDTable, i64 0, i64 6), align 8, !invariant.load !5 - %4 = getelementptr i64, i64* %locals.i7.i, i32 3 - store i64 %"rubyId_.i.i", i64* %4, align 8 - %5 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %rubyStr_test_known_nil.i.i, i64 %rubyId_test_known_nil.i, i64 %"rubyStr_test/testdata/compiler/intrinsics/t_must.rb.i.i", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 1, i32 noundef 6, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull align 8 %locals.i7.i, i32 noundef 4, i32 noundef 2) - store %struct.rb_iseq_struct* %5, %struct.rb_iseq_struct** @stackFramePrecomputed_func_Test.14test_known_nil, align 8 - call void @llvm.lifetime.end.p0i8(i64 32, i8* nonnull %1) - %"rubyId_rescue in test_known_nil.i.i" = load i64, i64* getelementptr inbounds ([18 x i64], [18 x i64]* @sorbet_moduleIDTable, i64 0, i64 7), align 8, !invariant.load !5 - %"rubyStr_rescue in test_known_nil.i.i" = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 3), align 8, !invariant.load !5 - %6 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_rescue in test_known_nil.i.i", i64 %"rubyId_rescue in test_known_nil.i.i", i64 %"rubyStr_test/testdata/compiler/intrinsics/t_must.rb.i.i", i64 %realpath, %struct.rb_iseq_struct* %5, i32 noundef 4, i32 noundef 6, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef null, i32 noundef 0, i32 noundef 2) - store %struct.rb_iseq_struct* %6, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Test.14test_known_nil$block_2", align 8 - %stackFrame.i9.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @stackFramePrecomputed_func_Test.14test_known_nil, align 8 - %"rubyId_ensure in test_known_nil.i.i" = load i64, i64* getelementptr inbounds ([18 x i64], [18 x i64]* @sorbet_moduleIDTable, i64 0, i64 8), align 8, !invariant.load !5 - %"rubyStr_ensure in test_known_nil.i.i" = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 4), align 8, !invariant.load !5 - %7 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_ensure in test_known_nil.i.i", i64 %"rubyId_ensure in test_known_nil.i.i", i64 %"rubyStr_test/testdata/compiler/intrinsics/t_must.rb.i.i", i64 %realpath, %struct.rb_iseq_struct* %stackFrame.i9.i, i32 noundef 5, i32 noundef 6, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef null, i32 noundef 0, i32 noundef 2) - store %struct.rb_iseq_struct* %7, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Test.14test_known_nil$block_3", align 8 - %8 = call i64 @sorbet_getConstant(i8* noundef getelementptr inbounds ([25 x i8], [25 x i8]* @sorbet_getTRetry.retry, i64 0, i64 0), i64 noundef 25) #15 - store i64 %8, i64* @"", align 8 - %"rubyId_===.i" = load i64, i64* getelementptr inbounds ([18 x i64], [18 x i64]* @sorbet_moduleIDTable, i64 0, i64 10), align 8, !dbg !21, !invariant.load !5 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @"ic_===", i64 %"rubyId_===.i", i32 noundef 16, i32 noundef 1, i32 noundef 0), !dbg !21 - %rubyId_puts.i = load i64, i64* getelementptr inbounds ([18 x i64], [18 x i64]* @sorbet_moduleIDTable, i64 0, i64 11), align 8, !dbg !24, !invariant.load !5 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts, i64 %rubyId_puts.i, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !24 - %9 = bitcast i64* %locals.i12.i to i8* - call void @llvm.lifetime.start.p0i8(i64 40, i8* nonnull %9) - %rubyStr_test_nilable_arg.i.i = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 5), align 8, !invariant.load !5 - store i64 %"rubyId_.i.i", i64* %locals.i12.i, align 8 - %rubyId_arg.i.i = load i64, i64* getelementptr inbounds ([18 x i64], [18 x i64]* @sorbet_moduleIDTable, i64 0, i64 12), align 8, !invariant.load !5 - %10 = getelementptr i64, i64* %locals.i12.i, i32 1 - store i64 %rubyId_arg.i.i, i64* %10, align 8 - %11 = getelementptr i64, i64* %locals.i12.i, i32 2 - store i64 %"rubyId_.i.i", i64* %11, align 8 - %12 = getelementptr i64, i64* %locals.i12.i, i32 3 - store i64 %"rubyId_.i.i", i64* %12, align 8 - %13 = getelementptr i64, i64* %locals.i12.i, i32 4 - store i64 %"rubyId_.i.i", i64* %13, align 8 - %14 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %rubyStr_test_nilable_arg.i.i, i64 %rubyId_test_nilable_arg.i, i64 %"rubyStr_test/testdata/compiler/intrinsics/t_must.rb.i.i", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 1, i32 noundef 14, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull align 8 %locals.i12.i, i32 noundef 5, i32 noundef 3) - store %struct.rb_iseq_struct* %14, %struct.rb_iseq_struct** @stackFramePrecomputed_func_Test.16test_nilable_arg, align 8 - call void @llvm.lifetime.end.p0i8(i64 40, i8* nonnull %9) - %"rubyId_rescue in test_nilable_arg.i.i" = load i64, i64* getelementptr inbounds ([18 x i64], [18 x i64]* @sorbet_moduleIDTable, i64 0, i64 13), align 8, !invariant.load !5 - %"rubyStr_rescue in test_nilable_arg.i.i" = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 6), align 8, !invariant.load !5 - %15 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_rescue in test_nilable_arg.i.i", i64 %"rubyId_rescue in test_nilable_arg.i.i", i64 %"rubyStr_test/testdata/compiler/intrinsics/t_must.rb.i.i", i64 %realpath, %struct.rb_iseq_struct* %14, i32 noundef 4, i32 noundef 14, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef null, i32 noundef 0, i32 noundef 3) - store %struct.rb_iseq_struct* %15, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Test.16test_nilable_arg$block_2", align 8 - %stackFrame.i19.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @stackFramePrecomputed_func_Test.16test_nilable_arg, align 8 - %"rubyId_ensure in test_nilable_arg.i.i" = load i64, i64* getelementptr inbounds ([18 x i64], [18 x i64]* @sorbet_moduleIDTable, i64 0, i64 14), align 8, !invariant.load !5 - %"rubyStr_ensure in test_nilable_arg.i.i" = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 7), align 8, !invariant.load !5 - %16 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_ensure in test_nilable_arg.i.i", i64 %"rubyId_ensure in test_nilable_arg.i.i", i64 %"rubyStr_test/testdata/compiler/intrinsics/t_must.rb.i.i", i64 %realpath, %struct.rb_iseq_struct* %stackFrame.i19.i, i32 noundef 5, i32 noundef 14, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef null, i32 noundef 0, i32 noundef 3) - store %struct.rb_iseq_struct* %16, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Test.16test_nilable_arg$block_3", align 8 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts.3, i64 %rubyId_puts.i, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !25 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @"ic_===.4", i64 %"rubyId_===.i", i32 noundef 16, i32 noundef 1, i32 noundef 0), !dbg !28 - call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts.5, i64 %rubyId_puts.i, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !30 - %"rubyId_.i.i" = load i64, i64* getelementptr inbounds ([18 x i64], [18 x i64]* @sorbet_moduleIDTable, i64 0, i64 16), align 8, !invariant.load !5 - %"rubyStr_.i.i" = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 9), align 8, !invariant.load !5 - %17 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_.i.i", i64 %"rubyId_.i.i", i64 %"rubyStr_test/testdata/compiler/intrinsics/t_must.rb.i.i", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 3, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals.i22.i, i32 noundef 0, i32 noundef 4) - store %struct.rb_iseq_struct* %17, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Test.13", align 8 - %18 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !31 - %19 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %18, i64 0, i32 2 - %20 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %19, align 8, !tbaa !33 - %stackFrame.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - %21 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %20, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame.i, %struct.rb_iseq_struct** %21, align 8, !tbaa !37 - %22 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %20, i64 0, i32 4 - %23 = load i64*, i64** %22, align 8, !tbaa !39 - %24 = load i64, i64* %23, align 8, !tbaa !6 - %25 = and i64 %24, -33 - store i64 %25, i64* %23, align 8, !tbaa !6 - call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %18, %struct.rb_control_frame_struct* %20, %struct.rb_iseq_struct* %stackFrame.i) #15 - %26 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %20, i64 0, i32 0 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %26, align 8, !dbg !40, !tbaa !31 - %27 = call i64 @rb_define_module(i8* getelementptr inbounds ([363 x i8], [363 x i8]* @sorbet_moduleStringTable, i64 0, i64 61)) #15, !dbg !41 - %28 = call %struct.rb_control_frame_struct* @sorbet_pushStaticInitFrame(i64 %27) #15, !dbg !41 - %29 = bitcast i64* %positional_table.i.i to i8* - call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %29) #15 - %stackFrame.i.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Test.13", align 8 - %30 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !31 - %31 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %30, i64 0, i32 2 - %32 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %31, align 8, !tbaa !33 - %33 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %32, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame.i.i, %struct.rb_iseq_struct** %33, align 8, !tbaa !37 - %34 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %32, i64 0, i32 4 - %35 = load i64*, i64** %34, align 8, !tbaa !39 - %36 = load i64, i64* %35, align 8, !tbaa !6 - %37 = and i64 %36, -33 - store i64 %37, i64* %35, align 8, !tbaa !6 - call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %30, %struct.rb_control_frame_struct* %32, %struct.rb_iseq_struct* %stackFrame.i.i) #15 - %38 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %28, i64 0, i32 0 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 6), i64** %38, align 8, !dbg !42, !tbaa !31 - %39 = load i64, i64* @guard_epoch_Test, align 8, !dbg !43 - %40 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !43, !tbaa !44 - %needTakeSlowPath = icmp ne i64 %39, %40, !dbg !43 - br i1 %needTakeSlowPath, label %41, label %42, !dbg !43, !prof !46 - -41: ; preds = %entry - call void @const_recompute_Test(), !dbg !43 - br label %42, !dbg !43 - -42: ; preds = %entry, %41 - %43 = load i64, i64* @guarded_const_Test, align 8, !dbg !43 - %44 = load i64, i64* @guard_epoch_Test, align 8, !dbg !43 - %45 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !43, !tbaa !44 - %guardUpdated = icmp eq i64 %44, %45, !dbg !43 - call void @llvm.assume(i1 %guardUpdated), !dbg !43 - %stackFrame17.i.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @stackFramePrecomputed_func_Test.14test_known_nil, align 8, !dbg !43 - %46 = call noalias nonnull i8* @ruby_xcalloc(i64 noundef 1, i64 noundef 64) #16, !dbg !43 - %47 = bitcast i8* %46 to i16*, !dbg !43 - %48 = load i16, i16* %47, align 8, !dbg !43 - %49 = and i16 %48, -384, !dbg !43 - store i16 %49, i16* %47, align 8, !dbg !43 - %50 = getelementptr inbounds i8, i8* %46, i64 4, !dbg !43 - call void @llvm.memset.p0i8.i64(i8* nonnull align 4 %50, i8 0, i64 28, i1 false) #15, !dbg !43 - call void @sorbet_vm_define_method(i64 %43, i8* getelementptr inbounds ([363 x i8], [363 x i8]* @sorbet_moduleStringTable, i64 0, i64 66), i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)* noundef @func_Test.14test_known_nil, i8* nonnull %46, %struct.rb_iseq_struct* %stackFrame17.i.i, i1 noundef zeroext true) #15, !dbg !43 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 14), i64** %38, align 8, !dbg !43, !tbaa !31 - %stackFrame26.i.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @stackFramePrecomputed_func_Test.16test_nilable_arg, align 8, !dbg !10 - %51 = call noalias nonnull i8* @ruby_xcalloc(i64 noundef 1, i64 noundef 64) #16, !dbg !10 - %52 = bitcast i8* %51 to i16*, !dbg !10 - %53 = load i16, i16* %52, align 8, !dbg !10 - %54 = and i16 %53, -384, !dbg !10 - %55 = or i16 %54, 1, !dbg !10 - store i16 %55, i16* %52, align 8, !dbg !10 - %56 = getelementptr inbounds i8, i8* %51, i64 8, !dbg !10 - %57 = bitcast i8* %56 to i32*, !dbg !10 - store i32 1, i32* %57, align 8, !dbg !10, !tbaa !47 - %58 = getelementptr inbounds i8, i8* %51, i64 12, !dbg !10 - %59 = getelementptr inbounds i8, i8* %51, i64 4, !dbg !10 - %60 = bitcast i8* %59 to i32*, !dbg !10 - call void @llvm.memset.p0i8.i64(i8* nonnull align 4 %58, i8 0, i64 20, i1 false) #15, !dbg !10 - store i32 1, i32* %60, align 4, !dbg !10, !tbaa !50 - store i64 %rubyId_arg.i.i, i64* %positional_table.i.i, align 8, !dbg !10 - %61 = call noalias nonnull i8* @ruby_xmalloc2(i64 noundef 1, i64 noundef 8) #16, !dbg !10 - call void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture nonnull writeonly align 1 %61, i8* nocapture noundef nonnull readonly align 8 dereferenceable(8) %29, i64 noundef 8, i1 noundef false) #15, !dbg !10 - %62 = getelementptr inbounds i8, i8* %51, i64 32, !dbg !10 - %63 = bitcast i8* %62 to i8**, !dbg !10 - store i8* %61, i8** %63, align 8, !dbg !10, !tbaa !51 - call void @sorbet_vm_define_method(i64 %43, i8* getelementptr inbounds ([363 x i8], [363 x i8]* @sorbet_moduleStringTable, i64 0, i64 81), i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)* noundef @func_Test.16test_nilable_arg, i8* nonnull %51, %struct.rb_iseq_struct* %stackFrame26.i.i, i1 noundef zeroext true) #15, !dbg !10 - call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %29) #15 - call void @sorbet_popFrame() #15, !dbg !41 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 24), i64** %26, align 8, !dbg !41, !tbaa !31 - %64 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %20, i64 0, i32 1, !dbg !17 - %65 = load i64*, i64** %64, align 8, !dbg !17 - store i64 %43, i64* %65, align 8, !dbg !17, !tbaa !6 - %66 = getelementptr inbounds i64, i64* %65, i64 1, !dbg !17 - store i64* %66, i64** %64, align 8, !dbg !17 - %send = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_test_known_nil, i64 0), !dbg !17 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 25), i64** %26, align 8, !dbg !17, !tbaa !31 - %67 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %20, i64 0, i32 1, !dbg !18 - %68 = load i64*, i64** %67, align 8, !dbg !18 - store i64 %43, i64* %68, align 8, !dbg !18, !tbaa !6 - %69 = getelementptr inbounds i64, i64* %68, i64 1, !dbg !18 - store i64 21, i64* %69, align 8, !dbg !18, !tbaa !6 - %70 = getelementptr inbounds i64, i64* %69, i64 1, !dbg !18 - store i64* %70, i64** %67, align 8, !dbg !18 - %send3 = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_test_nilable_arg, i64 0), !dbg !18 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 26), i64** %26, align 8, !dbg !18, !tbaa !31 - %71 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %20, i64 0, i32 1, !dbg !19 - %72 = load i64*, i64** %71, align 8, !dbg !19 - store i64 %43, i64* %72, align 8, !dbg !19, !tbaa !6 - %73 = getelementptr inbounds i64, i64* %72, i64 1, !dbg !19 - store i64 0, i64* %73, align 8, !dbg !19, !tbaa !6 - %74 = getelementptr inbounds i64, i64* %73, i64 1, !dbg !19 - store i64* %74, i64** %71, align 8, !dbg !19 - %send5 = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_test_nilable_arg.1, i64 0), !dbg !19 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 27), i64** %26, align 8, !dbg !19, !tbaa !31 - %75 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %20, i64 0, i32 1, !dbg !20 - %76 = load i64*, i64** %75, align 8, !dbg !20 - store i64 %43, i64* %76, align 8, !dbg !20, !tbaa !6 - %77 = getelementptr inbounds i64, i64* %76, i64 1, !dbg !20 - store i64 8, i64* %77, align 8, !dbg !20, !tbaa !6 - %78 = getelementptr inbounds i64, i64* %77, i64 1, !dbg !20 - store i64* %78, i64** %75, align 8, !dbg !20 - %send7 = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_test_nilable_arg.2, i64 0), !dbg !20 - ret void -} - -; Function Attrs: nounwind sspreq uwtable -define internal i64 @func_Test.14test_known_nil(i32 %argc, i64* nocapture nofree readnone %argArray, i64 %selfRaw, %struct.rb_control_frame_struct* nonnull align 8 dereferenceable(8) %cfp, i8* nocapture nofree readnone %calling, i8* nocapture nofree readnone %callData) #8 !dbg !23 { -functionEntryInitializers: - %0 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 0 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 6), i64** %0, align 8, !tbaa !31 - %tooManyArgs = icmp ugt i32 %argc, 0, !dbg !52 - br i1 %tooManyArgs, label %argCountFailBlock, label %fillRequiredArgs, !dbg !52, !prof !53 - -postProcess: ; preds = %fillRequiredArgs, %exception-continue - %".sroa.0.0" = phi i64 [ %6, %exception-continue ], [ %2, %fillRequiredArgs ], !dbg !54 - ret i64 %".sroa.0.0" - -argCountFailBlock: ; preds = %functionEntryInitializers - tail call void @sorbet_raiseArity(i32 %argc, i32 noundef 0, i32 noundef 0) #17, !dbg !52 - unreachable, !dbg !52 - -fillRequiredArgs: ; preds = %functionEntryInitializers - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 9), i64** %0, align 8, !dbg !54, !tbaa !31 - %1 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !55, !tbaa !31 - %"" = load i64, i64* @"", align 8, !dbg !55 - %2 = tail call i64 @sorbet_run_exception_handling(%struct.rb_execution_context_struct* %1, i64 (i64**, i64, %struct.rb_control_frame_struct*)* noundef @"func_Test.14test_known_nil$block_1", i64** nonnull align 8 dereferenceable(8) %0, i64 noundef 0, %struct.rb_control_frame_struct* nonnull align 8 dereferenceable(8) %cfp, i64 (i64**, i64, %struct.rb_control_frame_struct*)* noundef @"func_Test.14test_known_nil$block_2", i64 (i64**, i64, %struct.rb_control_frame_struct*)* noundef @"func_Test.14test_known_nil$block_4", i64 (i64**, i64, %struct.rb_control_frame_struct*)* noundef @"func_Test.14test_known_nil$block_3", i64 %"", i64 noundef 0, i64 noundef 0), !dbg !55 - %ensureReturnValue = icmp ne i64 %2, 52, !dbg !55 - br i1 %ensureReturnValue, label %postProcess, label %exception-continue, !dbg !55 - -exception-continue: ; preds = %fillRequiredArgs - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 12), i64** %0, align 8, !tbaa !31 - %3 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 4, !dbg !56 - %4 = load i64*, i64** %3, align 8, !dbg !56, !tbaa !39 - %5 = getelementptr inbounds i64, i64* %4, i64 -5, !dbg !56 - %6 = load i64, i64* %5, align 8, !dbg !56, !tbaa !6 - br label %postProcess, !dbg !56 -} - -; Function Attrs: noreturn nounwind ssp -define internal i64 @"func_Test.14test_known_nil$block_1"(i64** nocapture nonnull writeonly align 8 dereferenceable(8) %pc, i64 %localsOffset, %struct.rb_control_frame_struct* nocapture nofree readnone %cfp) #9 !dbg !57 { -functionEntryInitializers: - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 8), i64** %pc, align 8, !tbaa !31 - tail call void @llvm.experimental.noalias.scope.decl(metadata !58) #18, !dbg !61 - %0 = load i64, i64* @rb_eTypeError, align 8, !dbg !61, !tbaa !6, !noalias !58 - tail call void (i64, i8*, ...) @rb_raise(i64 %0, i8* noundef getelementptr inbounds ([25 x i8], [25 x i8]* @.str.1, i64 0, i64 0)) #14, !dbg !61, !noalias !58 - unreachable, !dbg !61 -} - -; Function Attrs: ssp -define internal noundef i64 @"func_Test.14test_known_nil$block_2"(i64** nocapture nofree readnone %pc, i64 %localsOffset, %struct.rb_control_frame_struct* nocapture nofree readnone %cfp) #10 !dbg !22 { -vm_get_ep.exit37: - %0 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !31 - %1 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %0, i64 0, i32 2 - %2 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %1, align 8, !tbaa !33 - %3 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 3 - %4 = load i64, i64* %3, align 8, !tbaa !62 - %stackFrame = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Test.14test_known_nil$block_2", align 8 - tail call void @sorbet_setExceptionStackFrame(%struct.rb_execution_context_struct* %0, %struct.rb_control_frame_struct* %2, %struct.rb_iseq_struct* %stackFrame) #15 - %5 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !31 - %6 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %5, i64 0, i32 2 - %7 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %6, align 8, !tbaa !33 - %8 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %7, i64 0, i32 0 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 9), i64** %8, align 8, !tbaa !31 - %9 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !21, !tbaa !31 - %10 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %9, i64 0, i32 2, !dbg !21 - %11 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %10, align 8, !dbg !21, !tbaa !33 - %12 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %11, i64 0, i32 4, !dbg !21 - %13 = load i64*, i64** %12, align 8, !dbg !21 - %14 = getelementptr inbounds i64, i64* %13, i64 -1, !dbg !21 - %15 = load i64, i64* %14, align 8, !dbg !21, !tbaa !6 - %16 = and i64 %15, -4, !dbg !21 - %17 = inttoptr i64 %16 to i64*, !dbg !21 - %18 = getelementptr inbounds i64, i64* %17, i64 -3, !dbg !21 - %19 = load i64, i64* %18, align 8, !dbg !21, !tbaa !6 - %20 = load i64, i64* @rb_eStandardError, align 8, !dbg !21 - %21 = load i64, i64* @rb_cModule, align 8, !dbg !21 - %22 = tail call i64 @rb_obj_is_kind_of(i64 %19, i64 %20), !dbg !21 - %23 = icmp eq i64 %22, 20, !dbg !21 - %24 = select i1 %23, i64 20, i64 0, !dbg !21 - %25 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %9, i64 0, i32 5, !dbg !21 - %26 = load i32, i32* %25, align 8, !dbg !21, !tbaa !63 - %27 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %9, i64 0, i32 6, !dbg !21 - %28 = load i32, i32* %27, align 4, !dbg !21, !tbaa !64 - %29 = xor i32 %28, -1, !dbg !21 - %30 = and i32 %29, %26, !dbg !21 - %31 = icmp eq i32 %30, 0, !dbg !21 - br i1 %31, label %afterSend, label %86, !dbg !21, !prof !65 - -blockExit: ; preds = %83, %81, %68, %66 - tail call void @sorbet_popFrame() - ret i64 52 - -vm_get_ep.exit35: ; preds = %afterSend - %32 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !66, !tbaa !31 - %33 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %32, i64 0, i32 2, !dbg !66 - %34 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %33, align 8, !dbg !66, !tbaa !33 - %35 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %34, i64 0, i32 4, !dbg !66 - %36 = load i64*, i64** %35, align 8, !dbg !66 - %37 = getelementptr inbounds i64, i64* %36, i64 -1, !dbg !66 - %38 = load i64, i64* %37, align 8, !dbg !66, !tbaa !6 - %39 = and i64 %38, -4, !dbg !66 - %40 = inttoptr i64 %39 to i64*, !dbg !66 - %41 = load i64, i64* %40, align 8, !dbg !66, !tbaa !6 - %42 = and i64 %41, 8, !dbg !66 - %43 = icmp eq i64 %42, 0, !dbg !66 - br i1 %43, label %44, label %46, !dbg !66, !prof !65 - -44: ; preds = %vm_get_ep.exit35 - %45 = getelementptr inbounds i64, i64* %40, i64 -3, !dbg !66 - store i64 8, i64* %45, align 8, !dbg !66, !tbaa !6 - br label %vm_get_ep.exit33, !dbg !66 - -46: ; preds = %vm_get_ep.exit35 - tail call void @sorbet_vm_env_write_slowpath(i64* nonnull align 8 dereferenceable(8) %40, i32 noundef -3, i64 noundef 8) #15, !dbg !66 - br label %vm_get_ep.exit33, !dbg !66 - -vm_get_ep.exit33: ; preds = %44, %46 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 10), i64** %8, align 8, !dbg !67, !tbaa !31 - %47 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !24, !tbaa !31 - %48 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %47, i64 0, i32 2, !dbg !24 - %49 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %48, align 8, !dbg !24, !tbaa !33 - %50 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %49, i64 0, i32 1, !dbg !24 - %51 = load i64*, i64** %50, align 8, !dbg !24 - store i64 %4, i64* %51, align 8, !dbg !24, !tbaa !6 - %52 = getelementptr inbounds i64, i64* %51, i64 1, !dbg !24 - store i64 %19, i64* %52, align 8, !dbg !24, !tbaa !6 - %53 = getelementptr inbounds i64, i64* %52, i64 1, !dbg !24 - store i64* %53, i64** %50, align 8, !dbg !24 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts, i64 0), !dbg !24 - %54 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !24, !tbaa !31 - %55 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %54, i64 0, i32 2, !dbg !24 - %56 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %55, align 8, !dbg !24, !tbaa !33 - %57 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %56, i64 0, i32 4, !dbg !24 - %58 = load i64*, i64** %57, align 8, !dbg !24 - %59 = getelementptr inbounds i64, i64* %58, i64 -1, !dbg !24 - %60 = load i64, i64* %59, align 8, !dbg !24, !tbaa !6 - %61 = and i64 %60, -4, !dbg !24 - %62 = inttoptr i64 %61 to i64*, !dbg !24 - %63 = load i64, i64* %62, align 8, !dbg !24, !tbaa !6 - %64 = and i64 %63, 8, !dbg !24 - %65 = icmp eq i64 %64, 0, !dbg !24 - br i1 %65, label %66, label %68, !dbg !24, !prof !65 - -66: ; preds = %vm_get_ep.exit33 - %67 = getelementptr inbounds i64, i64* %62, i64 -5, !dbg !24 - store i64 %send, i64* %67, align 8, !dbg !24, !tbaa !6 - br label %blockExit, !dbg !24 - -68: ; preds = %vm_get_ep.exit33 - tail call void @sorbet_vm_env_write_slowpath(i64* nonnull align 8 dereferenceable(8) %62, i32 noundef -5, i64 %send) #15, !dbg !24 - br label %blockExit, !dbg !24 - -vm_get_ep.exit: ; preds = %afterSend - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 8), i64** %8, align 8, !tbaa !31 - %69 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !68, !tbaa !31 - %70 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %69, i64 0, i32 2, !dbg !68 - %71 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %70, align 8, !dbg !68, !tbaa !33 - %72 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %71, i64 0, i32 4, !dbg !68 - %73 = load i64*, i64** %72, align 8, !dbg !68 - %74 = getelementptr inbounds i64, i64* %73, i64 -1, !dbg !68 - %75 = load i64, i64* %74, align 8, !dbg !68, !tbaa !6 - %76 = and i64 %75, -4, !dbg !68 - %77 = inttoptr i64 %76 to i64*, !dbg !68 - %78 = load i64, i64* %77, align 8, !dbg !68, !tbaa !6 - %79 = and i64 %78, 8, !dbg !68 - %80 = icmp eq i64 %79, 0, !dbg !68 - br i1 %80, label %81, label %83, !dbg !68, !prof !65 - -81: ; preds = %vm_get_ep.exit - %82 = getelementptr inbounds i64, i64* %77, i64 -6, !dbg !68 - store i64 20, i64* %82, align 8, !dbg !68, !tbaa !6 - br label %blockExit, !dbg !68 - -83: ; preds = %vm_get_ep.exit - tail call void @sorbet_vm_env_write_slowpath(i64* nonnull align 8 dereferenceable(8) %77, i32 noundef -6, i64 noundef 20) #15, !dbg !68 - br label %blockExit, !dbg !68 - -afterSend: ; preds = %86, %vm_get_ep.exit37 - %84 = and i64 %24, -9, !dbg !21 - %85 = icmp ne i64 %84, 0, !dbg !21 - br i1 %85, label %vm_get_ep.exit35, label %vm_get_ep.exit, !dbg !21 - -86: ; preds = %vm_get_ep.exit37 - %87 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %9, i64 0, i32 8, !dbg !21 - %88 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %87, align 8, !dbg !21, !tbaa !69 - %89 = tail call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %88, i32 noundef 0) #15, !dbg !21 - br label %afterSend, !dbg !21 -} - -; Function Attrs: ssp -define internal noundef i64 @"func_Test.14test_known_nil$block_3"(i64** nocapture nofree readnone %pc, i64 %localsOffset, %struct.rb_control_frame_struct* nocapture nofree readnone %cfp) #10 !dbg !70 { -functionEntryInitializers: - %stackFrame = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Test.14test_known_nil$block_3", align 8 - %0 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !31 - %1 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %0, i64 0, i32 2 - %2 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %1, align 8, !tbaa !33 - tail call void @sorbet_setExceptionStackFrame(%struct.rb_execution_context_struct* %0, %struct.rb_control_frame_struct* %2, %struct.rb_iseq_struct* %stackFrame) #15 - %3 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !31 - %4 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %3, i64 0, i32 2 - %5 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %4, align 8, !tbaa !33 - %6 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 0 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 6), i64** %6, align 8, !tbaa !31 - tail call void @sorbet_popFrame() - ret i64 52 -} - -; Function Attrs: argmemonly nofree norecurse nosync nounwind ssp willreturn writeonly -define internal noundef i64 @"func_Test.14test_known_nil$block_4"(i64** nocapture nofree nonnull writeonly align 8 dereferenceable(8) %pc, i64 %localsOffset, %struct.rb_control_frame_struct* nocapture nofree readnone %cfp) #11 !dbg !71 { -functionEntryInitializers: - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 6), i64** %pc, align 8, !tbaa !31 - ret i64 52 -} - -; Function Attrs: nounwind sspreq uwtable -define internal i64 @func_Test.16test_nilable_arg(i32 %argc, i64* nocapture readonly %argArray, i64 %selfRaw, %struct.rb_control_frame_struct* nonnull align 8 dereferenceable(8) %cfp, i8* nocapture nofree readnone %calling, i8* nocapture nofree readnone %callData) #8 !dbg !27 { -functionEntryInitializers: - %0 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 0 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 14), i64** %0, align 8, !tbaa !31 - %tooManyArgs = icmp ugt i32 %argc, 1, !dbg !72 - %tooFewArgs = icmp ult i32 %argc, 1, !dbg !72 - %or.cond = or i1 %tooManyArgs, %tooFewArgs, !dbg !72 - br i1 %or.cond, label %argCountFailBlock, label %fillRequiredArgs, !dbg !72, !prof !73 - -postProcess: ; preds = %sorbet_writeLocal.exit, %exception-continue - %".sroa.0.0" = phi i64 [ %13, %exception-continue ], [ %10, %sorbet_writeLocal.exit ], !dbg !74 - ret i64 %".sroa.0.0" - -argCountFailBlock: ; preds = %functionEntryInitializers - tail call void @sorbet_raiseArity(i32 %argc, i32 noundef 1, i32 noundef 1) #17, !dbg !72 - unreachable, !dbg !72 - -fillRequiredArgs: ; preds = %functionEntryInitializers - %rawArg_arg = load i64, i64* %argArray, align 8, !dbg !72 - %1 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 4, !dbg !72 - %2 = load i64*, i64** %1, align 8, !dbg !72, !tbaa !39 - %3 = load i64, i64* %2, align 8, !dbg !72, !tbaa !6 - %4 = and i64 %3, 8, !dbg !72 - %5 = icmp eq i64 %4, 0, !dbg !72 - br i1 %5, label %6, label %8, !dbg !72, !prof !65 - -6: ; preds = %fillRequiredArgs - %7 = getelementptr inbounds i64, i64* %2, i64 -4, !dbg !72 - store i64 %rawArg_arg, i64* %7, align 8, !dbg !72, !tbaa !6 - br label %sorbet_writeLocal.exit, !dbg !72 - -8: ; preds = %fillRequiredArgs - tail call void @sorbet_vm_env_write_slowpath(i64* nonnull align 8 dereferenceable(8) %2, i32 noundef -4, i64 %rawArg_arg) #15, !dbg !72 - br label %sorbet_writeLocal.exit, !dbg !72 - -sorbet_writeLocal.exit: ; preds = %6, %8 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 18), i64** %0, align 8, !dbg !74, !tbaa !31 - %9 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !75, !tbaa !31 - %"" = load i64, i64* @"", align 8, !dbg !75 - %10 = tail call i64 @sorbet_run_exception_handling(%struct.rb_execution_context_struct* %9, i64 (i64**, i64, %struct.rb_control_frame_struct*)* noundef @"func_Test.16test_nilable_arg$block_1", i64** nonnull %0, i64 noundef 0, %struct.rb_control_frame_struct* nonnull %cfp, i64 (i64**, i64, %struct.rb_control_frame_struct*)* noundef @"func_Test.16test_nilable_arg$block_2", i64 (i64**, i64, %struct.rb_control_frame_struct*)* noundef @"func_Test.16test_nilable_arg$block_4", i64 (i64**, i64, %struct.rb_control_frame_struct*)* noundef @"func_Test.16test_nilable_arg$block_3", i64 %"", i64 noundef 0, i64 noundef 0), !dbg !75 - %ensureReturnValue = icmp ne i64 %10, 52, !dbg !75 - br i1 %ensureReturnValue, label %postProcess, label %exception-continue, !dbg !75 - -exception-continue: ; preds = %sorbet_writeLocal.exit - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 21), i64** %0, align 8, !tbaa !31 - %11 = load i64*, i64** %1, align 8, !dbg !76, !tbaa !39 - %12 = getelementptr inbounds i64, i64* %11, i64 -6, !dbg !76 - %13 = load i64, i64* %12, align 8, !dbg !76, !tbaa !6 - br label %postProcess, !dbg !76 -} - -; Function Attrs: ssp -define internal noundef i64 @"func_Test.16test_nilable_arg$block_1"(i64** nocapture nonnull writeonly align 8 dereferenceable(8) %pc, i64 %localsOffset, %struct.rb_control_frame_struct* nonnull align 8 dereferenceable(40) %cfp) #10 !dbg !26 { -functionEntryInitializers: - %callArgs = alloca [3 x i64], align 8 - %0 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !31 - %1 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %0, i64 0, i32 2 - %2 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %1, align 8, !tbaa !33 - %3 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 3 - %4 = load i64, i64* %3, align 8, !tbaa !62 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 16), i64** %pc, align 8, !tbaa !31 - %5 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 4, !dbg !77 - %6 = load i64*, i64** %5, align 8, !dbg !77, !tbaa !39 - %7 = getelementptr inbounds i64, i64* %6, i64 -4, !dbg !77 - %8 = load i64, i64* %7, align 8, !dbg !77, !tbaa !6 - %callArgs0Addr = getelementptr [3 x i64], [3 x i64]* %callArgs, i64 0, i64 0, !dbg !77 - store i64 %8, i64* %callArgs0Addr, align 8, !dbg !77 - tail call void @llvm.experimental.noalias.scope.decl(metadata !78), !dbg !77 - %9 = icmp eq i64 %8, 8, !dbg !77 - br i1 %9, label %10, label %sorbet_T_must.exit, !dbg !77, !prof !53 - -10: ; preds = %functionEntryInitializers - %11 = load i64, i64* @rb_eTypeError, align 8, !dbg !77, !tbaa !6, !noalias !78 - tail call void (i64, i8*, ...) @rb_raise(i64 %11, i8* noundef getelementptr inbounds ([25 x i8], [25 x i8]* @.str.1, i64 0, i64 0)) #14, !dbg !77, !noalias !78 - unreachable, !dbg !77 - -sorbet_T_must.exit: ; preds = %functionEntryInitializers - %12 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !77, !tbaa !31 - %13 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %12, i64 0, i32 5, !dbg !77 - %14 = load i32, i32* %13, align 8, !dbg !77, !tbaa !63 - %15 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %12, i64 0, i32 6, !dbg !77 - %16 = load i32, i32* %15, align 4, !dbg !77, !tbaa !64 - %17 = xor i32 %16, -1, !dbg !77 - %18 = and i32 %17, %14, !dbg !77 - %19 = icmp eq i32 %18, 0, !dbg !77 - br i1 %19, label %rb_vm_check_ints.exit, label %20, !dbg !77, !prof !65 - -20: ; preds = %sorbet_T_must.exit - %21 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %12, i64 0, i32 8, !dbg !77 - %22 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %21, align 8, !dbg !77, !tbaa !69 - %23 = tail call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %22, i32 noundef 0) #15, !dbg !77 - br label %rb_vm_check_ints.exit, !dbg !77 - -rb_vm_check_ints.exit: ; preds = %sorbet_T_must.exit, %20 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 17), i64** %pc, align 8, !dbg !77, !tbaa !31 - %"rubyStr_ wasn't nil" = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 8), align 8, !dbg !81, !invariant.load !5 - %24 = load i64*, i64** %5, align 8, !dbg !82, !tbaa !39 - %25 = getelementptr inbounds i64, i64* %24, i64 -4, !dbg !82 - %26 = load i64, i64* %25, align 8, !dbg !82, !tbaa !6 - store i64 %26, i64* %callArgs0Addr, align 8, !dbg !82 - %callArgs1Addr = getelementptr [3 x i64], [3 x i64]* %callArgs, i64 0, i64 1, !dbg !82 - store i64 %"rubyStr_ wasn't nil", i64* %callArgs1Addr, align 8, !dbg !82 - %"rubyId_" = load i64, i64* getelementptr inbounds ([18 x i64], [18 x i64]* @sorbet_moduleIDTable, i64 0, i64 15), align 8, !dbg !82, !invariant.load !5 - %rawSendResult13 = call i64 @sorbet_stringInterpolate(i64 noundef 8, i64 %"rubyId_", i32 noundef 2, i64* noundef nonnull %callArgs0Addr, i64 (i64, i64, i32, i64*, i64)* noundef null, i64 noundef 0), !dbg !82 - %27 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 1, !dbg !25 - %28 = load i64*, i64** %27, align 8, !dbg !25 - store i64 %4, i64* %28, align 8, !dbg !25, !tbaa !6 - %29 = getelementptr inbounds i64, i64* %28, i64 1, !dbg !25 - store i64 %rawSendResult13, i64* %29, align 8, !dbg !25, !tbaa !6 - %30 = getelementptr inbounds i64, i64* %29, i64 1, !dbg !25 - store i64* %30, i64** %27, align 8, !dbg !25 - %send = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts.3, i64 0), !dbg !25 - %31 = load i64*, i64** %5, align 8, !dbg !25, !tbaa !39 - %32 = load i64, i64* %31, align 8, !dbg !25, !tbaa !6 - %33 = and i64 %32, 8, !dbg !25 - %34 = icmp eq i64 %33, 0, !dbg !25 - br i1 %34, label %35, label %37, !dbg !25, !prof !65 - -35: ; preds = %rb_vm_check_ints.exit - %36 = getelementptr inbounds i64, i64* %31, i64 -6, !dbg !25 - store i64 %send, i64* %36, align 8, !dbg !25, !tbaa !6 - br label %sorbet_writeLocal.exit, !dbg !25 - -37: ; preds = %rb_vm_check_ints.exit - call void @sorbet_vm_env_write_slowpath(i64* nonnull align 8 dereferenceable(8) %31, i32 noundef -6, i64 %send) #15, !dbg !25 - br label %sorbet_writeLocal.exit, !dbg !25 - -sorbet_writeLocal.exit: ; preds = %35, %37 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 18), i64** %pc, align 8, !dbg !25, !tbaa !31 - ret i64 52 -} - -; Function Attrs: ssp -define internal noundef i64 @"func_Test.16test_nilable_arg$block_2"(i64** nocapture nofree readnone %pc, i64 %localsOffset, %struct.rb_control_frame_struct* nocapture nofree readnone %cfp) #10 !dbg !29 { -vm_get_ep.exit37: - %0 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !31 - %1 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %0, i64 0, i32 2 - %2 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %1, align 8, !tbaa !33 - %3 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 3 - %4 = load i64, i64* %3, align 8, !tbaa !62 - %stackFrame = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Test.16test_nilable_arg$block_2", align 8 - tail call void @sorbet_setExceptionStackFrame(%struct.rb_execution_context_struct* %0, %struct.rb_control_frame_struct* %2, %struct.rb_iseq_struct* %stackFrame) #15 - %5 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !31 - %6 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %5, i64 0, i32 2 - %7 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %6, align 8, !tbaa !33 - %8 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %7, i64 0, i32 0 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 18), i64** %8, align 8, !tbaa !31 - %9 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !28, !tbaa !31 - %10 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %9, i64 0, i32 2, !dbg !28 - %11 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %10, align 8, !dbg !28, !tbaa !33 - %12 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %11, i64 0, i32 4, !dbg !28 - %13 = load i64*, i64** %12, align 8, !dbg !28 - %14 = getelementptr inbounds i64, i64* %13, i64 -1, !dbg !28 - %15 = load i64, i64* %14, align 8, !dbg !28, !tbaa !6 - %16 = and i64 %15, -4, !dbg !28 - %17 = inttoptr i64 %16 to i64*, !dbg !28 - %18 = getelementptr inbounds i64, i64* %17, i64 -3, !dbg !28 - %19 = load i64, i64* %18, align 8, !dbg !28, !tbaa !6 - %20 = load i64, i64* @rb_eStandardError, align 8, !dbg !28 - %21 = load i64, i64* @rb_cModule, align 8, !dbg !28 - %22 = tail call i64 @rb_obj_is_kind_of(i64 %19, i64 %20), !dbg !28 - %23 = icmp eq i64 %22, 20, !dbg !28 - %24 = select i1 %23, i64 20, i64 0, !dbg !28 - %25 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %9, i64 0, i32 5, !dbg !28 - %26 = load i32, i32* %25, align 8, !dbg !28, !tbaa !63 - %27 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %9, i64 0, i32 6, !dbg !28 - %28 = load i32, i32* %27, align 4, !dbg !28, !tbaa !64 - %29 = xor i32 %28, -1, !dbg !28 - %30 = and i32 %29, %26, !dbg !28 - %31 = icmp eq i32 %30, 0, !dbg !28 - br i1 %31, label %afterSend, label %86, !dbg !28, !prof !65 - -blockExit: ; preds = %83, %81, %68, %66 - tail call void @sorbet_popFrame() - ret i64 52 - -vm_get_ep.exit35: ; preds = %afterSend - %32 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !83, !tbaa !31 - %33 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %32, i64 0, i32 2, !dbg !83 - %34 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %33, align 8, !dbg !83, !tbaa !33 - %35 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %34, i64 0, i32 4, !dbg !83 - %36 = load i64*, i64** %35, align 8, !dbg !83 - %37 = getelementptr inbounds i64, i64* %36, i64 -1, !dbg !83 - %38 = load i64, i64* %37, align 8, !dbg !83, !tbaa !6 - %39 = and i64 %38, -4, !dbg !83 - %40 = inttoptr i64 %39 to i64*, !dbg !83 - %41 = load i64, i64* %40, align 8, !dbg !83, !tbaa !6 - %42 = and i64 %41, 8, !dbg !83 - %43 = icmp eq i64 %42, 0, !dbg !83 - br i1 %43, label %44, label %46, !dbg !83, !prof !65 - -44: ; preds = %vm_get_ep.exit35 - %45 = getelementptr inbounds i64, i64* %40, i64 -3, !dbg !83 - store i64 8, i64* %45, align 8, !dbg !83, !tbaa !6 - br label %vm_get_ep.exit33, !dbg !83 - -46: ; preds = %vm_get_ep.exit35 - tail call void @sorbet_vm_env_write_slowpath(i64* nonnull align 8 dereferenceable(8) %40, i32 noundef -3, i64 noundef 8) #15, !dbg !83 - br label %vm_get_ep.exit33, !dbg !83 - -vm_get_ep.exit33: ; preds = %44, %46 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 19), i64** %8, align 8, !dbg !84, !tbaa !31 - %47 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !30, !tbaa !31 - %48 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %47, i64 0, i32 2, !dbg !30 - %49 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %48, align 8, !dbg !30, !tbaa !33 - %50 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %49, i64 0, i32 1, !dbg !30 - %51 = load i64*, i64** %50, align 8, !dbg !30 - store i64 %4, i64* %51, align 8, !dbg !30, !tbaa !6 - %52 = getelementptr inbounds i64, i64* %51, i64 1, !dbg !30 - store i64 %19, i64* %52, align 8, !dbg !30, !tbaa !6 - %53 = getelementptr inbounds i64, i64* %52, i64 1, !dbg !30 - store i64* %53, i64** %50, align 8, !dbg !30 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts.5, i64 0), !dbg !30 - %54 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !30, !tbaa !31 - %55 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %54, i64 0, i32 2, !dbg !30 - %56 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %55, align 8, !dbg !30, !tbaa !33 - %57 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %56, i64 0, i32 4, !dbg !30 - %58 = load i64*, i64** %57, align 8, !dbg !30 - %59 = getelementptr inbounds i64, i64* %58, i64 -1, !dbg !30 - %60 = load i64, i64* %59, align 8, !dbg !30, !tbaa !6 - %61 = and i64 %60, -4, !dbg !30 - %62 = inttoptr i64 %61 to i64*, !dbg !30 - %63 = load i64, i64* %62, align 8, !dbg !30, !tbaa !6 - %64 = and i64 %63, 8, !dbg !30 - %65 = icmp eq i64 %64, 0, !dbg !30 - br i1 %65, label %66, label %68, !dbg !30, !prof !65 - -66: ; preds = %vm_get_ep.exit33 - %67 = getelementptr inbounds i64, i64* %62, i64 -6, !dbg !30 - store i64 %send, i64* %67, align 8, !dbg !30, !tbaa !6 - br label %blockExit, !dbg !30 - -68: ; preds = %vm_get_ep.exit33 - tail call void @sorbet_vm_env_write_slowpath(i64* nonnull align 8 dereferenceable(8) %62, i32 noundef -6, i64 %send) #15, !dbg !30 - br label %blockExit, !dbg !30 - -vm_get_ep.exit: ; preds = %afterSend - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 16), i64** %8, align 8, !tbaa !31 - %69 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !85, !tbaa !31 - %70 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %69, i64 0, i32 2, !dbg !85 - %71 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %70, align 8, !dbg !85, !tbaa !33 - %72 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %71, i64 0, i32 4, !dbg !85 - %73 = load i64*, i64** %72, align 8, !dbg !85 - %74 = getelementptr inbounds i64, i64* %73, i64 -1, !dbg !85 - %75 = load i64, i64* %74, align 8, !dbg !85, !tbaa !6 - %76 = and i64 %75, -4, !dbg !85 - %77 = inttoptr i64 %76 to i64*, !dbg !85 - %78 = load i64, i64* %77, align 8, !dbg !85, !tbaa !6 - %79 = and i64 %78, 8, !dbg !85 - %80 = icmp eq i64 %79, 0, !dbg !85 - br i1 %80, label %81, label %83, !dbg !85, !prof !65 - -81: ; preds = %vm_get_ep.exit - %82 = getelementptr inbounds i64, i64* %77, i64 -7, !dbg !85 - store i64 20, i64* %82, align 8, !dbg !85, !tbaa !6 - br label %blockExit, !dbg !85 - -83: ; preds = %vm_get_ep.exit - tail call void @sorbet_vm_env_write_slowpath(i64* nonnull align 8 dereferenceable(8) %77, i32 noundef -7, i64 noundef 20) #15, !dbg !85 - br label %blockExit, !dbg !85 - -afterSend: ; preds = %86, %vm_get_ep.exit37 - %84 = and i64 %24, -9, !dbg !28 - %85 = icmp ne i64 %84, 0, !dbg !28 - br i1 %85, label %vm_get_ep.exit35, label %vm_get_ep.exit, !dbg !28 - -86: ; preds = %vm_get_ep.exit37 - %87 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %9, i64 0, i32 8, !dbg !28 - %88 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %87, align 8, !dbg !28, !tbaa !69 - %89 = tail call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %88, i32 noundef 0) #15, !dbg !28 - br label %afterSend, !dbg !28 -} - -; Function Attrs: ssp -define internal noundef i64 @"func_Test.16test_nilable_arg$block_3"(i64** nocapture nofree readnone %pc, i64 %localsOffset, %struct.rb_control_frame_struct* nocapture nofree readnone %cfp) #10 !dbg !86 { -functionEntryInitializers: - %stackFrame = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Test.16test_nilable_arg$block_3", align 8 - %0 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !31 - %1 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %0, i64 0, i32 2 - %2 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %1, align 8, !tbaa !33 - tail call void @sorbet_setExceptionStackFrame(%struct.rb_execution_context_struct* %0, %struct.rb_control_frame_struct* %2, %struct.rb_iseq_struct* %stackFrame) #15 - %3 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !31 - %4 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %3, i64 0, i32 2 - %5 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %4, align 8, !tbaa !33 - %6 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 0 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 14), i64** %6, align 8, !tbaa !31 - tail call void @sorbet_popFrame() - ret i64 52 -} - -; Function Attrs: argmemonly nofree norecurse nosync nounwind ssp willreturn writeonly -define internal noundef i64 @"func_Test.16test_nilable_arg$block_4"(i64** nocapture nofree nonnull writeonly align 8 dereferenceable(8) %pc, i64 %localsOffset, %struct.rb_control_frame_struct* nocapture nofree readnone %cfp) #11 !dbg !87 { -functionEntryInitializers: - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 14), i64** %pc, align 8, !tbaa !31 - ret i64 52 -} - -; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly -declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #12 - -; Function Attrs: nofree nosync nounwind willreturn -declare void @llvm.assume(i1 noundef) #13 - -; Function Attrs: ssp -define linkonce void @const_recompute_Test() local_unnamed_addr #10 { - %1 = tail call i64 @sorbet_getConstant(i8* getelementptr inbounds ([363 x i8], [363 x i8]* @sorbet_moduleStringTable, i64 0, i64 61), i64 4) - store i64 %1, i64* @guarded_const_Test, align 8 - %2 = load i64, i64* @ruby_vm_global_constant_state, align 8, !tbaa !44 - store i64 %2, i64* @guard_epoch_Test, align 8 - ret void -} - -attributes #0 = { nounwind readnone willreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #1 = { noreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #2 = { "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #3 = { argmemonly nofree nosync nounwind willreturn } -attributes #4 = { inaccessiblememonly nofree nosync nounwind willreturn } -attributes #5 = { allocsize(0,1) "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #6 = { nounwind ssp uwtable "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #7 = { sspreq } -attributes #8 = { nounwind sspreq uwtable } -attributes #9 = { noreturn nounwind ssp } -attributes #10 = { ssp } -attributes #11 = { argmemonly nofree norecurse nosync nounwind ssp willreturn writeonly } -attributes #12 = { argmemonly nofree nosync nounwind willreturn writeonly } -attributes #13 = { nofree nosync nounwind willreturn } -attributes #14 = { noreturn nounwind } -attributes #15 = { nounwind } -attributes #16 = { nounwind allocsize(0,1) } -attributes #17 = { noreturn } -attributes #18 = { willreturn } - -!llvm.module.flags = !{!0, !1, !2} -!llvm.dbg.cu = !{!3} - -!0 = !{i32 2, !"Debug Info Version", i32 3} -!1 = !{i32 4, !"cf-protection-return", i32 1} -!2 = !{i32 4, !"cf-protection-branch", i32 1} -!3 = distinct !DICompileUnit(language: DW_LANG_C, file: !4, producer: "Sorbet LLVM", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !5) -!4 = !DIFile(filename: "test/testdata/compiler/intrinsics/t_must.rb", directory: ".") -!5 = !{} -!6 = !{!7, !7, i64 0} -!7 = !{!"long", !8, i64 0} -!8 = !{!"omnipotent char", !9, i64 0} -!9 = !{!"Simple C/C++ TBAA"} -!10 = !DILocation(line: 14, column: 3, scope: !11, inlinedAt: !15) -!11 = distinct !DISubprogram(name: "Test.", linkageName: "func_Test.13L62", scope: null, file: !4, line: 5, type: !12, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!12 = !DISubroutineType(types: !13) -!13 = !{!14} -!14 = !DIBasicType(name: "VALUE", size: 64, encoding: DW_ATE_signed) -!15 = distinct !DILocation(line: 5, column: 1, scope: !16) -!16 = distinct !DISubprogram(name: ".", linkageName: "func_.13", scope: null, file: !4, line: 5, type: !12, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!17 = !DILocation(line: 24, column: 1, scope: !16) -!18 = !DILocation(line: 25, column: 1, scope: !16) -!19 = !DILocation(line: 26, column: 1, scope: !16) -!20 = !DILocation(line: 27, column: 1, scope: !16) -!21 = !DILocation(line: 9, column: 15, scope: !22) -!22 = distinct !DISubprogram(name: "Test.test_known_nil", linkageName: "func_Test.14test_known_nil$block_2", scope: !23, file: !4, line: 6, type: !12, scopeLine: 6, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!23 = distinct !DISubprogram(name: "Test.test_known_nil", linkageName: "func_Test.14test_known_nil", scope: null, file: !4, line: 6, type: !12, scopeLine: 6, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!24 = !DILocation(line: 10, column: 7, scope: !22) -!25 = !DILocation(line: 17, column: 7, scope: !26) -!26 = distinct !DISubprogram(name: "Test.test_nilable_arg", linkageName: "func_Test.16test_nilable_arg$block_1", scope: !27, file: !4, line: 14, type: !12, scopeLine: 14, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!27 = distinct !DISubprogram(name: "Test.test_nilable_arg", linkageName: "func_Test.16test_nilable_arg", scope: null, file: !4, line: 14, type: !12, scopeLine: 14, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!28 = !DILocation(line: 18, column: 15, scope: !29) -!29 = distinct !DISubprogram(name: "Test.test_nilable_arg", linkageName: "func_Test.16test_nilable_arg$block_2", scope: !27, file: !4, line: 14, type: !12, scopeLine: 14, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!30 = !DILocation(line: 19, column: 7, scope: !29) -!31 = !{!32, !32, i64 0} -!32 = !{!"any pointer", !8, i64 0} -!33 = !{!34, !32, i64 16} -!34 = !{!"rb_execution_context_struct", !32, i64 0, !7, i64 8, !32, i64 16, !32, i64 24, !32, i64 32, !35, i64 40, !35, i64 44, !32, i64 48, !32, i64 56, !32, i64 64, !7, i64 72, !7, i64 80, !32, i64 88, !7, i64 96, !32, i64 104, !32, i64 112, !7, i64 120, !7, i64 128, !8, i64 136, !8, i64 137, !7, i64 144, !36, i64 152} -!35 = !{!"int", !8, i64 0} -!36 = !{!"", !32, i64 0, !32, i64 8, !7, i64 16, !8, i64 24} -!37 = !{!38, !32, i64 16} -!38 = !{!"rb_control_frame_struct", !32, i64 0, !32, i64 8, !32, i64 16, !7, i64 24, !32, i64 32, !32, i64 40, !32, i64 48} -!39 = !{!38, !32, i64 32} -!40 = !DILocation(line: 0, scope: !16) -!41 = !DILocation(line: 5, column: 1, scope: !16) -!42 = !DILocation(line: 0, scope: !11, inlinedAt: !15) -!43 = !DILocation(line: 6, column: 3, scope: !11, inlinedAt: !15) -!44 = !{!45, !45, i64 0} -!45 = !{!"long long", !8, i64 0} -!46 = !{!"branch_weights", i32 1, i32 10000} -!47 = !{!48, !35, i64 8} -!48 = !{!"rb_sorbet_param_struct", !49, i64 0, !35, i64 4, !35, i64 8, !35, i64 12, !35, i64 16, !35, i64 20, !35, i64 24, !35, i64 28, !32, i64 32, !35, i64 40, !35, i64 44, !35, i64 48, !35, i64 52, !32, i64 56} -!49 = !{!"", !35, i64 0, !35, i64 0, !35, i64 0, !35, i64 0, !35, i64 0, !35, i64 0, !35, i64 0, !35, i64 0, !35, i64 1, !35, i64 1} -!50 = !{!48, !35, i64 4} -!51 = !{!48, !32, i64 32} -!52 = !DILocation(line: 6, column: 3, scope: !23) -!53 = !{!"branch_weights", i32 1, i32 2000} -!54 = !DILocation(line: 0, scope: !23) -!55 = !DILocation(line: 9, column: 5, scope: !23) -!56 = !DILocation(line: 12, column: 3, scope: !23) -!57 = distinct !DISubprogram(name: "Test.test_known_nil", linkageName: "func_Test.14test_known_nil$block_1", scope: !23, file: !4, line: 6, type: !12, scopeLine: 6, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!58 = !{!59} -!59 = distinct !{!59, !60, !"sorbet_T_must: argument 0"} -!60 = distinct !{!60, !"sorbet_T_must"} -!61 = !DILocation(line: 8, column: 7, scope: !57) -!62 = !{!38, !7, i64 24} -!63 = !{!34, !35, i64 40} -!64 = !{!34, !35, i64 44} -!65 = !{!"branch_weights", i32 2000, i32 1} -!66 = !DILocation(line: 0, scope: !22) -!67 = !DILocation(line: 9, column: 5, scope: !22) -!68 = !DILocation(line: 8, column: 7, scope: !22) -!69 = !{!34, !32, i64 56} -!70 = distinct !DISubprogram(name: "Test.test_known_nil", linkageName: "func_Test.14test_known_nil$block_3", scope: !23, file: !4, line: 6, type: !12, scopeLine: 6, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!71 = distinct !DISubprogram(name: "Test.test_known_nil", linkageName: "func_Test.14test_known_nil$block_4", scope: !23, file: !4, line: 6, type: !12, scopeLine: 6, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!72 = !DILocation(line: 14, column: 3, scope: !27) -!73 = !{!"branch_weights", i32 4001, i32 4000000} -!74 = !DILocation(line: 0, scope: !27) -!75 = !DILocation(line: 18, column: 5, scope: !27) -!76 = !DILocation(line: 21, column: 3, scope: !27) -!77 = !DILocation(line: 16, column: 7, scope: !26) -!78 = !{!79} -!79 = distinct !{!79, !80, !"sorbet_T_must: argument 0"} -!80 = distinct !{!80, !"sorbet_T_must"} -!81 = !DILocation(line: 17, column: 19, scope: !26) -!82 = !DILocation(line: 17, column: 12, scope: !26) -!83 = !DILocation(line: 0, scope: !29) -!84 = !DILocation(line: 18, column: 5, scope: !29) -!85 = !DILocation(line: 16, column: 7, scope: !29) -!86 = distinct !DISubprogram(name: "Test.test_nilable_arg", linkageName: "func_Test.16test_nilable_arg$block_3", scope: !27, file: !4, line: 14, type: !12, scopeLine: 14, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!87 = distinct !DISubprogram(name: "Test.test_nilable_arg", linkageName: "func_Test.16test_nilable_arg$block_4", scope: !27, file: !4, line: 14, type: !12, scopeLine: 14, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) diff --git a/test/testdata/compiler/intrinsics/thread_aref_aset.rb b/test/testdata/compiler/intrinsics/thread_aref_aset.rb index a38036b5d1..02957d361c 100644 --- a/test/testdata/compiler/intrinsics/thread_aref_aset.rb +++ b/test/testdata/compiler/intrinsics/thread_aref_aset.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL OPT extend T::Sig @@ -10,82 +9,40 @@ def thread_current Thread.current end -# INITIAL-LABEL: define internal i64 @"func_Object#14thread_current -# INITIAL-NOT: call i64 @sorbet_i_send -# INITIAL: call i64 @sorbet_Thread_current -# INITIAL-NOT: call i64 @sorbet_i_send -# INITIAL{LITERAL}: } -# OPT-LABEL: define internal i64 @"func_Object#14thread_current -# OPT-NOT: call i64 @callFuncWithCache -# OPT: call i64 @rb_thread_current -# OPT-NOT: call i64 @callFuncWithCache -# OPT{LITERAL}: } sig {returns(Thread)} def thread_main Thread.main end -# INITIAL-LABEL: define internal i64 @"func_Object#11thread_main -# INITIAL-NOT: call i64 @sorbet_i_send -# INITIAL: call i64 @sorbet_Thread_main -# INITIAL-NOT: call i64 @sorbet_i_send -# INITIAL{LITERAL}: } -# OPT-LABEL: define internal i64 @"func_Object#11thread_main -# OPT-NOT: call i64 @callFuncWithCache -# OPT: call i64 @rb_thread_main -# OPT-NOT: call i64 @callFuncWithCache -# OPT{LITERAL}: } sig {params(thread: Thread).returns(T.untyped)} def thread_aref_constant(thread) thread[:my_key] end -# INITIAL-LABEL: define internal i64 @"func_Object#20thread_aref_constant -# INITIAL: call i64 @sorbet_Thread_square_br_symarg -# INITIAL{LITERAL}: } -# OPT-LABEL: define internal i64 @"func_Object#20thread_aref_constant -# OPT-NOT: call i64 @rb_id2sym -# OPT: call i64 @rb_thread_local_aref -# OPT-NOT: call i64 @rb_id2sym -# OPT{LITERAL}: } sig {params(thread: Thread, key: T.untyped).returns(T.untyped)} def thread_aref(thread, key) thread[key] end -# INITIAL-LABEL: define internal i64 @"func_Object#11thread_aref -# INITIAL: call i64 @sorbet_Thread_square_br -# INITIAL{LITERAL}: } sig {params(thread: Thread, val: T.untyped).returns(T.untyped)} def thread_aset_constant(thread, val) thread[:my_key] = val end -# INITIAL-LABEL: define internal i64 @"func_Object#20thread_aset_constant -# INITIAL: call i64 @sorbet_Thread_square_br_eq_symarg -# INITIAL{LITERAL}: } -# OPT-LABEL: define internal i64 @"func_Object#20thread_aset_constant -# OPT-NOT: call i64 @rb_id2sym -# OPT: call i64 @sorbet_Thread_square_br_eq_symarg -# OPT-NOT: call i64 @rb_id2sym -# OPT{LITERAL}: } sig {params(thread: Thread, key: T.untyped, val: T.untyped).returns(T.untyped)} def thread_aset(thread, key, val) thread[key] = val end -# INITIAL-LABEL: define internal i64 @"func_Object#11thread_aset -# INITIAL: call i64 @sorbet_Thread_square_br_eq -# INITIAL{LITERAL}: } current = thread_current p(current.class) diff --git a/test/testdata/compiler/keyword_arg.rb b/test/testdata/compiler/keyword_arg.rb index 5d8828d3e5..6a50799b12 100644 --- a/test/testdata/compiler/keyword_arg.rb +++ b/test/testdata/compiler/keyword_arg.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL extend T::Sig @@ -93,10 +92,3 @@ def self.builder_final(name, # static-init gets generated prior to the method definitions, so we look for the # calls first. -# INITIAL: call i64 @direct_func_FinalWrapper.13my_name_final -# INITIAL: call i64 @direct_func_FinalWrapper.7f_final -# INITIAL-COUNT-5: call i64 @direct_func_FinalWrapper2.13builder_final -# -# INITIAL: define i64 @direct_func_FinalWrapper.13my_name_final -# INITIAL: define i64 @direct_func_FinalWrapper.7f_final -# INITIAL: define i64 @direct_func_FinalWrapper2.13builder_final diff --git a/test/testdata/compiler/keyword_args_singleton_hash_large_small_impl.rb b/test/testdata/compiler/keyword_args_singleton_hash_large_small_impl.rb index 03f9d887f3..5e02aff8b2 100644 --- a/test/testdata/compiler/keyword_args_singleton_hash_large_small_impl.rb +++ b/test/testdata/compiler/keyword_args_singleton_hash_large_small_impl.rb @@ -21,7 +21,7 @@ def self.takes_keyword_args(hash={}) prop06: 0, prop07: 0, prop08: 0, - prop09: 0, # The ninth element causes this hash to overlflow the "small hash" implementation. + prop09: 0, # The ninth element causes this hash to overflow the "small hash" implementation. prop10: 0, # but we add a bunch more in case Ruby ever decides to define "small" as bigger than 8 prop11: 0, prop12: 0, diff --git a/test/testdata/compiler/keyword_args_singleton_pin__2.rb b/test/testdata/compiler/keyword_args_singleton_pin__2.rb index 8f425babf7..30535e68e9 100644 --- a/test/testdata/compiler/keyword_args_singleton_pin__2.rb +++ b/test/testdata/compiler/keyword_args_singleton_pin__2.rb @@ -5,11 +5,11 @@ # This file is compiled, which means we will create a keywordArgsSingleton for # it. This will be allocated on Ruby's heap. The Init_ function will have a # reference to this on the C stack as a local (depending on how the -# optimizations shake out), but there will be no naturally-occuring reference +# optimizations shake out), but there will be no naturally-occurring reference # to this Hash otherwise because the Ruby GC does not look for pointers into # its heap in shared objects' memory spaces. # -# So: once this compiled file loads (i.e., the require finishes), there will be +# So once this compiled file loads (i.e., the require finishes), there will be # no reference to the keywordArgsSingleton, unless we mark it for the GC. class Main diff --git a/test/testdata/compiler/kwargs_splat_hash_dup.rb b/test/testdata/compiler/kwargs_splat_hash_dup.rb index 483413e8ba..d1d567fe19 100644 --- a/test/testdata/compiler/kwargs_splat_hash_dup.rb +++ b/test/testdata/compiler/kwargs_splat_hash_dup.rb @@ -1,21 +1,11 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL -# run_filecheck: LOWERED def foo(**kwargs) puts kwargs end -# INITIAL-LABEL: call i64 @rb_to_hash_type -# INITIAL: call i64 @rb_hash_dup -# INITIAL{LITERAL}: } - -# LOWERED-LABEL: call i64 @rb_to_hash_type -# LOWERED: call i64 @rb_hash_dup -# LOWERED{LITERAL}: } - args = {a: 1, b: 2} foo(**args) puts args diff --git a/test/testdata/compiler/kwargs_splat_hash_no_dup.rb b/test/testdata/compiler/kwargs_splat_hash_no_dup.rb index d707b6adcb..5674d94dd2 100644 --- a/test/testdata/compiler/kwargs_splat_hash_no_dup.rb +++ b/test/testdata/compiler/kwargs_splat_hash_no_dup.rb @@ -1,33 +1,14 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL -# run_filecheck: LOWERED # We look for sorbet_magic_toHashDup instead of rb_to_hash_type followed by rb_hash_dup, # because in the INITIAL stage, sorbet_magic_toHashDup has not been inlined yet. -# INITIAL-LABEL: define internal i64 @"func_Object#3foo" -# INITIAL: call i64 @sorbet_magic_toHashDup -# INITIAL{LITERAL}: } - -# LOWERED-LABEL: define internal i64 @"func_Object#3foo" -# LOWERED: call i64 @rb_to_hash_type -# LOWERED-NOT: call i64 @rb_hash_dup -# LOWERED{LITERAL}: } - def foo(**kwargs) puts(**kwargs) end -# INITIAL-LABEL: define internal i64 @"func_Object#4main" -# INITIAL: call i64 @sorbet_magic_toHashDup -# INITIAL{LITERAL}: } - -# LOWERED-LABEL: define internal i64 @"func_Object#4main" -# LOWERED: call i64 @rb_to_hash_type -# LOWERED-NOT: call i64 @rb_hash_dup -# LOWERED{LITERAL}: } def main args = {a: 1, b: 2} diff --git a/test/testdata/compiler/literal_hash.opt.ll.exp b/test/testdata/compiler/literal_hash.opt.ll.exp deleted file mode 100644 index 51968d72f8..0000000000 --- a/test/testdata/compiler/literal_hash.opt.ll.exp +++ /dev/null @@ -1,440 +0,0 @@ -; ModuleID = 'payload' -source_filename = "llvm-link" -target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } -%struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.__pthread_cond_s } -%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } -%union.anon = type { i64 } -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } -%struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } -%struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } -%struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } -%struct.anon.0 = type { i16, [2 x i8] } -%struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } -%struct.rb_iseq_location_struct = type { i64, i64, i64, i64, i32, %struct.rb_code_location_struct } -%struct.rb_code_location_struct = type { %struct.rb_code_position_struct, %struct.rb_code_position_struct } -%struct.rb_code_position_struct = type { i32, i32 } -%struct.iseq_insn_info = type { %struct.iseq_insn_info_entry*, i32*, i32, %struct.succ_index_table* } -%struct.iseq_insn_info_entry = type opaque -%struct.succ_index_table = type opaque -%struct.iseq_catch_table = type opaque -%union.iseq_inline_storage_entry = type { %struct.iseq_inline_cache_entry } -%struct.iseq_inline_cache_entry = type { i64, %struct.rb_cref_struct*, i64 } -%struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } -%struct.rb_scope_visi_struct = type { i8, [3 x i8] } -%struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } -%struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } -%union.anon.13 = type { %struct.rb_method_cfunc_struct } -%struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%union.anon.15 = type { i32 } -%struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.16 = type { i64, i64, i64, i64* } -%union.anon.17 = type { %struct.anon.18 } -%struct.anon.18 = type { i64, i32 } -%struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } -%struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } -%struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } -%struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } -%struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } -%struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } -%struct.__sigset_t = type { [16 x i64] } -%struct.rb_calling_info = type { i64, i64, i32, i32 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } -%union.anon.9 = type { %union.pthread_cond_t } -%struct.rb_unblock_callback = type { void (i8*)*, i8* } -%struct.rb_mutex_struct = type opaque -%struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.10 = type { %struct.anon.11 } -%struct.anon.11 = type { i64, i64, i32 } -%struct.rb_fiber_struct = type opaque -%struct.anon.5 = type { [65 x i64] } -%struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } -%struct.rb_event_hook_struct = type opaque -%struct.rb_postponed_job_struct = type opaque -%struct.list_head = type { %struct.list_node } -%union.pthread_mutex_t = type { %struct.__pthread_mutex_s } -%struct.__pthread_mutex_s = type { i32, i32, i32, i32, i32, i16, i16, %struct.__pthread_internal_list } -%struct.__pthread_internal_list = type { %struct.__pthread_internal_list*, %struct.__pthread_internal_list* } -%struct.rb_objspace = type opaque -%struct.rb_at_exit_list = type { void (%struct.rb_vm_struct*)*, %struct.rb_at_exit_list* } -%struct.st_table = type { i8, i8, i8, i32, %struct.st_hash_type*, i64, i64*, i64, i64, %struct.st_table_entry* } -%struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } -%struct.st_table_entry = type opaque -%struct.rb_builtin_function = type opaque -%struct.anon.6 = type { i64, i64, i64, i64 } -%struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } -%struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } -%struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } -%struct.rb_call_info_with_kwarg = type { %struct.rb_call_info, %struct.rb_call_info_kw_arg* } -%struct.rb_call_info_kw_arg = type { i32, [1 x i64] } - -@ruby_current_vm_ptr = external local_unnamed_addr global %struct.rb_vm_struct*, align 8 -@ruby_current_execution_context_ptr = external local_unnamed_addr global %struct.rb_execution_context_struct*, align 8 -@rb_eRuntimeError = external local_unnamed_addr global i64, align 8 -@.str.9 = private unnamed_addr constant [95 x i8] c"sorbet_getBuildSCMRevision: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@.str.10 = private unnamed_addr constant [93 x i8] c"sorbet_getIsReleaseBuild: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@"stackFramePrecomputed_func_.13" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@iseqEncodedArray = internal global [15 x i64] zeroinitializer -@fileLineNumberInfo = internal global %struct.SorbetLineNumberInfo zeroinitializer -@ruby_hashLiteral1 = internal unnamed_addr global i64 0, align 8 -@ruby_hashLiteral2 = internal unnamed_addr global i64 0, align 8 -@ruby_hashLiteral3 = internal unnamed_addr global i64 0, align 8 -@ruby_hashLiteral4 = internal unnamed_addr global i64 0, align 8 -@ruby_hashLiteral5 = internal unnamed_addr global i64 0, align 8 -@ruby_hashLiteral6 = internal unnamed_addr global i64 0, align 8 -@ruby_hashLiteral7 = internal unnamed_addr global i64 0, align 8 -@ic_puts = internal global %struct.FunctionInlineCache zeroinitializer -@sorbet_moduleStringTable = internal unnamed_addr constant [140 x i8] c"\00test/testdata/compiler/literal_hash.rb\00foo\00bar\00baz\00quux\00wat\00how\00unknown\00do\00magic\00one\00two\00true\00false\00nil\00symbol\00puts\00master\00", align 1 -@sorbet_moduleIDTable = internal unnamed_addr global [4 x i64] zeroinitializer, align 8 -@sorbet_moduleIDDescriptors = internal unnamed_addr constant [4 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 16 }, %struct.rb_code_position_struct { i32 89, i32 2 }, %struct.rb_code_position_struct { i32 121, i32 6 }, %struct.rb_code_position_struct { i32 128, i32 4 }], align 8 -@sorbet_moduleRubyStringTable = internal unnamed_addr global [16 x i64] zeroinitializer, align 8 -@sorbet_moduleRubyStringDescriptors = internal unnamed_addr constant [16 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 16 }, %struct.rb_code_position_struct { i32 17, i32 38 }, %struct.rb_code_position_struct { i32 56, i32 3 }, %struct.rb_code_position_struct { i32 60, i32 3 }, %struct.rb_code_position_struct { i32 64, i32 3 }, %struct.rb_code_position_struct { i32 68, i32 4 }, %struct.rb_code_position_struct { i32 73, i32 3 }, %struct.rb_code_position_struct { i32 77, i32 3 }, %struct.rb_code_position_struct { i32 81, i32 7 }, %struct.rb_code_position_struct { i32 92, i32 5 }, %struct.rb_code_position_struct { i32 98, i32 3 }, %struct.rb_code_position_struct { i32 102, i32 3 }, %struct.rb_code_position_struct { i32 106, i32 4 }, %struct.rb_code_position_struct { i32 111, i32 5 }, %struct.rb_code_position_struct { i32 117, i32 3 }, %struct.rb_code_position_struct { i32 121, i32 6 }], align 8 - -; Function Attrs: nounwind readnone willreturn -declare i64 @rb_id2sym(i64) local_unnamed_addr #0 - -declare %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64, i64, i64, i64, %struct.rb_iseq_struct*, i32, i32, %struct.SorbetLineNumberInfo*, i64*, i32, i32) local_unnamed_addr #1 - -declare void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo*, i64*, i32) local_unnamed_addr #1 - -declare i64 @sorbet_readRealpath() local_unnamed_addr #1 - -declare void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) local_unnamed_addr #1 - -declare i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache*, i64) local_unnamed_addr #1 - -declare void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_iseq_struct*) local_unnamed_addr #1 - -declare i64 @sorbet_globalConstRegister(i64) local_unnamed_addr #1 - -declare i64 @sorbet_globalConstDupHash(i64) local_unnamed_addr #1 - -declare void @sorbet_vm_intern_ids(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #1 - -declare void @sorbet_vm_init_string_table(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #1 - -declare i64 @rb_hash_new_with_size(i64) local_unnamed_addr #1 - -declare void @rb_hash_bulk_insert(i64, i64*, i64) local_unnamed_addr #1 - -; Function Attrs: noreturn -declare void @rb_raise(i64, i8*, ...) local_unnamed_addr #2 - -; Function Attrs: nounwind ssp uwtable -define weak i32 @sorbet_getIsReleaseBuild() local_unnamed_addr #3 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([93 x i8], [93 x i8]* @.str.10, i64 0, i64 0)) #7 - unreachable -} - -; Function Attrs: nounwind ssp uwtable -define weak i8* @sorbet_getBuildSCMRevision() local_unnamed_addr #3 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([95 x i8], [95 x i8]* @.str.9, i64 0, i64 0)) #7 - unreachable -} - -define internal fastcc void @sorbet_globalConstructors(i64 %realpath) unnamed_addr { -allocRubyIds: - tail call void @sorbet_vm_intern_ids(i64* noundef getelementptr inbounds ([4 x i64], [4 x i64]* @sorbet_moduleIDTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([4 x %struct.rb_code_position_struct], [4 x %struct.rb_code_position_struct]* @sorbet_moduleIDDescriptors, i32 0, i32 0), i32 noundef 4, i8* noundef getelementptr inbounds ([140 x i8], [140 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_vm_init_string_table(i64* noundef getelementptr inbounds ([16 x i64], [16 x i64]* @sorbet_moduleRubyStringTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([16 x %struct.rb_code_position_struct], [16 x %struct.rb_code_position_struct]* @sorbet_moduleRubyStringDescriptors, i32 0, i32 0), i32 noundef 16, i8* noundef getelementptr inbounds ([140 x i8], [140 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef getelementptr inbounds ([15 x i64], [15 x i64]* @iseqEncodedArray, i32 0, i32 0), i32 noundef 15) - tail call fastcc void @"Constr_stackFramePrecomputed_func_.13"(i64 %realpath) - tail call fastcc void @Constr_ruby_hashLiteral1() #8 - tail call fastcc void @Constr_ruby_hashLiteral2() #8 - tail call fastcc void @Constr_ruby_hashLiteral3() #8 - tail call fastcc void @Constr_ruby_hashLiteral4() #8 - tail call fastcc void @Constr_ruby_hashLiteral5() #8 - tail call fastcc void @Constr_ruby_hashLiteral6() #8 - tail call fastcc void @Constr_ruby_hashLiteral7() #8 - %rubyId_puts = load i64, i64* getelementptr inbounds ([4 x i64], [4 x i64]* @sorbet_moduleIDTable, i64 0, i64 3), align 8, !dbg !10, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts, i64 %rubyId_puts, i32 noundef 20, i32 noundef 7, i32 noundef 0), !dbg !10 - ret void -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_.13"(i64 %realpath) unnamed_addr #4 { -entryInitializers: - %"rubyId_" = load i64, i64* getelementptr inbounds ([4 x i64], [4 x i64]* @sorbet_moduleIDTable, i64 0, i64 0), align 8, !invariant.load !5 - %"rubyStr_" = load i64, i64* getelementptr inbounds ([16 x i64], [16 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 0), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/literal_hash.rb" = load i64, i64* getelementptr inbounds ([16 x i64], [16 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_", i64 %"rubyId_", i64 %"rubyStr_test/testdata/compiler/literal_hash.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 0, i32 noundef 6, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 15) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - ret void -} - -; Function Attrs: nounwind ssp -define internal fastcc void @Constr_ruby_hashLiteral1() unnamed_addr #5 { -constrHashLiteral: - %argArray = alloca [14 x i64], align 8 - %rubyStr_foo = load i64, i64* getelementptr inbounds ([16 x i64], [16 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 2), align 8, !invariant.load !5 - %hashArgs0Addr = getelementptr [14 x i64], [14 x i64]* %argArray, i64 0, i64 0 - store i64 %rubyStr_foo, i64* %hashArgs0Addr, align 8 - %hashArgs1Addr = getelementptr [14 x i64], [14 x i64]* %argArray, i64 0, i64 1 - store i64 3, i64* %hashArgs1Addr, align 8 - %rubyStr_bar = load i64, i64* getelementptr inbounds ([16 x i64], [16 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 3), align 8, !invariant.load !5 - %hashArgs2Addr = getelementptr [14 x i64], [14 x i64]* %argArray, i64 0, i64 2 - store i64 %rubyStr_bar, i64* %hashArgs2Addr, align 8 - %hashArgs3Addr = getelementptr [14 x i64], [14 x i64]* %argArray, i64 0, i64 3 - store i64 2, i64* %hashArgs3Addr, align 8 - %rubyStr_baz = load i64, i64* getelementptr inbounds ([16 x i64], [16 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 4), align 8, !invariant.load !5 - %hashArgs4Addr = getelementptr [14 x i64], [14 x i64]* %argArray, i64 0, i64 4 - store i64 %rubyStr_baz, i64* %hashArgs4Addr, align 8 - %hashArgs5Addr = getelementptr [14 x i64], [14 x i64]* %argArray, i64 0, i64 5 - store i64 20, i64* %hashArgs5Addr, align 8 - %rubyStr_quux = load i64, i64* getelementptr inbounds ([16 x i64], [16 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 5), align 8, !invariant.load !5 - %hashArgs6Addr = getelementptr [14 x i64], [14 x i64]* %argArray, i64 0, i64 6 - store i64 %rubyStr_quux, i64* %hashArgs6Addr, align 8 - %hashArgs7Addr = getelementptr [14 x i64], [14 x i64]* %argArray, i64 0, i64 7 - store i64 0, i64* %hashArgs7Addr, align 8 - %rubyStr_wat = load i64, i64* getelementptr inbounds ([16 x i64], [16 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 6), align 8, !invariant.load !5 - %hashArgs8Addr = getelementptr [14 x i64], [14 x i64]* %argArray, i64 0, i64 8 - store i64 %rubyStr_wat, i64* %hashArgs8Addr, align 8 - %hashArgs9Addr = getelementptr [14 x i64], [14 x i64]* %argArray, i64 0, i64 9 - store i64 8, i64* %hashArgs9Addr, align 8 - %hashArgs10Addr = getelementptr [14 x i64], [14 x i64]* %argArray, i64 0, i64 10 - %0 = load <2 x i64>, <2 x i64>* bitcast (i64* getelementptr inbounds ([16 x i64], [16 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 7) to <2 x i64>*), align 8, !invariant.load !5 - %1 = bitcast i64* %hashArgs10Addr to <2 x i64>* - store <2 x i64> %0, <2 x i64>* %1, align 8 - %rubyId_do = load i64, i64* getelementptr inbounds ([4 x i64], [4 x i64]* @sorbet_moduleIDTable, i64 0, i64 1), align 8, !invariant.load !5 - %rawSym = tail call i64 @rb_id2sym(i64 %rubyId_do) #9 - %hashArgs12Addr = getelementptr [14 x i64], [14 x i64]* %argArray, i64 0, i64 12 - store i64 %rawSym, i64* %hashArgs12Addr, align 8 - %rubyStr_magic = load i64, i64* getelementptr inbounds ([16 x i64], [16 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 9), align 8, !invariant.load !5 - %hashArgs13Addr = getelementptr [14 x i64], [14 x i64]* %argArray, i64 0, i64 13 - store i64 %rubyStr_magic, i64* %hashArgs13Addr, align 8 - %2 = tail call i64 @rb_hash_new_with_size(i64 noundef 7) #8 - call void @rb_hash_bulk_insert(i64 noundef 14, i64* noundef nonnull %hashArgs0Addr, i64 %2) #8 - %3 = call i64 @sorbet_globalConstRegister(i64 %2) #8 - store i64 %3, i64* @ruby_hashLiteral1, align 8 - ret void -} - -; Function Attrs: nounwind ssp -define internal fastcc void @Constr_ruby_hashLiteral2() unnamed_addr #5 { -constrHashLiteral: - %argArray = alloca [2 x i64], align 8 - %hashArgs0Addr = getelementptr [2 x i64], [2 x i64]* %argArray, i64 0, i64 0 - store i64 3, i64* %hashArgs0Addr, align 8 - %rubyStr_one = load i64, i64* getelementptr inbounds ([16 x i64], [16 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 10), align 8, !invariant.load !5 - %hashArgs1Addr = getelementptr [2 x i64], [2 x i64]* %argArray, i64 0, i64 1 - store i64 %rubyStr_one, i64* %hashArgs1Addr, align 8 - %0 = tail call i64 @rb_hash_new_with_size(i64 noundef 1) #8 - call void @rb_hash_bulk_insert(i64 noundef 2, i64* noundef nonnull %hashArgs0Addr, i64 %0) #8 - %1 = call i64 @sorbet_globalConstRegister(i64 %0) #8 - store i64 %1, i64* @ruby_hashLiteral2, align 8 - ret void -} - -; Function Attrs: nounwind ssp -define internal fastcc void @Constr_ruby_hashLiteral3() unnamed_addr #5 { -constrHashLiteral: - %argArray = alloca [2 x i64], align 8 - %hashArgs0Addr = getelementptr [2 x i64], [2 x i64]* %argArray, i64 0, i64 0 - store i64 2, i64* %hashArgs0Addr, align 8 - %rubyStr_two = load i64, i64* getelementptr inbounds ([16 x i64], [16 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 11), align 8, !invariant.load !5 - %hashArgs1Addr = getelementptr [2 x i64], [2 x i64]* %argArray, i64 0, i64 1 - store i64 %rubyStr_two, i64* %hashArgs1Addr, align 8 - %0 = tail call i64 @rb_hash_new_with_size(i64 noundef 1) #8 - call void @rb_hash_bulk_insert(i64 noundef 2, i64* noundef nonnull %hashArgs0Addr, i64 %0) #8 - %1 = call i64 @sorbet_globalConstRegister(i64 %0) #8 - store i64 %1, i64* @ruby_hashLiteral3, align 8 - ret void -} - -; Function Attrs: nounwind ssp -define internal fastcc void @Constr_ruby_hashLiteral4() unnamed_addr #5 { -constrHashLiteral: - %argArray = alloca [2 x i64], align 8 - %hashArgs0Addr = getelementptr [2 x i64], [2 x i64]* %argArray, i64 0, i64 0 - store i64 20, i64* %hashArgs0Addr, align 8 - %rubyStr_true = load i64, i64* getelementptr inbounds ([16 x i64], [16 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 12), align 8, !invariant.load !5 - %hashArgs1Addr = getelementptr [2 x i64], [2 x i64]* %argArray, i64 0, i64 1 - store i64 %rubyStr_true, i64* %hashArgs1Addr, align 8 - %0 = tail call i64 @rb_hash_new_with_size(i64 noundef 1) #8 - call void @rb_hash_bulk_insert(i64 noundef 2, i64* noundef nonnull %hashArgs0Addr, i64 %0) #8 - %1 = call i64 @sorbet_globalConstRegister(i64 %0) #8 - store i64 %1, i64* @ruby_hashLiteral4, align 8 - ret void -} - -; Function Attrs: nounwind ssp -define internal fastcc void @Constr_ruby_hashLiteral5() unnamed_addr #5 { -constrHashLiteral: - %argArray = alloca [2 x i64], align 8 - %hashArgs0Addr = getelementptr [2 x i64], [2 x i64]* %argArray, i64 0, i64 0 - store i64 0, i64* %hashArgs0Addr, align 8 - %rubyStr_false = load i64, i64* getelementptr inbounds ([16 x i64], [16 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 13), align 8, !invariant.load !5 - %hashArgs1Addr = getelementptr [2 x i64], [2 x i64]* %argArray, i64 0, i64 1 - store i64 %rubyStr_false, i64* %hashArgs1Addr, align 8 - %0 = tail call i64 @rb_hash_new_with_size(i64 noundef 1) #8 - call void @rb_hash_bulk_insert(i64 noundef 2, i64* noundef nonnull %hashArgs0Addr, i64 %0) #8 - %1 = call i64 @sorbet_globalConstRegister(i64 %0) #8 - store i64 %1, i64* @ruby_hashLiteral5, align 8 - ret void -} - -; Function Attrs: nounwind ssp -define internal fastcc void @Constr_ruby_hashLiteral6() unnamed_addr #5 { -constrHashLiteral: - %argArray = alloca [2 x i64], align 8 - %hashArgs0Addr = getelementptr [2 x i64], [2 x i64]* %argArray, i64 0, i64 0 - store i64 8, i64* %hashArgs0Addr, align 8 - %rubyStr_nil = load i64, i64* getelementptr inbounds ([16 x i64], [16 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 14), align 8, !invariant.load !5 - %hashArgs1Addr = getelementptr [2 x i64], [2 x i64]* %argArray, i64 0, i64 1 - store i64 %rubyStr_nil, i64* %hashArgs1Addr, align 8 - %0 = tail call i64 @rb_hash_new_with_size(i64 noundef 1) #8 - call void @rb_hash_bulk_insert(i64 noundef 2, i64* noundef nonnull %hashArgs0Addr, i64 %0) #8 - %1 = call i64 @sorbet_globalConstRegister(i64 %0) #8 - store i64 %1, i64* @ruby_hashLiteral6, align 8 - ret void -} - -; Function Attrs: nounwind ssp -define internal fastcc void @Constr_ruby_hashLiteral7() unnamed_addr #5 { -constrHashLiteral: - %argArray = alloca [2 x i64], align 8 - %rubyId_symbol = load i64, i64* getelementptr inbounds ([4 x i64], [4 x i64]* @sorbet_moduleIDTable, i64 0, i64 2), align 8, !invariant.load !5 - %rawSym = tail call i64 @rb_id2sym(i64 %rubyId_symbol) #9 - %hashArgs0Addr = getelementptr [2 x i64], [2 x i64]* %argArray, i64 0, i64 0 - store i64 %rawSym, i64* %hashArgs0Addr, align 8 - %rubyStr_symbol = load i64, i64* getelementptr inbounds ([16 x i64], [16 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 15), align 8, !invariant.load !5 - %hashArgs1Addr = getelementptr [2 x i64], [2 x i64]* %argArray, i64 0, i64 1 - store i64 %rubyStr_symbol, i64* %hashArgs1Addr, align 8 - %0 = tail call i64 @rb_hash_new_with_size(i64 noundef 1) #8 - call void @rb_hash_bulk_insert(i64 noundef 2, i64* noundef nonnull %hashArgs0Addr, i64 %0) #8 - %1 = call i64 @sorbet_globalConstRegister(i64 %0) #8 - store i64 %1, i64* @ruby_hashLiteral7, align 8 - ret void -} - -; Function Attrs: sspreq -define void @Init_literal_hash() local_unnamed_addr #6 { -entry: - %realpath = tail call i64 @sorbet_readRealpath() - tail call fastcc void @sorbet_globalConstructors(i64 %realpath) - %0 = load %struct.rb_vm_struct*, %struct.rb_vm_struct** @ruby_current_vm_ptr, align 8, !tbaa !15 - %1 = getelementptr inbounds %struct.rb_vm_struct, %struct.rb_vm_struct* %0, i64 0, i32 18 - %2 = load i64, i64* %1, align 8, !tbaa !17 - %3 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !15 - %4 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %3, i64 0, i32 2 - %5 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %4, align 8, !tbaa !27 - %stackFrame.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - %6 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame.i, %struct.rb_iseq_struct** %6, align 8, !tbaa !30 - %7 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 4 - %8 = load i64*, i64** %7, align 8, !tbaa !32 - %9 = load i64, i64* %8, align 8, !tbaa !6 - %10 = and i64 %9, -33 - store i64 %10, i64* %8, align 8, !tbaa !6 - tail call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %3, %struct.rb_control_frame_struct* %5, %struct.rb_iseq_struct* %stackFrame.i) #8 - %11 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 0 - store i64* getelementptr inbounds ([15 x i64], [15 x i64]* @iseqEncodedArray, i64 0, i64 6), i64** %11, align 8, !dbg !33, !tbaa !15 - %hashLiteral.i = load i64, i64* @ruby_hashLiteral1, align 8, !dbg !34 - %duplicatedHash.i = tail call i64 @sorbet_globalConstDupHash(i64 %hashLiteral.i) #8, !dbg !34 - store i64* getelementptr inbounds ([15 x i64], [15 x i64]* @iseqEncodedArray, i64 0, i64 8), i64** %11, align 8, !dbg !34, !tbaa !15 - %hashLiteral80.i = load i64, i64* @ruby_hashLiteral2, align 8, !dbg !35 - %duplicatedHash81.i = tail call i64 @sorbet_globalConstDupHash(i64 %hashLiteral80.i) #8, !dbg !35 - store i64* getelementptr inbounds ([15 x i64], [15 x i64]* @iseqEncodedArray, i64 0, i64 9), i64** %11, align 8, !dbg !35, !tbaa !15 - %hashLiteral87.i = load i64, i64* @ruby_hashLiteral3, align 8, !dbg !36 - %duplicatedHash88.i = tail call i64 @sorbet_globalConstDupHash(i64 %hashLiteral87.i) #8, !dbg !36 - store i64* getelementptr inbounds ([15 x i64], [15 x i64]* @iseqEncodedArray, i64 0, i64 10), i64** %11, align 8, !dbg !36, !tbaa !15 - %hashLiteral94.i = load i64, i64* @ruby_hashLiteral4, align 8, !dbg !37 - %duplicatedHash95.i = tail call i64 @sorbet_globalConstDupHash(i64 %hashLiteral94.i) #8, !dbg !37 - store i64* getelementptr inbounds ([15 x i64], [15 x i64]* @iseqEncodedArray, i64 0, i64 11), i64** %11, align 8, !dbg !37, !tbaa !15 - %hashLiteral101.i = load i64, i64* @ruby_hashLiteral5, align 8, !dbg !38 - %duplicatedHash102.i = tail call i64 @sorbet_globalConstDupHash(i64 %hashLiteral101.i) #8, !dbg !38 - store i64* getelementptr inbounds ([15 x i64], [15 x i64]* @iseqEncodedArray, i64 0, i64 12), i64** %11, align 8, !dbg !38, !tbaa !15 - %hashLiteral108.i = load i64, i64* @ruby_hashLiteral6, align 8, !dbg !39 - %duplicatedHash109.i = tail call i64 @sorbet_globalConstDupHash(i64 %hashLiteral108.i) #8, !dbg !39 - store i64* getelementptr inbounds ([15 x i64], [15 x i64]* @iseqEncodedArray, i64 0, i64 13), i64** %11, align 8, !dbg !39, !tbaa !15 - %hashLiteral115.i = load i64, i64* @ruby_hashLiteral7, align 8, !dbg !40 - %duplicatedHash116.i = tail call i64 @sorbet_globalConstDupHash(i64 %hashLiteral115.i) #8, !dbg !40 - store i64* getelementptr inbounds ([15 x i64], [15 x i64]* @iseqEncodedArray, i64 0, i64 14), i64** %11, align 8, !dbg !40, !tbaa !15 - %12 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !10 - %13 = load i64*, i64** %12, align 8, !dbg !10 - store i64 %2, i64* %13, align 8, !dbg !10, !tbaa !6 - %14 = getelementptr inbounds i64, i64* %13, i64 1, !dbg !10 - store i64 %duplicatedHash.i, i64* %14, align 8, !dbg !10, !tbaa !6 - %15 = getelementptr inbounds i64, i64* %14, i64 1, !dbg !10 - store i64 %duplicatedHash81.i, i64* %15, align 8, !dbg !10, !tbaa !6 - %16 = getelementptr inbounds i64, i64* %15, i64 1, !dbg !10 - store i64 %duplicatedHash88.i, i64* %16, align 8, !dbg !10, !tbaa !6 - %17 = getelementptr inbounds i64, i64* %16, i64 1, !dbg !10 - store i64 %duplicatedHash95.i, i64* %17, align 8, !dbg !10, !tbaa !6 - %18 = getelementptr inbounds i64, i64* %17, i64 1, !dbg !10 - store i64 %duplicatedHash102.i, i64* %18, align 8, !dbg !10, !tbaa !6 - %19 = getelementptr inbounds i64, i64* %18, i64 1, !dbg !10 - store i64 %duplicatedHash109.i, i64* %19, align 8, !dbg !10, !tbaa !6 - %20 = getelementptr inbounds i64, i64* %19, i64 1, !dbg !10 - store i64 %duplicatedHash116.i, i64* %20, align 8, !dbg !10, !tbaa !6 - %21 = getelementptr inbounds i64, i64* %20, i64 1, !dbg !10 - store i64* %21, i64** %12, align 8, !dbg !10 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts, i64 0), !dbg !10 - ret void -} - -attributes #0 = { nounwind readnone willreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #1 = { "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #2 = { noreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #3 = { nounwind ssp uwtable "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #4 = { ssp } -attributes #5 = { nounwind ssp } -attributes #6 = { sspreq } -attributes #7 = { noreturn nounwind } -attributes #8 = { nounwind } -attributes #9 = { willreturn } - -!llvm.module.flags = !{!0, !1, !2} -!llvm.dbg.cu = !{!3} - -!0 = !{i32 2, !"Debug Info Version", i32 3} -!1 = !{i32 4, !"cf-protection-return", i32 1} -!2 = !{i32 4, !"cf-protection-branch", i32 1} -!3 = distinct !DICompileUnit(language: DW_LANG_C, file: !4, producer: "Sorbet LLVM", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !5) -!4 = !DIFile(filename: "test/testdata/compiler/literal_hash.rb", directory: ".") -!5 = !{} -!6 = !{!7, !7, i64 0} -!7 = !{!"long", !8, i64 0} -!8 = !{!"omnipotent char", !9, i64 0} -!9 = !{!"Simple C/C++ TBAA"} -!10 = !DILocation(line: 14, column: 1, scope: !11) -!11 = distinct !DISubprogram(name: ".", linkageName: "func_.13", scope: null, file: !4, line: 6, type: !12, scopeLine: 6, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!12 = !DISubroutineType(types: !13) -!13 = !{!14} -!14 = !DIBasicType(name: "VALUE", size: 64, encoding: DW_ATE_signed) -!15 = !{!16, !16, i64 0} -!16 = !{!"any pointer", !8, i64 0} -!17 = !{!18, !7, i64 400} -!18 = !{!"rb_vm_struct", !7, i64 0, !19, i64 8, !16, i64 192, !16, i64 200, !16, i64 208, !23, i64 216, !8, i64 224, !20, i64 264, !20, i64 280, !20, i64 296, !20, i64 312, !7, i64 328, !22, i64 336, !22, i64 340, !22, i64 344, !22, i64 344, !22, i64 344, !22, i64 344, !22, i64 348, !7, i64 352, !8, i64 360, !7, i64 400, !7, i64 408, !7, i64 416, !7, i64 424, !7, i64 432, !7, i64 440, !7, i64 448, !16, i64 456, !16, i64 464, !24, i64 472, !25, i64 992, !16, i64 1016, !16, i64 1024, !22, i64 1032, !22, i64 1036, !20, i64 1040, !8, i64 1056, !7, i64 1096, !7, i64 1104, !7, i64 1112, !7, i64 1120, !7, i64 1128, !22, i64 1136, !16, i64 1144, !16, i64 1152, !16, i64 1160, !16, i64 1168, !16, i64 1176, !16, i64 1184, !22, i64 1192, !26, i64 1200, !8, i64 1232} -!19 = !{!"rb_global_vm_lock_struct", !16, i64 0, !8, i64 8, !20, i64 48, !16, i64 64, !22, i64 72, !8, i64 80, !8, i64 128, !22, i64 176, !22, i64 180} -!20 = !{!"list_head", !21, i64 0} -!21 = !{!"list_node", !16, i64 0, !16, i64 8} -!22 = !{!"int", !8, i64 0} -!23 = !{!"long long", !8, i64 0} -!24 = !{!"", !8, i64 0} -!25 = !{!"rb_hook_list_struct", !16, i64 0, !22, i64 8, !22, i64 12, !22, i64 16} -!26 = !{!"", !7, i64 0, !7, i64 8, !7, i64 16, !7, i64 24} -!27 = !{!28, !16, i64 16} -!28 = !{!"rb_execution_context_struct", !16, i64 0, !7, i64 8, !16, i64 16, !16, i64 24, !16, i64 32, !22, i64 40, !22, i64 44, !16, i64 48, !16, i64 56, !16, i64 64, !7, i64 72, !7, i64 80, !16, i64 88, !7, i64 96, !16, i64 104, !16, i64 112, !7, i64 120, !7, i64 128, !8, i64 136, !8, i64 137, !7, i64 144, !29, i64 152} -!29 = !{!"", !16, i64 0, !16, i64 8, !7, i64 16, !8, i64 24} -!30 = !{!31, !16, i64 16} -!31 = !{!"rb_control_frame_struct", !16, i64 0, !16, i64 8, !16, i64 16, !7, i64 24, !16, i64 32, !16, i64 40, !16, i64 48} -!32 = !{!31, !16, i64 32} -!33 = !DILocation(line: 0, scope: !11) -!34 = !DILocation(line: 6, column: 5, scope: !11) -!35 = !DILocation(line: 8, column: 5, scope: !11) -!36 = !DILocation(line: 9, column: 5, scope: !11) -!37 = !DILocation(line: 10, column: 5, scope: !11) -!38 = !DILocation(line: 11, column: 5, scope: !11) -!39 = !DILocation(line: 12, column: 5, scope: !11) -!40 = !DILocation(line: 13, column: 5, scope: !11) diff --git a/test/testdata/compiler/literal_type_tests.rb b/test/testdata/compiler/literal_type_tests.rb index ac6afd5e6e..523f7df673 100644 --- a/test/testdata/compiler/literal_type_tests.rb +++ b/test/testdata/compiler/literal_type_tests.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL def literal_symbol(obj) case obj @@ -28,35 +27,3 @@ def literal_double(obj) def literal_integer(obj) 15 < obj end - -# INITIAL-LABEL: define internal i64 @"func_Object#14literal_symbol" -# INITIAL: [[ID_ADDR:%[_a-zA-Z0-9]+]] = load i64*, i64** @addr_id_matching{{.*}} -# INITIAL-NEXT: [[ID:%[_a-zA-Z0-9]+]] = load i64, i64* [[ID_ADDR]]{{.*}} -# INITIAL-NEXT: [[SYM:%[_a-zA-Z]+]] = call i64 @rb_id2sym(i64 [[ID]]{{.*}} -# INITIAL-NEXT: [[COND:%[0-9]+]] = call i1 @sorbet_i_isa_Symbol(i64 [[SYM]]{{.*}} -# INITIAL-NEXT: call void @llvm.assume(i1 [[COND]]){{.*}} -# INITIAL-NEXT: br i1 true, label %"fastSymCallIntrinsic_Symbol_===", label %"alternativeCallIntrinsic_Symbol_==="{{.*}} -# INITIAL{LITERAL}: } - -# INITIAL-LABEL: define internal i64 @"func_Object#14literal_string" -# INITIAL: [[STRING_TMP:%[_a-zA-Z0-9]+]] = load i64*, i64** @addr_rubystr_matching{{.*}} -# INITIAL-NEXT: [[STRING:%[_a-zA-Z]+]] = load i64, i64* [[STRING_TMP]]{{.*}} -# INITIAL-NEXT: [[COND:%[0-9]+]] = call i1 @sorbet_i_isa_String(i64 [[STRING]]{{.*}} -# INITIAL-NEXT: call void @llvm.assume(i1 [[COND]]){{.*}} -# INITIAL-NEXT: br i1 true, label %"fastSymCallIntrinsic_String_===", label %"alternativeCallIntrinsic_String_==="{{.*}} -# INITIAL{LITERAL}: } - -# INITIAL-LABEL: define internal i64 @"func_Object#14literal_double" -# INITIAL: [[DOUBLE:%[a-zA-Z]+]] = call i64 @sorbet_doubleToRubyValue({{.*}} -# INITIAL-NEXT: [[COND:%[0-9]+]] = call i1 @sorbet_i_isa_Float(i64 [[DOUBLE]]{{.*}} -# INITIAL-NEXT: call void @llvm.assume(i1 [[COND]]){{.*}} -# INITIAL-NEXT: br i1 true, label %"fastSymCallIntrinsic_Float_<=", label %"alternativeCallIntrinsic_Float_<="{{.*}} -# INITIAL{LITERAL}: } - -# INITIAL-LABEL: define internal i64 @"func_Object#15literal_integer" -# INITIAL: [[INTEGER:%[a-zA-Z]+]] = call i64 @sorbet_longToRubyValue({{.*}} -# INITIAL-NEXT: [[COND:%[0-9]+]] = call i1 @sorbet_i_isa_Integer(i64 [[INTEGER]]{{.*}} -# INITIAL-NEXT: call void @llvm.assume(i1 [[COND]]){{.*}} -# INITIAL-NEXT: br i1 true, label %"fastSymCallIntrinsic_Integer_<", label %"alternativeCallIntrinsic_Integer_<"{{.*}} -# INITIAL{LITERAL}: } - diff --git a/test/testdata/compiler/literals.opt.ll.exp b/test/testdata/compiler/literals.opt.ll.exp deleted file mode 100644 index 58191cdbe3..0000000000 --- a/test/testdata/compiler/literals.opt.ll.exp +++ /dev/null @@ -1,308 +0,0 @@ -; ModuleID = 'payload' -source_filename = "llvm-link" -target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } -%struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.__pthread_cond_s } -%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } -%union.anon = type { i64 } -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } -%struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } -%struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } -%struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } -%struct.anon.0 = type { i16, [2 x i8] } -%struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } -%struct.rb_iseq_location_struct = type { i64, i64, i64, i64, i32, %struct.rb_code_location_struct } -%struct.rb_code_location_struct = type { %struct.rb_code_position_struct, %struct.rb_code_position_struct } -%struct.rb_code_position_struct = type { i32, i32 } -%struct.iseq_insn_info = type { %struct.iseq_insn_info_entry*, i32*, i32, %struct.succ_index_table* } -%struct.iseq_insn_info_entry = type opaque -%struct.succ_index_table = type opaque -%struct.iseq_catch_table = type opaque -%union.iseq_inline_storage_entry = type { %struct.iseq_inline_cache_entry } -%struct.iseq_inline_cache_entry = type { i64, %struct.rb_cref_struct*, i64 } -%struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } -%struct.rb_scope_visi_struct = type { i8, [3 x i8] } -%struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } -%struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } -%union.anon.13 = type { %struct.rb_method_cfunc_struct } -%struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%union.anon.15 = type { i32 } -%struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.16 = type { i64, i64, i64, i64* } -%union.anon.17 = type { %struct.anon.18 } -%struct.anon.18 = type { i64, i32 } -%struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } -%struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } -%struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } -%struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } -%struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } -%struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } -%struct.__sigset_t = type { [16 x i64] } -%struct.rb_calling_info = type { i64, i64, i32, i32 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } -%union.anon.9 = type { %union.pthread_cond_t } -%struct.rb_unblock_callback = type { void (i8*)*, i8* } -%struct.rb_mutex_struct = type opaque -%struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.10 = type { %struct.anon.11 } -%struct.anon.11 = type { i64, i64, i32 } -%struct.rb_fiber_struct = type opaque -%struct.anon.5 = type { [65 x i64] } -%struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } -%struct.rb_event_hook_struct = type opaque -%struct.rb_postponed_job_struct = type opaque -%struct.list_head = type { %struct.list_node } -%union.pthread_mutex_t = type { %struct.__pthread_mutex_s } -%struct.__pthread_mutex_s = type { i32, i32, i32, i32, i32, i16, i16, %struct.__pthread_internal_list } -%struct.__pthread_internal_list = type { %struct.__pthread_internal_list*, %struct.__pthread_internal_list* } -%struct.rb_objspace = type opaque -%struct.rb_at_exit_list = type { void (%struct.rb_vm_struct*)*, %struct.rb_at_exit_list* } -%struct.st_table = type { i8, i8, i8, i32, %struct.st_hash_type*, i64, i64*, i64, i64, %struct.st_table_entry* } -%struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } -%struct.st_table_entry = type opaque -%struct.rb_builtin_function = type opaque -%struct.anon.6 = type { i64, i64, i64, i64 } -%struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } -%struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } -%struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } -%struct.rb_call_info_with_kwarg = type { %struct.rb_call_info, %struct.rb_call_info_kw_arg* } -%struct.rb_call_info_kw_arg = type { i32, [1 x i64] } - -@ruby_current_vm_ptr = external local_unnamed_addr global %struct.rb_vm_struct*, align 8 -@ruby_current_execution_context_ptr = external local_unnamed_addr global %struct.rb_execution_context_struct*, align 8 -@rb_eRuntimeError = external local_unnamed_addr global i64, align 8 -@.str.9 = private unnamed_addr constant [95 x i8] c"sorbet_getBuildSCMRevision: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@.str.10 = private unnamed_addr constant [93 x i8] c"sorbet_getIsReleaseBuild: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@"stackFramePrecomputed_func_.13" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@iseqEncodedArray = internal global [11 x i64] zeroinitializer -@fileLineNumberInfo = internal global %struct.SorbetLineNumberInfo zeroinitializer -@ic_puts = internal global %struct.FunctionInlineCache zeroinitializer -@ic_puts.1 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_puts.2 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_puts.3 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_puts.4 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_puts.5 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_puts.6 = internal global %struct.FunctionInlineCache zeroinitializer -@sorbet_moduleStringTable = internal unnamed_addr constant [72 x i8] c"\00test/testdata/compiler/literals.rb\00puts\00str\00sym\00master\00", align 1 -@sorbet_moduleIDTable = internal unnamed_addr global [3 x i64] zeroinitializer, align 8 -@sorbet_moduleIDDescriptors = internal unnamed_addr constant [3 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 16 }, %struct.rb_code_position_struct { i32 52, i32 4 }, %struct.rb_code_position_struct { i32 61, i32 3 }], align 8 -@sorbet_moduleRubyStringTable = internal unnamed_addr global [3 x i64] zeroinitializer, align 8 -@sorbet_moduleRubyStringDescriptors = internal unnamed_addr constant [3 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 16 }, %struct.rb_code_position_struct { i32 17, i32 34 }, %struct.rb_code_position_struct { i32 57, i32 3 }], align 8 - -; Function Attrs: nounwind readnone willreturn -declare i64 @rb_id2sym(i64) local_unnamed_addr #0 - -declare %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64, i64, i64, i64, %struct.rb_iseq_struct*, i32, i32, %struct.SorbetLineNumberInfo*, i64*, i32, i32) local_unnamed_addr #1 - -declare void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo*, i64*, i32) local_unnamed_addr #1 - -declare i64 @sorbet_readRealpath() local_unnamed_addr #1 - -declare void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) local_unnamed_addr #1 - -declare i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache*, i64) local_unnamed_addr #1 - -declare void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_iseq_struct*) local_unnamed_addr #1 - -declare void @sorbet_vm_intern_ids(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #1 - -declare void @sorbet_vm_init_string_table(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #1 - -; Function Attrs: noreturn -declare void @rb_raise(i64, i8*, ...) local_unnamed_addr #2 - -; Function Attrs: nounwind ssp uwtable -define weak i32 @sorbet_getIsReleaseBuild() local_unnamed_addr #3 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([93 x i8], [93 x i8]* @.str.10, i64 0, i64 0)) #6 - unreachable -} - -; Function Attrs: nounwind ssp uwtable -define weak i8* @sorbet_getBuildSCMRevision() local_unnamed_addr #3 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([95 x i8], [95 x i8]* @.str.9, i64 0, i64 0)) #6 - unreachable -} - -define internal fastcc void @sorbet_globalConstructors(i64 %realpath) unnamed_addr { -allocRubyIds: - tail call void @sorbet_vm_intern_ids(i64* noundef getelementptr inbounds ([3 x i64], [3 x i64]* @sorbet_moduleIDTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([3 x %struct.rb_code_position_struct], [3 x %struct.rb_code_position_struct]* @sorbet_moduleIDDescriptors, i32 0, i32 0), i32 noundef 3, i8* noundef getelementptr inbounds ([72 x i8], [72 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_vm_init_string_table(i64* noundef getelementptr inbounds ([3 x i64], [3 x i64]* @sorbet_moduleRubyStringTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([3 x %struct.rb_code_position_struct], [3 x %struct.rb_code_position_struct]* @sorbet_moduleRubyStringDescriptors, i32 0, i32 0), i32 noundef 3, i8* noundef getelementptr inbounds ([72 x i8], [72 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef getelementptr inbounds ([11 x i64], [11 x i64]* @iseqEncodedArray, i32 0, i32 0), i32 noundef 11) - tail call fastcc void @"Constr_stackFramePrecomputed_func_.13"(i64 %realpath) - %rubyId_puts = load i64, i64* getelementptr inbounds ([3 x i64], [3 x i64]* @sorbet_moduleIDTable, i64 0, i64 1), align 8, !dbg !10, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts, i64 %rubyId_puts, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !10 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts.1, i64 %rubyId_puts, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !15 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts.2, i64 %rubyId_puts, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !16 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts.3, i64 %rubyId_puts, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !17 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts.4, i64 %rubyId_puts, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !18 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts.5, i64 %rubyId_puts, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !19 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts.6, i64 %rubyId_puts, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !20 - ret void -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_.13"(i64 %realpath) unnamed_addr #4 { -entryInitializers: - %"rubyId_" = load i64, i64* getelementptr inbounds ([3 x i64], [3 x i64]* @sorbet_moduleIDTable, i64 0, i64 0), align 8, !invariant.load !5 - %"rubyStr_" = load i64, i64* getelementptr inbounds ([3 x i64], [3 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 0), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/literals.rb" = load i64, i64* getelementptr inbounds ([3 x i64], [3 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_", i64 %"rubyId_", i64 %"rubyStr_test/testdata/compiler/literals.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 0, i32 noundef 4, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 2) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - ret void -} - -; Function Attrs: sspreq -define void @Init_literals() local_unnamed_addr #5 { -entry: - %realpath = tail call i64 @sorbet_readRealpath() - tail call fastcc void @sorbet_globalConstructors(i64 %realpath) - %0 = load %struct.rb_vm_struct*, %struct.rb_vm_struct** @ruby_current_vm_ptr, align 8, !tbaa !21 - %1 = getelementptr inbounds %struct.rb_vm_struct, %struct.rb_vm_struct* %0, i64 0, i32 18 - %2 = load i64, i64* %1, align 8, !tbaa !23 - %3 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !21 - %4 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %3, i64 0, i32 2 - %5 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %4, align 8, !tbaa !33 - %stackFrame.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - %6 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame.i, %struct.rb_iseq_struct** %6, align 8, !tbaa !36 - %7 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 4 - %8 = load i64*, i64** %7, align 8, !tbaa !38 - %9 = load i64, i64* %8, align 8, !tbaa !6 - %10 = and i64 %9, -33 - store i64 %10, i64* %8, align 8, !tbaa !6 - tail call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %3, %struct.rb_control_frame_struct* %5, %struct.rb_iseq_struct* %stackFrame.i) #7 - %11 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 0 - store i64* getelementptr inbounds ([11 x i64], [11 x i64]* @iseqEncodedArray, i64 0, i64 4), i64** %11, align 8, !dbg !39, !tbaa !21 - %12 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !10 - %13 = load i64*, i64** %12, align 8, !dbg !10 - store i64 %2, i64* %13, align 8, !dbg !10, !tbaa !6 - %14 = getelementptr inbounds i64, i64* %13, i64 1, !dbg !10 - store i64 85, i64* %14, align 8, !dbg !10, !tbaa !6 - %15 = getelementptr inbounds i64, i64* %14, i64 1, !dbg !10 - store i64* %15, i64** %12, align 8, !dbg !10 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts, i64 0), !dbg !10 - store i64* getelementptr inbounds ([11 x i64], [11 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %11, align 8, !dbg !10, !tbaa !21 - %16 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !15 - %17 = load i64*, i64** %16, align 8, !dbg !15 - store i64 %2, i64* %17, align 8, !dbg !15, !tbaa !6 - %18 = getelementptr inbounds i64, i64* %17, i64 1, !dbg !15 - store i64 56565211319773434, i64* %18, align 8, !dbg !15, !tbaa !6 - %19 = getelementptr inbounds i64, i64* %18, i64 1, !dbg !15 - store i64* %19, i64** %16, align 8, !dbg !15 - %send2 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts.1, i64 0), !dbg !15 - store i64* getelementptr inbounds ([11 x i64], [11 x i64]* @iseqEncodedArray, i64 0, i64 6), i64** %11, align 8, !dbg !15, !tbaa !21 - %rubyStr_str.i = load i64, i64* getelementptr inbounds ([3 x i64], [3 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 2), align 8, !dbg !40, !invariant.load !5 - %20 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !16 - %21 = load i64*, i64** %20, align 8, !dbg !16 - store i64 %2, i64* %21, align 8, !dbg !16, !tbaa !6 - %22 = getelementptr inbounds i64, i64* %21, i64 1, !dbg !16 - store i64 %rubyStr_str.i, i64* %22, align 8, !dbg !16, !tbaa !6 - %23 = getelementptr inbounds i64, i64* %22, i64 1, !dbg !16 - store i64* %23, i64** %20, align 8, !dbg !16 - %send4 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts.2, i64 0), !dbg !16 - store i64* getelementptr inbounds ([11 x i64], [11 x i64]* @iseqEncodedArray, i64 0, i64 7), i64** %11, align 8, !dbg !16, !tbaa !21 - %rubyId_sym.i = load i64, i64* getelementptr inbounds ([3 x i64], [3 x i64]* @sorbet_moduleIDTable, i64 0, i64 2), align 8, !dbg !41, !invariant.load !5 - %rawSym.i = tail call i64 @rb_id2sym(i64 %rubyId_sym.i) #7, !dbg !41 - %24 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !17 - %25 = load i64*, i64** %24, align 8, !dbg !17 - store i64 %2, i64* %25, align 8, !dbg !17, !tbaa !6 - %26 = getelementptr inbounds i64, i64* %25, i64 1, !dbg !17 - store i64 %rawSym.i, i64* %26, align 8, !dbg !17, !tbaa !6 - %27 = getelementptr inbounds i64, i64* %26, i64 1, !dbg !17 - store i64* %27, i64** %24, align 8, !dbg !17 - %send6 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts.3, i64 0), !dbg !17 - store i64* getelementptr inbounds ([11 x i64], [11 x i64]* @iseqEncodedArray, i64 0, i64 8), i64** %11, align 8, !dbg !17, !tbaa !21 - %28 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !18 - %29 = load i64*, i64** %28, align 8, !dbg !18 - store i64 %2, i64* %29, align 8, !dbg !18, !tbaa !6 - %30 = getelementptr inbounds i64, i64* %29, i64 1, !dbg !18 - store i64 0, i64* %30, align 8, !dbg !18, !tbaa !6 - %31 = getelementptr inbounds i64, i64* %30, i64 1, !dbg !18 - store i64* %31, i64** %28, align 8, !dbg !18 - %send8 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts.4, i64 0), !dbg !18 - store i64* getelementptr inbounds ([11 x i64], [11 x i64]* @iseqEncodedArray, i64 0, i64 9), i64** %11, align 8, !dbg !18, !tbaa !21 - %32 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !19 - %33 = load i64*, i64** %32, align 8, !dbg !19 - store i64 %2, i64* %33, align 8, !dbg !19, !tbaa !6 - %34 = getelementptr inbounds i64, i64* %33, i64 1, !dbg !19 - store i64 20, i64* %34, align 8, !dbg !19, !tbaa !6 - %35 = getelementptr inbounds i64, i64* %34, i64 1, !dbg !19 - store i64* %35, i64** %32, align 8, !dbg !19 - %send10 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts.5, i64 0), !dbg !19 - store i64* getelementptr inbounds ([11 x i64], [11 x i64]* @iseqEncodedArray, i64 0, i64 10), i64** %11, align 8, !dbg !19, !tbaa !21 - %36 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !20 - %37 = load i64*, i64** %36, align 8, !dbg !20 - store i64 %2, i64* %37, align 8, !dbg !20, !tbaa !6 - %38 = getelementptr inbounds i64, i64* %37, i64 1, !dbg !20 - store i64 8, i64* %38, align 8, !dbg !20, !tbaa !6 - %39 = getelementptr inbounds i64, i64* %38, i64 1, !dbg !20 - store i64* %39, i64** %36, align 8, !dbg !20 - %send12 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts.6, i64 0), !dbg !20 - ret void -} - -attributes #0 = { nounwind readnone willreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #1 = { "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #2 = { noreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #3 = { nounwind ssp uwtable "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #4 = { ssp } -attributes #5 = { sspreq } -attributes #6 = { noreturn nounwind } -attributes #7 = { nounwind } - -!llvm.module.flags = !{!0, !1, !2} -!llvm.dbg.cu = !{!3} - -!0 = !{i32 2, !"Debug Info Version", i32 3} -!1 = !{i32 4, !"cf-protection-return", i32 1} -!2 = !{i32 4, !"cf-protection-branch", i32 1} -!3 = distinct !DICompileUnit(language: DW_LANG_C, file: !4, producer: "Sorbet LLVM", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !5) -!4 = !DIFile(filename: "test/testdata/compiler/literals.rb", directory: ".") -!5 = !{} -!6 = !{!7, !7, i64 0} -!7 = !{!"long", !8, i64 0} -!8 = !{!"omnipotent char", !9, i64 0} -!9 = !{!"Simple C/C++ TBAA"} -!10 = !DILocation(line: 4, column: 1, scope: !11) -!11 = distinct !DISubprogram(name: ".", linkageName: "func_.13", scope: null, file: !4, line: 4, type: !12, scopeLine: 4, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!12 = !DISubroutineType(types: !13) -!13 = !{!14} -!14 = !DIBasicType(name: "VALUE", size: 64, encoding: DW_ATE_signed) -!15 = !DILocation(line: 5, column: 1, scope: !11) -!16 = !DILocation(line: 6, column: 1, scope: !11) -!17 = !DILocation(line: 7, column: 1, scope: !11) -!18 = !DILocation(line: 8, column: 1, scope: !11) -!19 = !DILocation(line: 9, column: 1, scope: !11) -!20 = !DILocation(line: 10, column: 1, scope: !11) -!21 = !{!22, !22, i64 0} -!22 = !{!"any pointer", !8, i64 0} -!23 = !{!24, !7, i64 400} -!24 = !{!"rb_vm_struct", !7, i64 0, !25, i64 8, !22, i64 192, !22, i64 200, !22, i64 208, !29, i64 216, !8, i64 224, !26, i64 264, !26, i64 280, !26, i64 296, !26, i64 312, !7, i64 328, !28, i64 336, !28, i64 340, !28, i64 344, !28, i64 344, !28, i64 344, !28, i64 344, !28, i64 348, !7, i64 352, !8, i64 360, !7, i64 400, !7, i64 408, !7, i64 416, !7, i64 424, !7, i64 432, !7, i64 440, !7, i64 448, !22, i64 456, !22, i64 464, !30, i64 472, !31, i64 992, !22, i64 1016, !22, i64 1024, !28, i64 1032, !28, i64 1036, !26, i64 1040, !8, i64 1056, !7, i64 1096, !7, i64 1104, !7, i64 1112, !7, i64 1120, !7, i64 1128, !28, i64 1136, !22, i64 1144, !22, i64 1152, !22, i64 1160, !22, i64 1168, !22, i64 1176, !22, i64 1184, !28, i64 1192, !32, i64 1200, !8, i64 1232} -!25 = !{!"rb_global_vm_lock_struct", !22, i64 0, !8, i64 8, !26, i64 48, !22, i64 64, !28, i64 72, !8, i64 80, !8, i64 128, !28, i64 176, !28, i64 180} -!26 = !{!"list_head", !27, i64 0} -!27 = !{!"list_node", !22, i64 0, !22, i64 8} -!28 = !{!"int", !8, i64 0} -!29 = !{!"long long", !8, i64 0} -!30 = !{!"", !8, i64 0} -!31 = !{!"rb_hook_list_struct", !22, i64 0, !28, i64 8, !28, i64 12, !28, i64 16} -!32 = !{!"", !7, i64 0, !7, i64 8, !7, i64 16, !7, i64 24} -!33 = !{!34, !22, i64 16} -!34 = !{!"rb_execution_context_struct", !22, i64 0, !7, i64 8, !22, i64 16, !22, i64 24, !22, i64 32, !28, i64 40, !28, i64 44, !22, i64 48, !22, i64 56, !22, i64 64, !7, i64 72, !7, i64 80, !22, i64 88, !7, i64 96, !22, i64 104, !22, i64 112, !7, i64 120, !7, i64 128, !8, i64 136, !8, i64 137, !7, i64 144, !35, i64 152} -!35 = !{!"", !22, i64 0, !22, i64 8, !7, i64 16, !8, i64 24} -!36 = !{!37, !22, i64 16} -!37 = !{!"rb_control_frame_struct", !22, i64 0, !22, i64 8, !22, i64 16, !7, i64 24, !22, i64 32, !22, i64 40, !22, i64 48} -!38 = !{!37, !22, i64 32} -!39 = !DILocation(line: 0, scope: !11) -!40 = !DILocation(line: 6, column: 6, scope: !11) -!41 = !DILocation(line: 7, column: 6, scope: !11) diff --git a/test/testdata/compiler/locals_type_info.rb b/test/testdata/compiler/locals_type_info.rb index 21e2939c8b..af98a18163 100644 --- a/test/testdata/compiler/locals_type_info.rb +++ b/test/testdata/compiler/locals_type_info.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL -# run_filecheck: OPT extend T::Sig @@ -28,20 +26,4 @@ def f(queue, obj) # is there so that if it disappeared in some way, we have a warning besides just the # OPT-NOT test below succeeding. -# INITIAL-LABEL: define internal i64 @"func_Object#1f$block_1" -# INITIAL: [[QUEUE:%[0-9]+]] = call i64 @sorbet_readLocal{{.*}} -# INITIAL-NEXT: [[ASSUMPTION:%[0-9]+]] = call i1 @sorbet_i_isa_Array(i64 [[QUEUE]]){{.*}} -# INITIAL-NEXT: call void @llvm.assume(i1 [[ASSUMPTION]]){{.*}} -# INITIAL: sendContinuation: -# INITIAL-NEXT: [[TEST:%[0-9]+]] = call i1 @sorbet_i_isa_Array(i64 [[QUEUE]]){{.*}} -# INITIAL-NEXT: [[COND:%[0-9]+]] = call i1 @llvm.expect.i1(i1 [[TEST]], i1 true){{.*}} -# INITIAL-NEXT: br i1 [[COND]], label %"fastSymCallIntrinsic_Array_<<", label %"alternativeCallIntrinsic_Array_<<"{{.*}} -# INITIAL: "alternativeCallIntrinsic_Array_<<": -# INITIAL: call i64{{.*}}@sorbet_i_send -# INITIAL: "fastSymCallIntrinsic_Array_<<": -# INITIAL{LITERAL}: } - # We should have optimized out the alternative VM-based call path based on type assumptions. -# OPT-LABEL: define internal i64 @"func_Object#1f$block_1" -# OPT-NOT: call i64 @sorbet_callFuncWithCache -# OPT{LITERAL}: } diff --git a/test/testdata/compiler/method.rb b/test/testdata/compiler/method.rb index 543aff2a9a..5086d2c1a2 100644 --- a/test/testdata/compiler/method.rb +++ b/test/testdata/compiler/method.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL def hello(name) i = 0 @@ -12,7 +11,3 @@ def hello(name) end hello("sorbet") -# INITIAL-LABEL: define internal i64 @"func_.13 -# INITIAL: [[VAR:%[a-zA-Z0-9_]+]] = load i8*, i8** @addr_str_hello{{.*}} -# INITIAL: call void @sorbet_defineMethod({{.*}}[[VAR]] -# INITIAL{LITERAL}: } diff --git a/test/testdata/compiler/no_codegen_for_let.rb b/test/testdata/compiler/no_codegen_for_let.rb index e1f1798cb8..b6c4ea15af 100644 --- a/test/testdata/compiler/no_codegen_for_let.rb +++ b/test/testdata/compiler/no_codegen_for_let.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL def bar(x) y = T.let(x, T.nilable(String)) @@ -11,7 +10,3 @@ def bar(x) p bar('baz') p bar(nil) -# INITIAL-LABEL: @"func_Object#3bar" -# INITIAL-NOT: sorbet_i_getRubyClass{{.*}}str_T -# INITIAL-NOT: sorbet_callFuncWithCache{{.*}}ic_nilable -# INITIAL: {{^[}]}} diff --git a/test/testdata/compiler/regexp_flags.rb b/test/testdata/compiler/regexp_flags.rb index e1c7a7c444..7ca137c01e 100644 --- a/test/testdata/compiler/regexp_flags.rb +++ b/test/testdata/compiler/regexp_flags.rb @@ -1,106 +1,35 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL OPT def noflags(x) x.match(/pattern/) end -# INITIAL-LABEL: define internal i64 @"func_Object#7noflags"( -# INITIAL: load i64, i64* @rubyRegexpFrozen_pattern_0 -# INITIAL-LITERAL: } - -# OPT-LABEL: define internal i64 @"func_Object#7noflags"( -# OPT-NOT: ic_new -# OPT: load i64, i64* @rubyRegexpFrozen_pattern_0 -# OPT-NOT: ic_new -# OPT-LITERAL: } - def i_flag(x) x.match(/pattern/i) end -# INITIAL-LABEL: define internal i64 @"func_Object#6i_flag"( -# INITIAL: load i64, i64* @rubyRegexpFrozen_pattern_1 -# INITIAL-LITERAL: } - -# OPT-LABEL: define internal i64 @"func_Object#6i_flag"( -# OPT-NOT: ic_new -# OPT: load i64, i64* @rubyRegexpFrozen_pattern_1 -# OPT-NOT: ic_new -# OPT-LITERAL: } - def ix_flag(x) x.match(/pattern # Comment/ix) end -# INITIAL-LABEL: define internal i64 @"func_Object#7ix_flag"( -# INITIAL: load i64, i64* @"rubyRegexpFrozen_pattern # Comment_3" -# INITIAL-LITERAL: } - -# OPT-LABEL: define internal i64 @"func_Object#7ix_flag"( -# OPT-NOT: ic_new -# OPT: load i64, i64* @"rubyRegexpFrozen_pattern # Comment_3" -# OPT-NOT: ic_new -# OPT-LITERAL: } - def ixm_flag(x) x.match(/pattern.more # Comment/ixm) end -# INITIAL-LABEL: define internal i64 @"func_Object#8ixm_flag"( -# INITIAL: load i64, i64* @"rubyRegexpFrozen_pattern.more # Comment_7" -# INITIAL-LITERAL: } - -# OPT-LABEL: define internal i64 @"func_Object#8ixm_flag"( -# OPT-NOT: ic_new -# OPT: load i64, i64* @"rubyRegexpFrozen_pattern.more # Comment_7" -# OPT-NOT: ic_new -# OPT-LITERAL: } - def x_flag(x) x.match(/pattern # Comment/x) end -# INITIAL-LABEL: define internal i64 @"func_Object#6x_flag"( -# INITIAL: load i64, i64* @"rubyRegexpFrozen_pattern # Comment_2" -# INITIAL-LITERAL: } - -# OPT-LABEL: define internal i64 @"func_Object#6x_flag"( -# OPT-NOT: ic_new -# OPT: load i64, i64* @"rubyRegexpFrozen_pattern # Comment_2" -# OPT-NOT: ic_new -# OPT-LITERAL: } - def xm_flag(x) x.match(/pattern.still more # Comment/xm) end -# INITIAL-LABEL: define internal i64 @"func_Object#7xm_flag"( -# INITIAL: load i64, i64* @"rubyRegexpFrozen_pattern.still more # Comment_6" -# INITIAL-LITERAL: } - -# OPT-LABEL: define internal i64 @"func_Object#7xm_flag"( -# OPT-NOT: ic_new -# OPT: load i64, i64* @"rubyRegexpFrozen_pattern.still more # Comment__6 -# OPT-NOT: ic_new -# OPT-LITERAL: } - def m_flag(x) x.match(/pattern.mflag/m) end -# INITIAL-LABEL: define internal i64 @"func_Object#6m_flag"( -# INITIAL: load i64, i64* @rubyRegexpFrozen_pattern.mflag_4 -# INITIAL-LITERAL: } - -# OPT-LABEL: define internal i64 @"func_Object#6m_flag"( -# OPT-NOT: ic_new -# OPT: load i64, i64* @rubyRegexpFrozen_pattern.mflag_4 -# OPT-NOT: ic_new -# OPT-LITERAL: } - # Run some execution tests, too. p noflags("pattern") p noflags("PATTERN") diff --git a/test/testdata/compiler/repeated_casts.opt.ll.exp b/test/testdata/compiler/repeated_casts.opt.ll.exp deleted file mode 100644 index eecdfd148e..0000000000 --- a/test/testdata/compiler/repeated_casts.opt.ll.exp +++ /dev/null @@ -1,492 +0,0 @@ -; ModuleID = 'payload' -source_filename = "llvm-link" -target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } -%struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } -%struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } -%struct.anon.0 = type { i16, [2 x i8] } -%struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } -%struct.rb_iseq_location_struct = type { i64, i64, i64, i64, i32, %struct.rb_code_location_struct } -%struct.rb_code_location_struct = type { %struct.rb_code_position_struct, %struct.rb_code_position_struct } -%struct.rb_code_position_struct = type { i32, i32 } -%struct.iseq_insn_info = type { %struct.iseq_insn_info_entry*, i32*, i32, %struct.succ_index_table* } -%struct.iseq_insn_info_entry = type opaque -%struct.succ_index_table = type opaque -%struct.iseq_catch_table = type opaque -%union.iseq_inline_storage_entry = type { %struct.iseq_inline_cache_entry } -%struct.iseq_inline_cache_entry = type { i64, %struct.rb_cref_struct*, i64 } -%struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } -%struct.rb_scope_visi_struct = type { i8, [3 x i8] } -%struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } -%struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } -%union.anon.13 = type { %struct.rb_method_cfunc_struct } -%struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%struct.rb_calling_info = type { i64, i64, i32, i32 } -%union.anon.15 = type { i32 } -%struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.16 = type { i64, i64, i64, i64* } -%union.anon.17 = type { %struct.anon.18 } -%struct.anon.18 = type { i64, i32 } -%struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } -%struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } -%struct.rb_fiber_struct = type opaque -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } -%struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } -%struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.__pthread_cond_s } -%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } -%union.anon = type { i64 } -%struct.anon.5 = type { [65 x i64] } -%struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } -%struct.rb_event_hook_struct = type opaque -%struct.rb_postponed_job_struct = type opaque -%struct.list_head = type { %struct.list_node } -%struct.rb_objspace = type opaque -%struct.rb_at_exit_list = type { void (%struct.rb_vm_struct*)*, %struct.rb_at_exit_list* } -%struct.rb_builtin_function = type opaque -%struct.anon.6 = type { i64, i64, i64, i64 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } -%union.anon.9 = type { %union.pthread_cond_t } -%union.pthread_mutex_t = type { %struct.__pthread_mutex_s } -%struct.__pthread_mutex_s = type { i32, i32, i32, i32, i32, i16, i16, %struct.__pthread_internal_list } -%struct.__pthread_internal_list = type { %struct.__pthread_internal_list*, %struct.__pthread_internal_list* } -%struct.rb_unblock_callback = type { void (i8*)*, i8* } -%struct.rb_mutex_struct = type opaque -%struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.10 = type { %struct.anon.11 } -%struct.anon.11 = type { i64, i64, i32 } -%struct.st_table = type { i8, i8, i8, i32, %struct.st_hash_type*, i64, i64*, i64, i64, %struct.st_table_entry* } -%struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } -%struct.st_table_entry = type opaque -%struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } -%struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } -%struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } -%struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } -%struct.__sigset_t = type { [16 x i64] } -%struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } -%struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } -%struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } -%struct.rb_call_info_with_kwarg = type { %struct.rb_call_info, %struct.rb_call_info_kw_arg* } -%struct.rb_call_info_kw_arg = type { i32, [1 x i64] } - -@ruby_current_execution_context_ptr = external local_unnamed_addr global %struct.rb_execution_context_struct*, align 8 -@ruby_vm_global_constant_state = external local_unnamed_addr global i64, align 8 -@rb_eRuntimeError = external local_unnamed_addr global i64, align 8 -@.str.9 = private unnamed_addr constant [95 x i8] c"sorbet_getBuildSCMRevision: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@.str.10 = private unnamed_addr constant [93 x i8] c"sorbet_getIsReleaseBuild: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@"stackFramePrecomputed_func_Object#10doubleCast" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@iseqEncodedArray = internal global [12 x i64] zeroinitializer -@fileLineNumberInfo = internal global %struct.SorbetLineNumberInfo zeroinitializer -@ic_foo = internal global %struct.FunctionInlineCache zeroinitializer -@ic_foo.1 = internal global %struct.FunctionInlineCache zeroinitializer -@"stackFramePrecomputed_func_.13" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"stackFramePrecomputed_func_A#3foo" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"stackFramePrecomputed_func_A.13" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@sorbet_moduleStringTable = internal unnamed_addr constant [115 x i8] c"doubleCast\00test/testdata/compiler/repeated_casts.rb\00A\00T.cast\00foo\00\00Object\00normal\00a\00master\00\00", align 1 -@sorbet_moduleIDTable = internal unnamed_addr global [6 x i64] zeroinitializer, align 8 -@sorbet_moduleIDDescriptors = internal unnamed_addr constant [6 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 10 }, %struct.rb_code_position_struct { i32 61, i32 3 }, %struct.rb_code_position_struct { i32 65, i32 16 }, %struct.rb_code_position_struct { i32 89, i32 6 }, %struct.rb_code_position_struct { i32 96, i32 1 }, %struct.rb_code_position_struct { i32 105, i32 9 }], align 8 -@sorbet_moduleRubyStringTable = internal unnamed_addr global [5 x i64] zeroinitializer, align 8 -@sorbet_moduleRubyStringDescriptors = internal unnamed_addr constant [5 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 10 }, %struct.rb_code_position_struct { i32 11, i32 40 }, %struct.rb_code_position_struct { i32 65, i32 16 }, %struct.rb_code_position_struct { i32 61, i32 3 }, %struct.rb_code_position_struct { i32 105, i32 9 }], align 8 -@guard_epoch_A = linkonce local_unnamed_addr global i64 0 -@guarded_const_A = linkonce local_unnamed_addr global i64 0 -@rb_cObject = external local_unnamed_addr constant i64 - -; Function Attrs: nounwind readnone willreturn -declare i64 @rb_obj_is_kind_of(i64, i64) local_unnamed_addr #0 - -; Function Attrs: cold noreturn -declare void @sorbet_cast_failure(i64, i8*, i8*) local_unnamed_addr #1 - -; Function Attrs: noreturn -declare void @sorbet_raiseArity(i32, i32, i32) local_unnamed_addr #2 - -declare %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64, i64, i64, i64, %struct.rb_iseq_struct*, i32, i32, %struct.SorbetLineNumberInfo*, i64*, i32, i32) local_unnamed_addr #3 - -declare void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo*, i64*, i32) local_unnamed_addr #3 - -declare i64 @sorbet_getConstant(i8*, i64) local_unnamed_addr #3 - -declare i64 @sorbet_readRealpath() local_unnamed_addr #3 - -declare %struct.rb_control_frame_struct* @sorbet_pushStaticInitFrame(i64) local_unnamed_addr #3 - -declare void @sorbet_popFrame() local_unnamed_addr #3 - -declare void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) local_unnamed_addr #3 - -declare i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache*, i64) local_unnamed_addr #3 - -declare void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_iseq_struct*) local_unnamed_addr #3 - -declare void @sorbet_vm_define_method(i64, i8*, i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)*, i8*, %struct.rb_iseq_struct*, i1 zeroext) local_unnamed_addr #3 - -declare void @sorbet_vm_intern_ids(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #3 - -declare void @sorbet_vm_init_string_table(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #3 - -declare i64 @rb_define_class(i8*, i64) local_unnamed_addr #3 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #4 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #4 - -; Function Attrs: noreturn -declare void @rb_raise(i64, i8*, ...) local_unnamed_addr #2 - -; Function Attrs: allocsize(0,1) -declare noalias nonnull i8* @ruby_xcalloc(i64, i64) local_unnamed_addr #5 - -; Function Attrs: allocsize(0,1) -declare noalias nonnull i8* @ruby_xmalloc2(i64, i64) local_unnamed_addr #5 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) #4 - -; Function Attrs: nounwind ssp uwtable -define weak i32 @sorbet_getIsReleaseBuild() local_unnamed_addr #6 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([93 x i8], [93 x i8]* @.str.10, i64 0, i64 0)) #13 - unreachable -} - -; Function Attrs: nounwind ssp uwtable -define weak i8* @sorbet_getBuildSCMRevision() local_unnamed_addr #6 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([95 x i8], [95 x i8]* @.str.9, i64 0, i64 0)) #13 - unreachable -} - -define internal fastcc void @sorbet_globalConstructors(i64 %realpath) unnamed_addr { -allocRubyIds: - tail call void @sorbet_vm_intern_ids(i64* noundef getelementptr inbounds ([6 x i64], [6 x i64]* @sorbet_moduleIDTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([6 x %struct.rb_code_position_struct], [6 x %struct.rb_code_position_struct]* @sorbet_moduleIDDescriptors, i32 0, i32 0), i32 noundef 6, i8* noundef getelementptr inbounds ([115 x i8], [115 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_vm_init_string_table(i64* noundef getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleRubyStringTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([5 x %struct.rb_code_position_struct], [5 x %struct.rb_code_position_struct]* @sorbet_moduleRubyStringDescriptors, i32 0, i32 0), i32 noundef 5, i8* noundef getelementptr inbounds ([115 x i8], [115 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef getelementptr inbounds ([12 x i64], [12 x i64]* @iseqEncodedArray, i32 0, i32 0), i32 noundef 12) - tail call fastcc void @"Constr_stackFramePrecomputed_func_Object#10doubleCast"(i64 %realpath) - %rubyId_foo = load i64, i64* getelementptr inbounds ([6 x i64], [6 x i64]* @sorbet_moduleIDTable, i64 0, i64 1), align 8, !dbg !10, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_foo, i64 %rubyId_foo, i32 noundef 16, i32 noundef 0, i32 noundef 0), !dbg !10 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_foo.1, i64 %rubyId_foo, i32 noundef 16, i32 noundef 0, i32 noundef 0), !dbg !15 - tail call fastcc void @"Constr_stackFramePrecomputed_func_.13"(i64 %realpath) - tail call fastcc void @"Constr_stackFramePrecomputed_func_A#3foo"(i64 %realpath) - tail call fastcc void @"Constr_stackFramePrecomputed_func_A.13"(i64 %realpath) - ret void -} - -; Function Attrs: nounwind sspreq uwtable -define internal i64 @"func_Object#10doubleCast"(i32 %argc, i64* nocapture readonly %argArray, i64 %selfRaw, %struct.rb_control_frame_struct* nonnull align 8 dereferenceable(8) %cfp, i8* nocapture nofree readnone %calling, i8* nocapture nofree readnone %callData) #7 !dbg !11 { -functionEntryInitializers: - %0 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 0 - store i64* getelementptr inbounds ([12 x i64], [12 x i64]* @iseqEncodedArray, i64 0, i64 8), i64** %0, align 8, !tbaa !16 - %tooManyArgs = icmp ugt i32 %argc, 1, !dbg !18 - %tooFewArgs = icmp ult i32 %argc, 1, !dbg !18 - %or.cond = or i1 %tooManyArgs, %tooFewArgs, !dbg !18 - br i1 %or.cond, label %argCountFailBlock, label %fillRequiredArgs, !dbg !18, !prof !19 - -argCountFailBlock: ; preds = %functionEntryInitializers - tail call void @sorbet_raiseArity(i32 %argc, i32 noundef 1, i32 noundef 1) #14, !dbg !18 - unreachable, !dbg !18 - -fillRequiredArgs: ; preds = %functionEntryInitializers - %rawArg_a = load i64, i64* %argArray, align 8, !dbg !18 - store i64* getelementptr inbounds ([12 x i64], [12 x i64]* @iseqEncodedArray, i64 0, i64 9), i64** %0, align 8, !dbg !20, !tbaa !16 - %1 = load i64, i64* @guard_epoch_A, align 8, !dbg !10 - %2 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !10, !tbaa !21 - %needTakeSlowPath = icmp ne i64 %1, %2, !dbg !10 - br i1 %needTakeSlowPath, label %3, label %4, !dbg !10, !prof !23 - -3: ; preds = %fillRequiredArgs - tail call void @const_recompute_A(), !dbg !10 - br label %4, !dbg !10 - -4: ; preds = %fillRequiredArgs, %3 - %5 = load i64, i64* @guarded_const_A, align 8, !dbg !10 - %6 = load i64, i64* @guard_epoch_A, align 8, !dbg !10 - %7 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !10, !tbaa !21 - %guardUpdated = icmp eq i64 %6, %7, !dbg !10 - tail call void @llvm.assume(i1 %guardUpdated), !dbg !10 - %8 = tail call i64 @rb_obj_is_kind_of(i64 %rawArg_a, i64 %5), !dbg !10 - %9 = icmp eq i64 %8, 20, !dbg !10 - br i1 %9, label %typeTestSuccess, label %codeRepl, !dbg !10, !prof !24 - -typeTestSuccess: ; preds = %4 - %10 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 1, !dbg !10 - %11 = load i64*, i64** %10, align 8, !dbg !10 - store i64 %rawArg_a, i64* %11, align 8, !dbg !10, !tbaa !6 - %12 = getelementptr inbounds i64, i64* %11, i64 1, !dbg !10 - store i64* %12, i64** %10, align 8, !dbg !10 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_foo, i64 0), !dbg !10 - store i64* getelementptr inbounds ([12 x i64], [12 x i64]* @iseqEncodedArray, i64 0, i64 10), i64** %0, align 8, !dbg !10, !tbaa !16 - %13 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 1, !dbg !15 - %14 = load i64*, i64** %13, align 8, !dbg !15 - store i64 %rawArg_a, i64* %14, align 8, !dbg !15, !tbaa !6 - %15 = getelementptr inbounds i64, i64* %14, i64 1, !dbg !15 - store i64* %15, i64** %13, align 8, !dbg !15 - %send35 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_foo.1, i64 0), !dbg !15 - ret i64 %send35 - -codeRepl: ; preds = %4 - tail call fastcc void @"func_Object#10doubleCast.cold.1"(i64 %rawArg_a) #15, !dbg !10 - unreachable -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_Object#10doubleCast"(i64 %realpath) unnamed_addr #8 { -entryInitializers: - %rubyId_doubleCast = load i64, i64* getelementptr inbounds ([6 x i64], [6 x i64]* @sorbet_moduleIDTable, i64 0, i64 0), align 8, !invariant.load !5 - %rubyStr_doubleCast = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 0), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/repeated_casts.rb" = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %rubyStr_doubleCast, i64 %rubyId_doubleCast, i64 %"rubyStr_test/testdata/compiler/repeated_casts.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 1, i32 noundef 8, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 1) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Object#10doubleCast", align 8 - ret void -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_.13"(i64 %realpath) unnamed_addr #8 { -entryInitializers: - %"rubyId_" = load i64, i64* getelementptr inbounds ([6 x i64], [6 x i64]* @sorbet_moduleIDTable, i64 0, i64 2), align 8, !invariant.load !5 - %"rubyStr_" = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 2), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/repeated_casts.rb" = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_", i64 %"rubyId_", i64 %"rubyStr_test/testdata/compiler/repeated_casts.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 0, i32 noundef 4, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 4) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - ret void -} - -; Function Attrs: sspreq -define void @Init_repeated_casts() local_unnamed_addr #9 { -entry: - %positional_table.i = alloca i64, align 8, !dbg !25 - %realpath = tail call i64 @sorbet_readRealpath() - tail call fastcc void @sorbet_globalConstructors(i64 %realpath) - %0 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !16 - %1 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %0, i64 0, i32 2 - %2 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %1, align 8, !tbaa !27 - %3 = bitcast i64* %positional_table.i to i8* - call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %3) - %stackFrame.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - %4 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame.i, %struct.rb_iseq_struct** %4, align 8, !tbaa !31 - %5 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 4 - %6 = load i64*, i64** %5, align 8, !tbaa !33 - %7 = load i64, i64* %6, align 8, !tbaa !6 - %8 = and i64 %7, -33 - store i64 %8, i64* %6, align 8, !tbaa !6 - tail call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %0, %struct.rb_control_frame_struct* %2, %struct.rb_iseq_struct* %stackFrame.i) #16 - %9 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 0 - store i64* getelementptr inbounds ([12 x i64], [12 x i64]* @iseqEncodedArray, i64 0, i64 4), i64** %9, align 8, !dbg !34, !tbaa !16 - %10 = load i64, i64* @rb_cObject, align 8, !dbg !35 - %11 = tail call i64 @rb_define_class(i8* getelementptr inbounds ([115 x i8], [115 x i8]* @sorbet_moduleStringTable, i64 0, i64 52), i64 %10) #16, !dbg !35 - %12 = tail call %struct.rb_control_frame_struct* @sorbet_pushStaticInitFrame(i64 %11) #16, !dbg !35 - %stackFrame.i.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_A.13", align 8 - %13 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !16 - %14 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %13, i64 0, i32 2 - %15 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %14, align 8, !tbaa !27 - %16 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %15, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame.i.i, %struct.rb_iseq_struct** %16, align 8, !tbaa !31 - %17 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %15, i64 0, i32 4 - %18 = load i64*, i64** %17, align 8, !tbaa !33 - %19 = load i64, i64* %18, align 8, !tbaa !6 - %20 = and i64 %19, -33 - store i64 %20, i64* %18, align 8, !tbaa !6 - tail call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %13, %struct.rb_control_frame_struct* %15, %struct.rb_iseq_struct* %stackFrame.i.i) #16 - %21 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %12, i64 0, i32 0 - store i64* getelementptr inbounds ([12 x i64], [12 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %21, align 8, !dbg !36, !tbaa !16 - %22 = load i64, i64* @guard_epoch_A, align 8, !dbg !39 - %23 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !39, !tbaa !21 - %needTakeSlowPath = icmp ne i64 %22, %23, !dbg !39 - br i1 %needTakeSlowPath, label %24, label %25, !dbg !39, !prof !23 - -24: ; preds = %entry - tail call void @const_recompute_A(), !dbg !39 - br label %25, !dbg !39 - -25: ; preds = %entry, %24 - %26 = load i64, i64* @guarded_const_A, align 8, !dbg !39 - %27 = load i64, i64* @guard_epoch_A, align 8, !dbg !39 - %28 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !39, !tbaa !21 - %guardUpdated = icmp eq i64 %27, %28, !dbg !39 - tail call void @llvm.assume(i1 %guardUpdated), !dbg !39 - %stackFrame7.i.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_A#3foo", align 8, !dbg !39 - %29 = tail call noalias nonnull i8* @ruby_xcalloc(i64 noundef 1, i64 noundef 64) #17, !dbg !39 - %30 = bitcast i8* %29 to i16*, !dbg !39 - %31 = load i16, i16* %30, align 8, !dbg !39 - %32 = and i16 %31, -384, !dbg !39 - store i16 %32, i16* %30, align 8, !dbg !39 - %33 = getelementptr inbounds i8, i8* %29, i64 4, !dbg !39 - tail call void @llvm.memset.p0i8.i64(i8* nonnull align 4 %33, i8 0, i64 28, i1 false) #16, !dbg !39 - tail call void @sorbet_vm_define_method(i64 %26, i8* getelementptr inbounds ([115 x i8], [115 x i8]* @sorbet_moduleStringTable, i64 0, i64 61), i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)* noundef @"func_A#3foo", i8* nonnull %29, %struct.rb_iseq_struct* %stackFrame7.i.i, i1 noundef zeroext false) #16, !dbg !39 - tail call void @sorbet_popFrame() #16, !dbg !35 - store i64* getelementptr inbounds ([12 x i64], [12 x i64]* @iseqEncodedArray, i64 0, i64 8), i64** %9, align 8, !dbg !35, !tbaa !16 - %stackFrame19.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_Object#10doubleCast", align 8, !dbg !25 - %34 = tail call noalias nonnull i8* @ruby_xcalloc(i64 noundef 1, i64 noundef 64) #17, !dbg !25 - %35 = bitcast i8* %34 to i16*, !dbg !25 - %36 = load i16, i16* %35, align 8, !dbg !25 - %37 = and i16 %36, -384, !dbg !25 - %38 = or i16 %37, 1, !dbg !25 - store i16 %38, i16* %35, align 8, !dbg !25 - %39 = getelementptr inbounds i8, i8* %34, i64 8, !dbg !25 - %40 = bitcast i8* %39 to i32*, !dbg !25 - store i32 1, i32* %40, align 8, !dbg !25, !tbaa !40 - %41 = getelementptr inbounds i8, i8* %34, i64 12, !dbg !25 - %42 = getelementptr inbounds i8, i8* %34, i64 4, !dbg !25 - %43 = bitcast i8* %42 to i32*, !dbg !25 - tail call void @llvm.memset.p0i8.i64(i8* nonnull align 4 %41, i8 0, i64 20, i1 false) #16, !dbg !25 - store i32 1, i32* %43, align 4, !dbg !25, !tbaa !43 - %rubyId_a.i = load i64, i64* getelementptr inbounds ([6 x i64], [6 x i64]* @sorbet_moduleIDTable, i64 0, i64 4), align 8, !dbg !25, !invariant.load !5 - store i64 %rubyId_a.i, i64* %positional_table.i, align 8, !dbg !25 - %44 = tail call noalias nonnull i8* @ruby_xmalloc2(i64 noundef 1, i64 noundef 8) #17, !dbg !25 - call void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture nonnull writeonly align 1 %44, i8* nocapture noundef nonnull readonly align 8 dereferenceable(8) %3, i64 noundef 8, i1 noundef false) #16, !dbg !25 - %45 = getelementptr inbounds i8, i8* %34, i64 32, !dbg !25 - %46 = bitcast i8* %45 to i8**, !dbg !25 - store i8* %44, i8** %46, align 8, !dbg !25, !tbaa !44 - tail call void @sorbet_vm_define_method(i64 %10, i8* noundef getelementptr inbounds ([115 x i8], [115 x i8]* @sorbet_moduleStringTable, i64 0, i64 0), i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)* noundef @"func_Object#10doubleCast", i8* nonnull %34, %struct.rb_iseq_struct* %stackFrame19.i, i1 noundef zeroext false) #16, !dbg !25 - call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %3) - ret void -} - -; Function Attrs: nounwind sspreq uwtable -define internal noundef i64 @"func_A#3foo"(i32 %argc, i64* nocapture nofree readnone %argArray, i64 %selfRaw, %struct.rb_control_frame_struct* nocapture nonnull writeonly align 8 dereferenceable(8) %cfp, i8* nocapture nofree readnone %calling, i8* nocapture nofree readnone %callData) #7 !dbg !45 { -functionEntryInitializers: - %0 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 0 - store i64* getelementptr inbounds ([12 x i64], [12 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %0, align 8, !tbaa !16 - %tooManyArgs = icmp ugt i32 %argc, 0, !dbg !46 - br i1 %tooManyArgs, label %argCountFailBlock, label %fillRequiredArgs, !dbg !46, !prof !47 - -argCountFailBlock: ; preds = %functionEntryInitializers - tail call void @sorbet_raiseArity(i32 %argc, i32 noundef 0, i32 noundef 0) #14, !dbg !46 - unreachable, !dbg !46 - -fillRequiredArgs: ; preds = %functionEntryInitializers - store i64* getelementptr inbounds ([12 x i64], [12 x i64]* @iseqEncodedArray, i64 0, i64 6), i64** %0, align 8, !dbg !48, !tbaa !16 - ret i64 8 -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_A#3foo"(i64 %realpath) unnamed_addr #8 { -entryInitializers: - %rubyId_foo = load i64, i64* getelementptr inbounds ([6 x i64], [6 x i64]* @sorbet_moduleIDTable, i64 0, i64 1), align 8, !invariant.load !5 - %rubyStr_foo = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 3), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/repeated_casts.rb" = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %rubyStr_foo, i64 %rubyId_foo, i64 %"rubyStr_test/testdata/compiler/repeated_casts.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 1, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 0) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_A#3foo", align 8 - ret void -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_A.13"(i64 %realpath) unnamed_addr #8 { -entryInitializers: - %"rubyId_" = load i64, i64* getelementptr inbounds ([6 x i64], [6 x i64]* @sorbet_moduleIDTable, i64 0, i64 5), align 8, !invariant.load !5 - %"rubyStr_" = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 4), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/repeated_casts.rb" = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_", i64 %"rubyId_", i64 %"rubyStr_test/testdata/compiler/repeated_casts.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 3, i32 noundef 4, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 4) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_A.13", align 8 - ret void -} - -; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly -declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #10 - -; Function Attrs: cold minsize noreturn nounwind sspreq uwtable -define internal fastcc void @"func_Object#10doubleCast.cold.1"(i64 %rawArg_a) unnamed_addr #11 !dbg !49 { -newFuncRoot: - tail call void @sorbet_cast_failure(i64 %rawArg_a, i8* getelementptr inbounds ([115 x i8], [115 x i8]* @sorbet_moduleStringTable, i64 0, i64 54), i8* getelementptr inbounds ([115 x i8], [115 x i8]* @sorbet_moduleStringTable, i64 0, i64 52)) #14, !dbg !51 - unreachable, !dbg !51 -} - -; Function Attrs: nofree nosync nounwind willreturn -declare void @llvm.assume(i1 noundef) #12 - -; Function Attrs: ssp -define linkonce void @const_recompute_A() local_unnamed_addr #8 { - %1 = tail call i64 @sorbet_getConstant(i8* getelementptr inbounds ([115 x i8], [115 x i8]* @sorbet_moduleStringTable, i64 0, i64 52), i64 1) - store i64 %1, i64* @guarded_const_A, align 8 - %2 = load i64, i64* @ruby_vm_global_constant_state, align 8, !tbaa !21 - store i64 %2, i64* @guard_epoch_A, align 8 - ret void -} - -attributes #0 = { nounwind readnone willreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #1 = { cold noreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #2 = { noreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #3 = { "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #4 = { argmemonly nofree nosync nounwind willreturn } -attributes #5 = { allocsize(0,1) "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #6 = { nounwind ssp uwtable "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #7 = { nounwind sspreq uwtable } -attributes #8 = { ssp } -attributes #9 = { sspreq } -attributes #10 = { argmemonly nofree nosync nounwind willreturn writeonly } -attributes #11 = { cold minsize noreturn nounwind sspreq uwtable } -attributes #12 = { nofree nosync nounwind willreturn } -attributes #13 = { noreturn nounwind } -attributes #14 = { noreturn } -attributes #15 = { noinline } -attributes #16 = { nounwind } -attributes #17 = { nounwind allocsize(0,1) } - -!llvm.module.flags = !{!0, !1, !2} -!llvm.dbg.cu = !{!3} - -!0 = !{i32 2, !"Debug Info Version", i32 3} -!1 = !{i32 4, !"cf-protection-return", i32 1} -!2 = !{i32 4, !"cf-protection-branch", i32 1} -!3 = distinct !DICompileUnit(language: DW_LANG_C, file: !4, producer: "Sorbet LLVM", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !5) -!4 = !DIFile(filename: "test/testdata/compiler/repeated_casts.rb", directory: ".") -!5 = !{} -!6 = !{!7, !7, i64 0} -!7 = !{!"long", !8, i64 0} -!8 = !{!"omnipotent char", !9, i64 0} -!9 = !{!"Simple C/C++ TBAA"} -!10 = !DILocation(line: 9, column: 3, scope: !11) -!11 = distinct !DISubprogram(name: "Object#doubleCast", linkageName: "func_Object#10doubleCast", scope: null, file: !4, line: 8, type: !12, scopeLine: 8, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!12 = !DISubroutineType(types: !13) -!13 = !{!14} -!14 = !DIBasicType(name: "VALUE", size: 64, encoding: DW_ATE_signed) -!15 = !DILocation(line: 10, column: 3, scope: !11) -!16 = !{!17, !17, i64 0} -!17 = !{!"any pointer", !8, i64 0} -!18 = !DILocation(line: 8, column: 1, scope: !11) -!19 = !{!"branch_weights", i32 4001, i32 4000000} -!20 = !DILocation(line: 8, column: 16, scope: !11) -!21 = !{!22, !22, i64 0} -!22 = !{!"long long", !8, i64 0} -!23 = !{!"branch_weights", i32 1, i32 10000} -!24 = !{!"branch_weights", i32 2000, i32 1} -!25 = !DILocation(line: 8, column: 1, scope: !26) -!26 = distinct !DISubprogram(name: ".", linkageName: "func_.13", scope: null, file: !4, line: 4, type: !12, scopeLine: 4, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!27 = !{!28, !17, i64 16} -!28 = !{!"rb_execution_context_struct", !17, i64 0, !7, i64 8, !17, i64 16, !17, i64 24, !17, i64 32, !29, i64 40, !29, i64 44, !17, i64 48, !17, i64 56, !17, i64 64, !7, i64 72, !7, i64 80, !17, i64 88, !7, i64 96, !17, i64 104, !17, i64 112, !7, i64 120, !7, i64 128, !8, i64 136, !8, i64 137, !7, i64 144, !30, i64 152} -!29 = !{!"int", !8, i64 0} -!30 = !{!"", !17, i64 0, !17, i64 8, !7, i64 16, !8, i64 24} -!31 = !{!32, !17, i64 16} -!32 = !{!"rb_control_frame_struct", !17, i64 0, !17, i64 8, !17, i64 16, !7, i64 24, !17, i64 32, !17, i64 40, !17, i64 48} -!33 = !{!32, !17, i64 32} -!34 = !DILocation(line: 0, scope: !26) -!35 = !DILocation(line: 4, column: 1, scope: !26) -!36 = !DILocation(line: 0, scope: !37, inlinedAt: !38) -!37 = distinct !DISubprogram(name: "A.", linkageName: "func_A.13L61", scope: null, file: !4, line: 4, type: !12, scopeLine: 4, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!38 = distinct !DILocation(line: 4, column: 1, scope: !26) -!39 = !DILocation(line: 5, column: 3, scope: !37, inlinedAt: !38) -!40 = !{!41, !29, i64 8} -!41 = !{!"rb_sorbet_param_struct", !42, i64 0, !29, i64 4, !29, i64 8, !29, i64 12, !29, i64 16, !29, i64 20, !29, i64 24, !29, i64 28, !17, i64 32, !29, i64 40, !29, i64 44, !29, i64 48, !29, i64 52, !17, i64 56} -!42 = !{!"", !29, i64 0, !29, i64 0, !29, i64 0, !29, i64 0, !29, i64 0, !29, i64 0, !29, i64 0, !29, i64 0, !29, i64 1, !29, i64 1} -!43 = !{!41, !29, i64 4} -!44 = !{!41, !17, i64 32} -!45 = distinct !DISubprogram(name: "A#foo", linkageName: "func_A#3foo", scope: null, file: !4, line: 5, type: !12, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!46 = !DILocation(line: 5, column: 3, scope: !45) -!47 = !{!"branch_weights", i32 1, i32 2000} -!48 = !DILocation(line: 0, scope: !45) -!49 = distinct !DISubprogram(name: "func_Object#10doubleCast.cold.1", linkageName: "func_Object#10doubleCast.cold.1", scope: null, file: !4, type: !50, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !3, retainedNodes: !5) -!50 = !DISubroutineType(types: !5) -!51 = !DILocation(line: 9, column: 3, scope: !49) diff --git a/test/testdata/compiler/send_with_block_param.opt.ll.exp b/test/testdata/compiler/send_with_block_param.opt.ll.exp deleted file mode 100644 index cc60a36163..0000000000 --- a/test/testdata/compiler/send_with_block_param.opt.ll.exp +++ /dev/null @@ -1,335 +0,0 @@ -; ModuleID = 'payload' -source_filename = "llvm-link" -target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } -%struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.__pthread_cond_s } -%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } -%union.anon = type { i64 } -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } -%struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } -%struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } -%struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } -%struct.anon.0 = type { i16, [2 x i8] } -%struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } -%struct.rb_iseq_location_struct = type { i64, i64, i64, i64, i32, %struct.rb_code_location_struct } -%struct.rb_code_location_struct = type { %struct.rb_code_position_struct, %struct.rb_code_position_struct } -%struct.rb_code_position_struct = type { i32, i32 } -%struct.iseq_insn_info = type { %struct.iseq_insn_info_entry*, i32*, i32, %struct.succ_index_table* } -%struct.iseq_insn_info_entry = type opaque -%struct.succ_index_table = type opaque -%struct.iseq_catch_table = type opaque -%union.iseq_inline_storage_entry = type { %struct.iseq_inline_cache_entry } -%struct.iseq_inline_cache_entry = type { i64, %struct.rb_cref_struct*, i64 } -%struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } -%struct.rb_scope_visi_struct = type { i8, [3 x i8] } -%struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } -%struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } -%union.anon.13 = type { %struct.rb_method_cfunc_struct } -%struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%union.anon.15 = type { i32 } -%struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.16 = type { i64, i64, i64, i64* } -%union.anon.17 = type { %struct.anon.18 } -%struct.anon.18 = type { i64, i32 } -%struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } -%struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } -%struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } -%struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } -%struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } -%struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } -%struct.__sigset_t = type { [16 x i64] } -%struct.rb_calling_info = type { i64, i64, i32, i32 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } -%union.anon.9 = type { %union.pthread_cond_t } -%struct.rb_unblock_callback = type { void (i8*)*, i8* } -%struct.rb_mutex_struct = type opaque -%struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.10 = type { %struct.anon.11 } -%struct.anon.11 = type { i64, i64, i32 } -%struct.rb_fiber_struct = type opaque -%struct.anon.5 = type { [65 x i64] } -%struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } -%struct.rb_event_hook_struct = type opaque -%struct.rb_postponed_job_struct = type opaque -%struct.list_head = type { %struct.list_node } -%union.pthread_mutex_t = type { %struct.__pthread_mutex_s } -%struct.__pthread_mutex_s = type { i32, i32, i32, i32, i32, i16, i16, %struct.__pthread_internal_list } -%struct.__pthread_internal_list = type { %struct.__pthread_internal_list*, %struct.__pthread_internal_list* } -%struct.rb_objspace = type opaque -%struct.rb_at_exit_list = type { void (%struct.rb_vm_struct*)*, %struct.rb_at_exit_list* } -%struct.st_table = type { i8, i8, i8, i32, %struct.st_hash_type*, i64, i64*, i64, i64, %struct.st_table_entry* } -%struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } -%struct.st_table_entry = type opaque -%struct.rb_builtin_function = type opaque -%struct.anon.6 = type { i64, i64, i64, i64 } -%struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } -%struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } -%struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } -%struct.rb_call_info_with_kwarg = type { %struct.rb_call_info, %struct.rb_call_info_kw_arg* } -%struct.rb_call_info_kw_arg = type { i32, [1 x i64] } - -@ruby_current_vm_ptr = external local_unnamed_addr global %struct.rb_vm_struct*, align 8 -@ruby_current_execution_context_ptr = external local_unnamed_addr global %struct.rb_execution_context_struct*, align 8 -@.str.7 = private unnamed_addr constant [8 x i8] c"to_proc\00", align 1 -@sorbet_makeBlockHandlerProc.rb_funcallv_data = internal global %struct.rb_call_data zeroinitializer, align 8 -@rb_eRuntimeError = external local_unnamed_addr global i64, align 8 -@.str.9 = private unnamed_addr constant [95 x i8] c"sorbet_getBuildSCMRevision: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@.str.10 = private unnamed_addr constant [93 x i8] c"sorbet_getIsReleaseBuild: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@"stackFramePrecomputed_func_.13" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@iseqEncodedArray = internal global [7 x i64] zeroinitializer -@fileLineNumberInfo = internal global %struct.SorbetLineNumberInfo zeroinitializer -@ruby_hashLiteral1 = internal unnamed_addr global i64 0, align 8 -@ic_map = internal global %struct.FunctionInlineCache zeroinitializer -@ic_puts = internal global %struct.FunctionInlineCache zeroinitializer -@sorbet_moduleStringTable = internal unnamed_addr constant [104 x i8] c"\00test/testdata/compiler/send_with_block_param.rb\00T\00unsafe\00\00map\00puts\00master\00", align 1 -@sorbet_moduleIDTable = internal unnamed_addr global [5 x i64] zeroinitializer, align 8 -@sorbet_moduleIDDescriptors = internal unnamed_addr constant [5 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 16 }, %struct.rb_code_position_struct { i32 67, i32 6 }, %struct.rb_code_position_struct { i32 74, i32 13 }, %struct.rb_code_position_struct { i32 88, i32 3 }, %struct.rb_code_position_struct { i32 92, i32 4 }], align 8 -@sorbet_moduleRubyStringTable = internal unnamed_addr global [2 x i64] zeroinitializer, align 8 -@sorbet_moduleRubyStringDescriptors = internal unnamed_addr constant [2 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 16 }, %struct.rb_code_position_struct { i32 17, i32 47 }], align 8 - -declare %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64, i64, i64, i64, %struct.rb_iseq_struct*, i32, i32, %struct.SorbetLineNumberInfo*, i64*, i32, i32) local_unnamed_addr #0 - -declare void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo*, i64*, i32) local_unnamed_addr #0 - -declare i64 @sorbet_readRealpath() local_unnamed_addr #0 - -declare void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) local_unnamed_addr #0 - -declare i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache*, i64) local_unnamed_addr #0 - -declare void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_iseq_struct*) local_unnamed_addr #0 - -declare i64 @sorbet_globalConstRegister(i64) local_unnamed_addr #0 - -declare i64 @sorbet_globalConstDupHash(i64) local_unnamed_addr #0 - -declare void @sorbet_vm_intern_ids(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #0 - -declare void @sorbet_vm_init_string_table(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #0 - -declare i64 @rb_intern2(i8*, i64) local_unnamed_addr #0 - -declare i64 @rb_ary_new_from_values(i64, i64*) local_unnamed_addr #0 - -declare i64 @rb_hash_new_with_size(i64) local_unnamed_addr #0 - -declare void @rb_hash_bulk_insert(i64, i64*, i64) local_unnamed_addr #0 - -; Function Attrs: noreturn -declare void @rb_raise(i64, i8*, ...) local_unnamed_addr #1 - -declare i64 @rb_funcallv_with_cc(%struct.rb_call_data*, i64, i64, i32, i64*) local_unnamed_addr #0 - -; Function Attrs: inaccessiblememonly nofree nosync nounwind willreturn -declare void @llvm.experimental.noalias.scope.decl(metadata) #2 - -declare i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct*, i32) local_unnamed_addr #0 - -; Function Attrs: nounwind ssp uwtable -define weak i32 @sorbet_getIsReleaseBuild() local_unnamed_addr #3 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([93 x i8], [93 x i8]* @.str.10, i64 0, i64 0)) #8 - unreachable -} - -; Function Attrs: nounwind ssp uwtable -define weak i8* @sorbet_getBuildSCMRevision() local_unnamed_addr #3 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([95 x i8], [95 x i8]* @.str.9, i64 0, i64 0)) #8 - unreachable -} - -define internal fastcc void @sorbet_globalConstructors(i64 %realpath) unnamed_addr { -allocRubyIds: - tail call void @sorbet_vm_intern_ids(i64* noundef getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleIDTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([5 x %struct.rb_code_position_struct], [5 x %struct.rb_code_position_struct]* @sorbet_moduleIDDescriptors, i32 0, i32 0), i32 noundef 5, i8* noundef getelementptr inbounds ([104 x i8], [104 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_vm_init_string_table(i64* noundef getelementptr inbounds ([2 x i64], [2 x i64]* @sorbet_moduleRubyStringTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([2 x %struct.rb_code_position_struct], [2 x %struct.rb_code_position_struct]* @sorbet_moduleRubyStringDescriptors, i32 0, i32 0), i32 noundef 2, i8* noundef getelementptr inbounds ([104 x i8], [104 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef getelementptr inbounds ([7 x i64], [7 x i64]* @iseqEncodedArray, i32 0, i32 0), i32 noundef 7) - tail call fastcc void @"Constr_stackFramePrecomputed_func_.13"(i64 %realpath) - tail call fastcc void @Constr_ruby_hashLiteral1() #9 - %rubyId_map = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleIDTable, i64 0, i64 3), align 8, !dbg !10, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_map, i64 %rubyId_map, i32 noundef 18, i32 noundef 0, i32 noundef 0), !dbg !10 - %rubyId_puts = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleIDTable, i64 0, i64 4), align 8, !dbg !15, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts, i64 %rubyId_puts, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !15 - ret void -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_.13"(i64 %realpath) unnamed_addr #4 { -entryInitializers: - %"rubyId_" = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleIDTable, i64 0, i64 0), align 8, !invariant.load !5 - %"rubyStr_" = load i64, i64* getelementptr inbounds ([2 x i64], [2 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 0), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/send_with_block_param.rb" = load i64, i64* getelementptr inbounds ([2 x i64], [2 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_", i64 %"rubyId_", i64 %"rubyStr_test/testdata/compiler/send_with_block_param.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 0, i32 noundef 4, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 4) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - ret void -} - -; Function Attrs: nounwind ssp -define internal fastcc void @Constr_ruby_hashLiteral1() unnamed_addr #5 { -constrHashLiteral: - %argArray = alloca [2 x i64], align 8 - %hashArgs0Addr = getelementptr [2 x i64], [2 x i64]* %argArray, i64 0, i64 0 - %0 = bitcast i64* %hashArgs0Addr to <2 x i64>* - store <2 x i64> , <2 x i64>* %0, align 8 - %1 = tail call i64 @rb_hash_new_with_size(i64 noundef 1) #9 - call void @rb_hash_bulk_insert(i64 noundef 2, i64* noundef nonnull %hashArgs0Addr, i64 %1) #9 - %2 = call i64 @sorbet_globalConstRegister(i64 %1) #9 - store i64 %2, i64* @ruby_hashLiteral1, align 8 - ret void -} - -; Function Attrs: sspreq -define void @Init_send_with_block_param() local_unnamed_addr #6 { -entry: - %callArgs.i = alloca [4 x i64], align 8 - %realpath = tail call i64 @sorbet_readRealpath() - tail call fastcc void @sorbet_globalConstructors(i64 %realpath) - %0 = load %struct.rb_vm_struct*, %struct.rb_vm_struct** @ruby_current_vm_ptr, align 8, !tbaa !16 - %1 = getelementptr inbounds %struct.rb_vm_struct, %struct.rb_vm_struct* %0, i64 0, i32 18 - %2 = load i64, i64* %1, align 8, !tbaa !18 - %3 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !16 - %4 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %3, i64 0, i32 2 - %5 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %4, align 8, !tbaa !28 - %6 = bitcast [4 x i64]* %callArgs.i to i8* - call void @llvm.lifetime.start.p0i8(i64 32, i8* nonnull %6) - %stackFrame.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - %7 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %4, align 8, !tbaa !28 - %8 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %7, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame.i, %struct.rb_iseq_struct** %8, align 8, !tbaa !31 - %9 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %7, i64 0, i32 4 - %10 = load i64*, i64** %9, align 8, !tbaa !33 - %11 = load i64, i64* %10, align 8, !tbaa !6 - %12 = and i64 %11, -33 - store i64 %12, i64* %10, align 8, !tbaa !6 - tail call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %3, %struct.rb_control_frame_struct* %7, %struct.rb_iseq_struct* %stackFrame.i) #9 - %13 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 0 - store i64* getelementptr inbounds ([7 x i64], [7 x i64]* @iseqEncodedArray, i64 0, i64 4), i64** %13, align 8, !dbg !34, !tbaa !16 - %hashLiteral.i = load i64, i64* @ruby_hashLiteral1, align 8, !dbg !35 - %duplicatedHash.i = tail call i64 @sorbet_globalConstDupHash(i64 %hashLiteral.i) #9, !dbg !35 - %callArgs0Addr.i = getelementptr [4 x i64], [4 x i64]* %callArgs.i, i64 0, i64 0, !dbg !36 - tail call void @llvm.experimental.noalias.scope.decl(metadata !37) #9, !dbg !36 - %14 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !36, !tbaa !16 - %15 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %14, i64 0, i32 5, !dbg !36 - %16 = load i32, i32* %15, align 8, !dbg !36, !tbaa !40 - %17 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %14, i64 0, i32 6, !dbg !36 - %18 = load i32, i32* %17, align 4, !dbg !36, !tbaa !41 - %19 = xor i32 %18, -1, !dbg !36 - %20 = and i32 %19, %16, !dbg !36 - %21 = icmp eq i32 %20, 0, !dbg !36 - br i1 %21, label %rb_vm_check_ints.exit.i, label %22, !dbg !36, !prof !42 - -22: ; preds = %entry - %23 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %14, i64 0, i32 8, !dbg !36 - %24 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %23, align 8, !dbg !36, !tbaa !43 - %25 = tail call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %24, i32 noundef 0) #9, !dbg !36 - br label %rb_vm_check_ints.exit.i, !dbg !36 - -rb_vm_check_ints.exit.i: ; preds = %22, %entry - store i64* getelementptr inbounds ([7 x i64], [7 x i64]* @iseqEncodedArray, i64 0, i64 6), i64** %13, align 8, !dbg !36, !tbaa !16 - store i64 3, i64* %callArgs0Addr.i, align 8, !dbg !10 - tail call void @llvm.experimental.noalias.scope.decl(metadata !44) #9, !dbg !10 - %26 = call i64 @rb_ary_new_from_values(i64 noundef 1, i64* noundef nonnull %callArgs0Addr.i) #9, !dbg !10 - %27 = icmp eq i64 %duplicatedHash.i, 8, !dbg !10 - br i1 %27, label %"func_.13.exit", label %28, !dbg !10 - -28: ; preds = %rb_vm_check_ints.exit.i - %29 = call i64 @rb_intern2(i8* noundef getelementptr inbounds ([8 x i8], [8 x i8]* @.str.7, i64 0, i64 0), i64 noundef 7) #9, !dbg !10 - %30 = call i64 @rb_funcallv_with_cc(%struct.rb_call_data* noundef nonnull @sorbet_makeBlockHandlerProc.rb_funcallv_data, i64 %duplicatedHash.i, i64 %29, i32 noundef 0, i64* noundef null) #9, !dbg !10 - br label %"func_.13.exit", !dbg !10 - -"func_.13.exit": ; preds = %rb_vm_check_ints.exit.i, %28 - %31 = phi i64 [ %30, %28 ], [ 0, %rb_vm_check_ints.exit.i ], !dbg !10 - %32 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !10 - %33 = load i64*, i64** %32, align 8, !dbg !10 - store i64 %26, i64* %33, align 8, !dbg !10, !tbaa !6 - %34 = getelementptr inbounds i64, i64* %33, i64 1, !dbg !10 - store i64* %34, i64** %32, align 8, !dbg !10 - %send.i = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* noundef @ic_map, i64 %31) #9, !dbg !10 - %35 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !15 - %36 = load i64*, i64** %35, align 8, !dbg !15 - store i64 %2, i64* %36, align 8, !dbg !15, !tbaa !6 - %37 = getelementptr inbounds i64, i64* %36, i64 1, !dbg !15 - store i64 %send.i, i64* %37, align 8, !dbg !15, !tbaa !6 - %38 = getelementptr inbounds i64, i64* %37, i64 1, !dbg !15 - store i64* %38, i64** %35, align 8, !dbg !15 - %send = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts, i64 0), !dbg !15 - call void @llvm.lifetime.end.p0i8(i64 32, i8* nonnull %6) - ret void -} - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #7 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #7 - -attributes #0 = { "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #1 = { noreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #2 = { inaccessiblememonly nofree nosync nounwind willreturn } -attributes #3 = { nounwind ssp uwtable "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #4 = { ssp } -attributes #5 = { nounwind ssp } -attributes #6 = { sspreq } -attributes #7 = { argmemonly nofree nosync nounwind willreturn } -attributes #8 = { noreturn nounwind } -attributes #9 = { nounwind } - -!llvm.module.flags = !{!0, !1, !2} -!llvm.dbg.cu = !{!3} - -!0 = !{i32 2, !"Debug Info Version", i32 3} -!1 = !{i32 4, !"cf-protection-return", i32 1} -!2 = !{i32 4, !"cf-protection-branch", i32 1} -!3 = distinct !DICompileUnit(language: DW_LANG_C, file: !4, producer: "Sorbet LLVM", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !5) -!4 = !DIFile(filename: "test/testdata/compiler/send_with_block_param.rb", directory: ".") -!5 = !{} -!6 = !{!7, !7, i64 0} -!7 = !{!"long", !8, i64 0} -!8 = !{!"omnipotent char", !9, i64 0} -!9 = !{!"Simple C/C++ TBAA"} -!10 = !DILocation(line: 6, column: 6, scope: !11) -!11 = distinct !DISubprogram(name: ".", linkageName: "func_.13", scope: null, file: !4, line: 4, type: !12, scopeLine: 4, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!12 = !DISubroutineType(types: !13) -!13 = !{!14} -!14 = !DIBasicType(name: "VALUE", size: 64, encoding: DW_ATE_signed) -!15 = !DILocation(line: 6, column: 1, scope: !11) -!16 = !{!17, !17, i64 0} -!17 = !{!"any pointer", !8, i64 0} -!18 = !{!19, !7, i64 400} -!19 = !{!"rb_vm_struct", !7, i64 0, !20, i64 8, !17, i64 192, !17, i64 200, !17, i64 208, !24, i64 216, !8, i64 224, !21, i64 264, !21, i64 280, !21, i64 296, !21, i64 312, !7, i64 328, !23, i64 336, !23, i64 340, !23, i64 344, !23, i64 344, !23, i64 344, !23, i64 344, !23, i64 348, !7, i64 352, !8, i64 360, !7, i64 400, !7, i64 408, !7, i64 416, !7, i64 424, !7, i64 432, !7, i64 440, !7, i64 448, !17, i64 456, !17, i64 464, !25, i64 472, !26, i64 992, !17, i64 1016, !17, i64 1024, !23, i64 1032, !23, i64 1036, !21, i64 1040, !8, i64 1056, !7, i64 1096, !7, i64 1104, !7, i64 1112, !7, i64 1120, !7, i64 1128, !23, i64 1136, !17, i64 1144, !17, i64 1152, !17, i64 1160, !17, i64 1168, !17, i64 1176, !17, i64 1184, !23, i64 1192, !27, i64 1200, !8, i64 1232} -!20 = !{!"rb_global_vm_lock_struct", !17, i64 0, !8, i64 8, !21, i64 48, !17, i64 64, !23, i64 72, !8, i64 80, !8, i64 128, !23, i64 176, !23, i64 180} -!21 = !{!"list_head", !22, i64 0} -!22 = !{!"list_node", !17, i64 0, !17, i64 8} -!23 = !{!"int", !8, i64 0} -!24 = !{!"long long", !8, i64 0} -!25 = !{!"", !8, i64 0} -!26 = !{!"rb_hook_list_struct", !17, i64 0, !23, i64 8, !23, i64 12, !23, i64 16} -!27 = !{!"", !7, i64 0, !7, i64 8, !7, i64 16, !7, i64 24} -!28 = !{!29, !17, i64 16} -!29 = !{!"rb_execution_context_struct", !17, i64 0, !7, i64 8, !17, i64 16, !17, i64 24, !17, i64 32, !23, i64 40, !23, i64 44, !17, i64 48, !17, i64 56, !17, i64 64, !7, i64 72, !7, i64 80, !17, i64 88, !7, i64 96, !17, i64 104, !17, i64 112, !7, i64 120, !7, i64 128, !8, i64 136, !8, i64 137, !7, i64 144, !30, i64 152} -!30 = !{!"", !17, i64 0, !17, i64 8, !7, i64 16, !8, i64 24} -!31 = !{!32, !17, i64 16} -!32 = !{!"rb_control_frame_struct", !17, i64 0, !17, i64 8, !17, i64 16, !7, i64 24, !17, i64 32, !17, i64 40, !17, i64 48} -!33 = !{!32, !17, i64 32} -!34 = !DILocation(line: 0, scope: !11) -!35 = !DILocation(line: 4, column: 14, scope: !11) -!36 = !DILocation(line: 4, column: 5, scope: !11) -!37 = !{!38} -!38 = distinct !{!38, !39, !"sorbet_T_unsafe: argument 0"} -!39 = distinct !{!39, !"sorbet_T_unsafe"} -!40 = !{!29, !23, i64 40} -!41 = !{!29, !23, i64 44} -!42 = !{!"branch_weights", i32 2000, i32 1} -!43 = !{!29, !17, i64 56} -!44 = !{!45} -!45 = distinct !{!45, !46, !"sorbet_buildArrayIntrinsic: argument 0"} -!46 = distinct !{!46, !"sorbet_buildArrayIntrinsic"} diff --git a/test/testdata/compiler/sig_failure_messages.rb b/test/testdata/compiler/sig_failure_messages.rb index 1abdc27844..f4bdda3ddb 100644 --- a/test/testdata/compiler/sig_failure_messages.rb +++ b/test/testdata/compiler/sig_failure_messages.rb @@ -10,9 +10,9 @@ def show_type_error(&blk) rescue TypeError => exception # sorbet-runtime includes three lines, something like: # - # TypeError: Parameter 'x': Expected type Integer, got type String with value "hello" - # Caller: /home/aprocter/stripe/sorbet/foo.rb:20 - # Definition: /home/aprocter/stripe/sorbet/foo.rb:16 + # TypeError Parameter 'x': Expected type Integer, got type String with value "hello" + # Caller /home/aprocter/stripe/sorbet/foo.rb:20 + # Definition /home/aprocter/stripe/sorbet/foo.rb:16 # # The compiler currently only produces the first of these lines. exception_line = exception.message.split("\n").fetch(0) diff --git a/test/testdata/compiler/sig_rewriter.opt.ll.exp b/test/testdata/compiler/sig_rewriter.opt.ll.exp deleted file mode 100644 index aeaf21d54f..0000000000 --- a/test/testdata/compiler/sig_rewriter.opt.ll.exp +++ /dev/null @@ -1,511 +0,0 @@ -; ModuleID = 'payload' -source_filename = "llvm-link" -target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } -%struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.__pthread_cond_s } -%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } -%union.anon = type { i64 } -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } -%struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } -%struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } -%struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } -%struct.anon.0 = type { i16, [2 x i8] } -%struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } -%struct.rb_iseq_location_struct = type { i64, i64, i64, i64, i32, %struct.rb_code_location_struct } -%struct.rb_code_location_struct = type { %struct.rb_code_position_struct, %struct.rb_code_position_struct } -%struct.rb_code_position_struct = type { i32, i32 } -%struct.iseq_insn_info = type { %struct.iseq_insn_info_entry*, i32*, i32, %struct.succ_index_table* } -%struct.iseq_insn_info_entry = type opaque -%struct.succ_index_table = type opaque -%struct.iseq_catch_table = type opaque -%union.iseq_inline_storage_entry = type { %struct.iseq_inline_cache_entry } -%struct.iseq_inline_cache_entry = type { i64, %struct.rb_cref_struct*, i64 } -%struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } -%struct.rb_scope_visi_struct = type { i8, [3 x i8] } -%struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } -%struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } -%union.anon.13 = type { %struct.rb_method_cfunc_struct } -%struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%union.anon.15 = type { i32 } -%struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.16 = type { i64, i64, i64, i64* } -%union.anon.17 = type { %struct.anon.18 } -%struct.anon.18 = type { i64, i32 } -%struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } -%struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } -%struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } -%struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } -%struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } -%struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } -%struct.__sigset_t = type { [16 x i64] } -%struct.rb_calling_info = type { i64, i64, i32, i32 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } -%union.anon.9 = type { %union.pthread_cond_t } -%struct.rb_unblock_callback = type { void (i8*)*, i8* } -%struct.rb_mutex_struct = type opaque -%struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.10 = type { %struct.anon.11 } -%struct.anon.11 = type { i64, i64, i32 } -%struct.rb_fiber_struct = type opaque -%struct.anon.5 = type { [65 x i64] } -%struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } -%struct.rb_event_hook_struct = type opaque -%struct.rb_postponed_job_struct = type opaque -%struct.list_head = type { %struct.list_node } -%union.pthread_mutex_t = type { %struct.__pthread_mutex_s } -%struct.__pthread_mutex_s = type { i32, i32, i32, i32, i32, i16, i16, %struct.__pthread_internal_list } -%struct.__pthread_internal_list = type { %struct.__pthread_internal_list*, %struct.__pthread_internal_list* } -%struct.rb_objspace = type opaque -%struct.rb_at_exit_list = type { void (%struct.rb_vm_struct*)*, %struct.rb_at_exit_list* } -%struct.st_table = type { i8, i8, i8, i32, %struct.st_hash_type*, i64, i64*, i64, i64, %struct.st_table_entry* } -%struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } -%struct.st_table_entry = type opaque -%struct.rb_builtin_function = type opaque -%struct.anon.6 = type { i64, i64, i64, i64 } -%struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } -%struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } -%struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } -%struct.rb_call_info_with_kwarg = type { %struct.rb_call_info, %struct.rb_call_info_kw_arg* } -%struct.rb_call_info_kw_arg = type { i32, [1 x i64] } - -@ruby_current_vm_ptr = external local_unnamed_addr global %struct.rb_vm_struct*, align 8 -@ruby_current_execution_context_ptr = external local_unnamed_addr global %struct.rb_execution_context_struct*, align 8 -@ruby_vm_global_constant_state = external local_unnamed_addr global i64, align 8 -@rb_eRuntimeError = external local_unnamed_addr global i64, align 8 -@.str.9 = private unnamed_addr constant [95 x i8] c"sorbet_getBuildSCMRevision: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@.str.10 = private unnamed_addr constant [93 x i8] c"sorbet_getIsReleaseBuild: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@"stackFramePrecomputed_func_.13" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@iseqEncodedArray = internal global [14 x i64] zeroinitializer -@fileLineNumberInfo = internal global %struct.SorbetLineNumberInfo zeroinitializer -@ic_new = internal global %struct.FunctionInlineCache zeroinitializer -@ic_initialize = internal global %struct.FunctionInlineCache zeroinitializer -@ic_foo = internal global %struct.FunctionInlineCache zeroinitializer -@ic_puts = internal global %struct.FunctionInlineCache zeroinitializer -@"stackFramePrecomputed_func_A#3foo" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"stackFramePrecomputed_func_A.13" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"stackFramePrecomputed_func_A.13$block_1" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@ic_returns = internal global %struct.FunctionInlineCache zeroinitializer -@ic_extend = internal global %struct.FunctionInlineCache zeroinitializer -@sorbet_moduleStringTable = internal unnamed_addr constant [175 x i8] c"\00test/testdata/compiler/sig_rewriter.rb\00A\00Object\00new\00initialize\00foo\00puts\00master\00Return value\00Integer\00\00block in \00returns\00T::Sig\00extend\00normal\00", align 1 -@sorbet_moduleIDTable = internal unnamed_addr global [10 x i64] zeroinitializer, align 8 -@sorbet_moduleIDDescriptors = internal unnamed_addr constant [10 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 16 }, %struct.rb_code_position_struct { i32 65, i32 3 }, %struct.rb_code_position_struct { i32 69, i32 10 }, %struct.rb_code_position_struct { i32 80, i32 3 }, %struct.rb_code_position_struct { i32 84, i32 4 }, %struct.rb_code_position_struct { i32 117, i32 9 }, %struct.rb_code_position_struct { i32 127, i32 18 }, %struct.rb_code_position_struct { i32 146, i32 7 }, %struct.rb_code_position_struct { i32 161, i32 6 }, %struct.rb_code_position_struct { i32 168, i32 6 }], align 8 -@sorbet_moduleRubyStringTable = internal unnamed_addr global [5 x i64] zeroinitializer, align 8 -@sorbet_moduleRubyStringDescriptors = internal unnamed_addr constant [5 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 16 }, %struct.rb_code_position_struct { i32 17, i32 38 }, %struct.rb_code_position_struct { i32 80, i32 3 }, %struct.rb_code_position_struct { i32 117, i32 9 }, %struct.rb_code_position_struct { i32 127, i32 18 }], align 8 -@rb_cObject = external local_unnamed_addr constant i64 -@"guard_epoch_T::Sig" = linkonce local_unnamed_addr global i64 0 -@"guarded_const_T::Sig" = linkonce local_unnamed_addr global i64 0 -@guard_epoch_A = linkonce local_unnamed_addr global i64 0 -@guarded_const_A = linkonce local_unnamed_addr global i64 0 -@rb_cInteger = external local_unnamed_addr constant i64 - -; Function Attrs: nounwind readnone willreturn -declare i64 @rb_id2sym(i64) local_unnamed_addr #0 - -; Function Attrs: noreturn -declare void @sorbet_raiseArity(i32, i32, i32) local_unnamed_addr #1 - -declare %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64, i64, i64, i64, %struct.rb_iseq_struct*, i32, i32, %struct.SorbetLineNumberInfo*, i64*, i32, i32) local_unnamed_addr #2 - -declare void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo*, i64*, i32) local_unnamed_addr #2 - -declare i64 @sorbet_getConstant(i8*, i64) local_unnamed_addr #2 - -declare i64 @sorbet_readRealpath() local_unnamed_addr #2 - -declare %struct.rb_control_frame_struct* @sorbet_pushStaticInitFrame(i64) local_unnamed_addr #2 - -declare void @sorbet_popFrame() local_unnamed_addr #2 - -declare void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) local_unnamed_addr #2 - -declare i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache*, i64) local_unnamed_addr #2 - -declare void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_iseq_struct*) local_unnamed_addr #2 - -declare void @sorbet_vm_register_sig(i64, i64, i64, i64, i64 (i64, i64, i32, i64*, i64)*) local_unnamed_addr #2 - -declare void @sorbet_vm_define_method(i64, i8*, i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)*, i8*, %struct.rb_iseq_struct*, i1 zeroext) local_unnamed_addr #2 - -declare void @sorbet_vm_intern_ids(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #2 - -declare void @sorbet_vm_init_string_table(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #2 - -declare i64 @sorbet_maybeAllocateObjectFastPath(i64, %struct.FunctionInlineCache*) local_unnamed_addr #2 - -declare i64 @rb_define_class(i8*, i64) local_unnamed_addr #2 - -; Function Attrs: noreturn -declare void @rb_raise(i64, i8*, ...) local_unnamed_addr #1 - -; Function Attrs: allocsize(0,1) -declare noalias nonnull i8* @ruby_xcalloc(i64, i64) local_unnamed_addr #3 - -; Function Attrs: nounwind ssp uwtable -define weak i32 @sorbet_getIsReleaseBuild() local_unnamed_addr #4 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([93 x i8], [93 x i8]* @.str.10, i64 0, i64 0)) #10 - unreachable -} - -; Function Attrs: nounwind ssp uwtable -define weak i8* @sorbet_getBuildSCMRevision() local_unnamed_addr #4 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([95 x i8], [95 x i8]* @.str.9, i64 0, i64 0)) #10 - unreachable -} - -define internal fastcc void @sorbet_globalConstructors(i64 %realpath) unnamed_addr { -allocRubyIds: - tail call void @sorbet_vm_intern_ids(i64* noundef getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([10 x %struct.rb_code_position_struct], [10 x %struct.rb_code_position_struct]* @sorbet_moduleIDDescriptors, i32 0, i32 0), i32 noundef 10, i8* noundef getelementptr inbounds ([175 x i8], [175 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_vm_init_string_table(i64* noundef getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleRubyStringTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([5 x %struct.rb_code_position_struct], [5 x %struct.rb_code_position_struct]* @sorbet_moduleRubyStringDescriptors, i32 0, i32 0), i32 noundef 5, i8* noundef getelementptr inbounds ([175 x i8], [175 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef getelementptr inbounds ([14 x i64], [14 x i64]* @iseqEncodedArray, i32 0, i32 0), i32 noundef 14) - tail call fastcc void @"Constr_stackFramePrecomputed_func_.13"(i64 %realpath) - %rubyId_new = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 1), align 8, !dbg !10, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_new, i64 %rubyId_new, i32 noundef 16, i32 noundef 0, i32 noundef 0), !dbg !10 - %rubyId_initialize = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 2), align 8, !dbg !10, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_initialize, i64 %rubyId_initialize, i32 noundef 20, i32 noundef 0, i32 noundef 0), !dbg !10 - %rubyId_foo = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 3), align 8, !dbg !10, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_foo, i64 %rubyId_foo, i32 noundef 16, i32 noundef 0, i32 noundef 0), !dbg !10 - %rubyId_puts = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 4), align 8, !dbg !15, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts, i64 %rubyId_puts, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !15 - tail call fastcc void @"Constr_stackFramePrecomputed_func_A#3foo"(i64 %realpath) - tail call fastcc void @"Constr_stackFramePrecomputed_func_A.13"(i64 %realpath) - tail call fastcc void @"Constr_stackFramePrecomputed_func_A.13$block_1"(i64 %realpath) - %rubyId_returns = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 7), align 8, !dbg !16, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_returns, i64 %rubyId_returns, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !16 - %rubyId_extend = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 8), align 8, !dbg !19, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_extend, i64 %rubyId_extend, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !19 - ret void -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_.13"(i64 %realpath) unnamed_addr #5 { -entryInitializers: - %"rubyId_" = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 0), align 8, !invariant.load !5 - %"rubyStr_" = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 0), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/sig_rewriter.rb" = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_", i64 %"rubyId_", i64 %"rubyStr_test/testdata/compiler/sig_rewriter.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 0, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 2) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - ret void -} - -; Function Attrs: sspreq -define void @Init_sig_rewriter() local_unnamed_addr #6 { -entry: - %realpath = tail call i64 @sorbet_readRealpath() - tail call fastcc void @sorbet_globalConstructors(i64 %realpath) - %0 = load %struct.rb_vm_struct*, %struct.rb_vm_struct** @ruby_current_vm_ptr, align 8, !tbaa !20 - %1 = getelementptr inbounds %struct.rb_vm_struct, %struct.rb_vm_struct* %0, i64 0, i32 18 - %2 = load i64, i64* %1, align 8, !tbaa !22 - %3 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !20 - %4 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %3, i64 0, i32 2 - %5 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %4, align 8, !tbaa !32 - %stackFrame.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - %6 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame.i, %struct.rb_iseq_struct** %6, align 8, !tbaa !35 - %7 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 4 - %8 = load i64*, i64** %7, align 8, !tbaa !37 - %9 = load i64, i64* %8, align 8, !tbaa !6 - %10 = and i64 %9, -33 - store i64 %10, i64* %8, align 8, !tbaa !6 - tail call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %3, %struct.rb_control_frame_struct* %5, %struct.rb_iseq_struct* %stackFrame.i) #11 - %11 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 0 - store i64* getelementptr inbounds ([14 x i64], [14 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %11, align 8, !dbg !38, !tbaa !20 - %12 = load i64, i64* @rb_cObject, align 8, !dbg !39 - %13 = tail call i64 @rb_define_class(i8* getelementptr inbounds ([175 x i8], [175 x i8]* @sorbet_moduleStringTable, i64 0, i64 56), i64 %12) #11, !dbg !39 - %14 = tail call %struct.rb_control_frame_struct* @sorbet_pushStaticInitFrame(i64 %13) #11, !dbg !39 - %stackFrame.i.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_A.13", align 8 - %15 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !20 - %16 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %15, i64 0, i32 2 - %17 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %16, align 8, !tbaa !32 - %18 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %17, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame.i.i, %struct.rb_iseq_struct** %18, align 8, !tbaa !35 - %19 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %17, i64 0, i32 4 - %20 = load i64*, i64** %19, align 8, !tbaa !37 - %21 = load i64, i64* %20, align 8, !tbaa !6 - %22 = and i64 %21, -33 - store i64 %22, i64* %20, align 8, !tbaa !6 - tail call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %15, %struct.rb_control_frame_struct* %17, %struct.rb_iseq_struct* %stackFrame.i.i) #11 - %23 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %14, i64 0, i32 0 - store i64* getelementptr inbounds ([14 x i64], [14 x i64]* @iseqEncodedArray, i64 0, i64 7), i64** %23, align 8, !dbg !40, !tbaa !20 - %rubyId_foo.i.i = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 3), align 8, !dbg !42, !invariant.load !5 - %rawSym.i.i = tail call i64 @rb_id2sym(i64 %rubyId_foo.i.i) #11, !dbg !42 - tail call void @sorbet_vm_register_sig(i64 noundef 0, i64 %rawSym.i.i, i64 %13, i64 noundef 8, i64 (i64, i64, i32, i64*, i64)* noundef @"func_A.13L62$block_1") #11, !dbg !42 - store i64* getelementptr inbounds ([14 x i64], [14 x i64]* @iseqEncodedArray, i64 0, i64 6), i64** %23, align 8, !dbg !42, !tbaa !20 - %24 = load i64, i64* @"guard_epoch_T::Sig", align 8, !dbg !43 - %25 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !43, !tbaa !44 - %needTakeSlowPath = icmp ne i64 %24, %25, !dbg !43 - br i1 %needTakeSlowPath, label %26, label %27, !dbg !43, !prof !45 - -26: ; preds = %entry - tail call void @"const_recompute_T::Sig"(), !dbg !43 - br label %27, !dbg !43 - -27: ; preds = %entry, %26 - %28 = load i64, i64* @"guarded_const_T::Sig", align 8, !dbg !43 - %29 = load i64, i64* @"guard_epoch_T::Sig", align 8, !dbg !43 - %30 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !43, !tbaa !44 - %guardUpdated = icmp eq i64 %29, %30, !dbg !43 - tail call void @llvm.assume(i1 %guardUpdated), !dbg !43 - %31 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %14, i64 0, i32 1, !dbg !43 - %32 = load i64*, i64** %31, align 8, !dbg !43 - store i64 %13, i64* %32, align 8, !dbg !43, !tbaa !6 - %33 = getelementptr inbounds i64, i64* %32, i64 1, !dbg !43 - store i64 %28, i64* %33, align 8, !dbg !43, !tbaa !6 - %34 = getelementptr inbounds i64, i64* %33, i64 1, !dbg !43 - store i64* %34, i64** %31, align 8, !dbg !43 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_extend, i64 0), !dbg !43 - store i64* getelementptr inbounds ([14 x i64], [14 x i64]* @iseqEncodedArray, i64 0, i64 8), i64** %23, align 8, !dbg !43, !tbaa !20 - %35 = load i64, i64* @guard_epoch_A, align 8, !dbg !46 - %36 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !46, !tbaa !44 - %needTakeSlowPath1 = icmp ne i64 %35, %36, !dbg !46 - br i1 %needTakeSlowPath1, label %37, label %38, !dbg !46, !prof !45 - -37: ; preds = %27 - tail call void @const_recompute_A(), !dbg !46 - br label %38, !dbg !46 - -38: ; preds = %27, %37 - %39 = load i64, i64* @guarded_const_A, align 8, !dbg !46 - %40 = load i64, i64* @guard_epoch_A, align 8, !dbg !46 - %41 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !46, !tbaa !44 - %guardUpdated2 = icmp eq i64 %40, %41, !dbg !46 - tail call void @llvm.assume(i1 %guardUpdated2), !dbg !46 - %stackFrame42.i.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_A#3foo", align 8, !dbg !46 - %42 = tail call noalias nonnull i8* @ruby_xcalloc(i64 noundef 1, i64 noundef 64) #12, !dbg !46 - %43 = bitcast i8* %42 to i16*, !dbg !46 - %44 = load i16, i16* %43, align 8, !dbg !46 - %45 = and i16 %44, -384, !dbg !46 - store i16 %45, i16* %43, align 8, !dbg !46 - %46 = getelementptr inbounds i8, i8* %42, i64 4, !dbg !46 - tail call void @llvm.memset.p0i8.i64(i8* nonnull align 4 %46, i8 0, i64 28, i1 false) #11, !dbg !46 - tail call void @sorbet_vm_define_method(i64 %39, i8* getelementptr inbounds ([175 x i8], [175 x i8]* @sorbet_moduleStringTable, i64 0, i64 80), i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*, i8*)* noundef @"func_A#3foo", i8* nonnull %42, %struct.rb_iseq_struct* %stackFrame42.i.i, i1 noundef zeroext false) #11, !dbg !46 - tail call void @sorbet_popFrame() #11, !dbg !39 - store i64* getelementptr inbounds ([14 x i64], [14 x i64]* @iseqEncodedArray, i64 0, i64 13), i64** %11, align 8, !dbg !39, !tbaa !20 - %47 = tail call i64 @sorbet_maybeAllocateObjectFastPath(i64 %39, %struct.FunctionInlineCache* noundef @ic_new) #11, !dbg !10 - %48 = icmp eq i64 %47, 52, !dbg !10 - br i1 %48, label %slowNew.i, label %fastNew.i, !dbg !10 - -slowNew.i: ; preds = %38 - %49 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !10 - %50 = load i64*, i64** %49, align 8, !dbg !10 - store i64 %39, i64* %50, align 8, !dbg !10, !tbaa !6 - %51 = getelementptr inbounds i64, i64* %50, i64 1, !dbg !10 - store i64* %51, i64** %49, align 8, !dbg !10 - %52 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* noundef @ic_new, i64 noundef 0) #11, !dbg !10 - br label %"func_.13.exit", !dbg !10 - -fastNew.i: ; preds = %38 - %53 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !10 - %54 = load i64*, i64** %53, align 8, !dbg !10 - store i64 %47, i64* %54, align 8, !dbg !10, !tbaa !6 - %55 = getelementptr inbounds i64, i64* %54, i64 1, !dbg !10 - store i64* %55, i64** %53, align 8, !dbg !10 - %56 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* noundef @ic_initialize, i64 noundef 0) #11, !dbg !10 - br label %"func_.13.exit", !dbg !10 - -"func_.13.exit": ; preds = %slowNew.i, %fastNew.i - %initializedObject.i = phi i64 [ %52, %slowNew.i ], [ %47, %fastNew.i ], !dbg !10 - %57 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !10 - %58 = load i64*, i64** %57, align 8, !dbg !10 - store i64 %initializedObject.i, i64* %58, align 8, !dbg !10, !tbaa !6 - %59 = getelementptr inbounds i64, i64* %58, i64 1, !dbg !10 - store i64* %59, i64** %57, align 8, !dbg !10 - %send4 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_foo, i64 0), !dbg !10 - %60 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !15 - %61 = load i64*, i64** %60, align 8, !dbg !15 - store i64 %2, i64* %61, align 8, !dbg !15, !tbaa !6 - %62 = getelementptr inbounds i64, i64* %61, i64 1, !dbg !15 - store i64 %send4, i64* %62, align 8, !dbg !15, !tbaa !6 - %63 = getelementptr inbounds i64, i64* %62, i64 1, !dbg !15 - store i64* %63, i64** %60, align 8, !dbg !15 - %send6 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts, i64 0), !dbg !15 - ret void -} - -; Function Attrs: nounwind sspreq uwtable -define internal noundef i64 @"func_A#3foo"(i32 %argc, i64* nocapture nofree readnone %argArray, i64 %selfRaw, %struct.rb_control_frame_struct* nocapture nonnull writeonly align 8 dereferenceable(8) %cfp, i8* nocapture nofree readnone %calling, i8* nocapture nofree readnone %callData) #7 !dbg !47 { -functionEntryInitializers: - %0 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 0 - store i64* getelementptr inbounds ([14 x i64], [14 x i64]* @iseqEncodedArray, i64 0, i64 8), i64** %0, align 8, !tbaa !20 - %tooManyArgs = icmp ugt i32 %argc, 0, !dbg !48 - br i1 %tooManyArgs, label %argCountFailBlock, label %fillRequiredArgs, !dbg !48, !prof !49 - -argCountFailBlock: ; preds = %functionEntryInitializers - tail call void @sorbet_raiseArity(i32 %argc, i32 noundef 0, i32 noundef 0) #13, !dbg !48 - unreachable, !dbg !48 - -fillRequiredArgs: ; preds = %functionEntryInitializers - store i64* getelementptr inbounds ([14 x i64], [14 x i64]* @iseqEncodedArray, i64 0, i64 9), i64** %0, align 8, !dbg !50, !tbaa !20 - ret i64 183 -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_A#3foo"(i64 %realpath) unnamed_addr #5 { -entryInitializers: - %rubyId_foo = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 3), align 8, !invariant.load !5 - %rubyStr_foo = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 2), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/sig_rewriter.rb" = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %rubyStr_foo, i64 %rubyId_foo, i64 %"rubyStr_test/testdata/compiler/sig_rewriter.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 1, i32 noundef 8, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 0) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_A#3foo", align 8 - ret void -} - -; Function Attrs: ssp -define internal i64 @"func_A.13L62$block_1"(i64 %firstYieldArgRaw, i64 %localsOffset, i32 %argc, i64* nocapture nofree readnone %argArray, i64 %blockArg) #5 !dbg !17 { -functionEntryInitializers: - %0 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !20 - %1 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %0, i64 0, i32 2 - %2 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %1, align 8, !tbaa !32 - %3 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 3 - %4 = load i64, i64* %3, align 8, !tbaa !51 - %stackFrame = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_A.13$block_1", align 8 - %5 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame, %struct.rb_iseq_struct** %5, align 8, !tbaa !35 - %6 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 4 - %7 = load i64*, i64** %6, align 8, !tbaa !37 - %8 = load i64, i64* %7, align 8, !tbaa !6 - %9 = and i64 %8, -129 - store i64 %9, i64* %7, align 8, !tbaa !6 - %10 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 0 - store i64* getelementptr inbounds ([14 x i64], [14 x i64]* @iseqEncodedArray, i64 0, i64 7), i64** %10, align 8, !tbaa !20 - %11 = load i64, i64* @rb_cInteger, align 8, !dbg !16 - %12 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 1, !dbg !16 - %13 = load i64*, i64** %12, align 8, !dbg !16 - store i64 %4, i64* %13, align 8, !dbg !16, !tbaa !6 - %14 = getelementptr inbounds i64, i64* %13, i64 1, !dbg !16 - store i64 %11, i64* %14, align 8, !dbg !16, !tbaa !6 - %15 = getelementptr inbounds i64, i64* %14, i64 1, !dbg !16 - store i64* %15, i64** %12, align 8, !dbg !16 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_returns, i64 0), !dbg !16 - ret i64 %send, !dbg !16 -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_A.13"(i64 %realpath) unnamed_addr #5 { -entryInitializers: - %"rubyId_" = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 5), align 8, !invariant.load !5 - %"rubyStr_" = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 3), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/sig_rewriter.rb" = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_", i64 %"rubyId_", i64 %"rubyStr_test/testdata/compiler/sig_rewriter.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 3, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 4) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_A.13", align 8 - ret void -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_A.13$block_1"(i64 %realpath) unnamed_addr #5 { -entryInitializers: - %stackFrame = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_A.13", align 8 - %"rubyId_block in " = load i64, i64* getelementptr inbounds ([10 x i64], [10 x i64]* @sorbet_moduleIDTable, i64 0, i64 6), align 8, !invariant.load !5 - %"rubyStr_block in " = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 4), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/sig_rewriter.rb" = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %0 = tail call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_block in ", i64 %"rubyId_block in ", i64 %"rubyStr_test/testdata/compiler/sig_rewriter.rb", i64 %realpath, %struct.rb_iseq_struct* %stackFrame, i32 noundef 2, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef null, i32 noundef 0, i32 noundef 4) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_A.13$block_1", align 8 - ret void -} - -; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly -declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #8 - -; Function Attrs: nofree nosync nounwind willreturn -declare void @llvm.assume(i1 noundef) #9 - -; Function Attrs: ssp -define linkonce void @"const_recompute_T::Sig"() local_unnamed_addr #5 { - %1 = tail call i64 @sorbet_getConstant(i8* getelementptr inbounds ([175 x i8], [175 x i8]* @sorbet_moduleStringTable, i64 0, i64 154), i64 6) - store i64 %1, i64* @"guarded_const_T::Sig", align 8 - %2 = load i64, i64* @ruby_vm_global_constant_state, align 8, !tbaa !44 - store i64 %2, i64* @"guard_epoch_T::Sig", align 8 - ret void -} - -; Function Attrs: ssp -define linkonce void @const_recompute_A() local_unnamed_addr #5 { - %1 = tail call i64 @sorbet_getConstant(i8* getelementptr inbounds ([175 x i8], [175 x i8]* @sorbet_moduleStringTable, i64 0, i64 56), i64 1) - store i64 %1, i64* @guarded_const_A, align 8 - %2 = load i64, i64* @ruby_vm_global_constant_state, align 8, !tbaa !44 - store i64 %2, i64* @guard_epoch_A, align 8 - ret void -} - -attributes #0 = { nounwind readnone willreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #1 = { noreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #2 = { "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #3 = { allocsize(0,1) "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #4 = { nounwind ssp uwtable "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #5 = { ssp } -attributes #6 = { sspreq } -attributes #7 = { nounwind sspreq uwtable } -attributes #8 = { argmemonly nofree nosync nounwind willreturn writeonly } -attributes #9 = { nofree nosync nounwind willreturn } -attributes #10 = { noreturn nounwind } -attributes #11 = { nounwind } -attributes #12 = { nounwind allocsize(0,1) } -attributes #13 = { noreturn } - -!llvm.module.flags = !{!0, !1, !2} -!llvm.dbg.cu = !{!3} - -!0 = !{i32 2, !"Debug Info Version", i32 3} -!1 = !{i32 4, !"cf-protection-return", i32 1} -!2 = !{i32 4, !"cf-protection-branch", i32 1} -!3 = distinct !DICompileUnit(language: DW_LANG_C, file: !4, producer: "Sorbet LLVM", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !5) -!4 = !DIFile(filename: "test/testdata/compiler/sig_rewriter.rb", directory: ".") -!5 = !{} -!6 = !{!7, !7, i64 0} -!7 = !{!"long", !8, i64 0} -!8 = !{!"omnipotent char", !9, i64 0} -!9 = !{!"Simple C/C++ TBAA"} -!10 = !DILocation(line: 13, column: 6, scope: !11) -!11 = distinct !DISubprogram(name: ".", linkageName: "func_.13", scope: null, file: !4, line: 5, type: !12, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!12 = !DISubroutineType(types: !13) -!13 = !{!14} -!14 = !DIBasicType(name: "VALUE", size: 64, encoding: DW_ATE_signed) -!15 = !DILocation(line: 13, column: 1, scope: !11) -!16 = !DILocation(line: 7, column: 8, scope: !17) -!17 = distinct !DISubprogram(name: "A.", linkageName: "func_A.13L62$block_1", scope: !18, file: !4, line: 5, type: !12, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!18 = distinct !DISubprogram(name: "A.", linkageName: "func_A.13L62", scope: null, file: !4, line: 5, type: !12, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!19 = !DILocation(line: 6, column: 3, scope: !18) -!20 = !{!21, !21, i64 0} -!21 = !{!"any pointer", !8, i64 0} -!22 = !{!23, !7, i64 400} -!23 = !{!"rb_vm_struct", !7, i64 0, !24, i64 8, !21, i64 192, !21, i64 200, !21, i64 208, !28, i64 216, !8, i64 224, !25, i64 264, !25, i64 280, !25, i64 296, !25, i64 312, !7, i64 328, !27, i64 336, !27, i64 340, !27, i64 344, !27, i64 344, !27, i64 344, !27, i64 344, !27, i64 348, !7, i64 352, !8, i64 360, !7, i64 400, !7, i64 408, !7, i64 416, !7, i64 424, !7, i64 432, !7, i64 440, !7, i64 448, !21, i64 456, !21, i64 464, !29, i64 472, !30, i64 992, !21, i64 1016, !21, i64 1024, !27, i64 1032, !27, i64 1036, !25, i64 1040, !8, i64 1056, !7, i64 1096, !7, i64 1104, !7, i64 1112, !7, i64 1120, !7, i64 1128, !27, i64 1136, !21, i64 1144, !21, i64 1152, !21, i64 1160, !21, i64 1168, !21, i64 1176, !21, i64 1184, !27, i64 1192, !31, i64 1200, !8, i64 1232} -!24 = !{!"rb_global_vm_lock_struct", !21, i64 0, !8, i64 8, !25, i64 48, !21, i64 64, !27, i64 72, !8, i64 80, !8, i64 128, !27, i64 176, !27, i64 180} -!25 = !{!"list_head", !26, i64 0} -!26 = !{!"list_node", !21, i64 0, !21, i64 8} -!27 = !{!"int", !8, i64 0} -!28 = !{!"long long", !8, i64 0} -!29 = !{!"", !8, i64 0} -!30 = !{!"rb_hook_list_struct", !21, i64 0, !27, i64 8, !27, i64 12, !27, i64 16} -!31 = !{!"", !7, i64 0, !7, i64 8, !7, i64 16, !7, i64 24} -!32 = !{!33, !21, i64 16} -!33 = !{!"rb_execution_context_struct", !21, i64 0, !7, i64 8, !21, i64 16, !21, i64 24, !21, i64 32, !27, i64 40, !27, i64 44, !21, i64 48, !21, i64 56, !21, i64 64, !7, i64 72, !7, i64 80, !21, i64 88, !7, i64 96, !21, i64 104, !21, i64 112, !7, i64 120, !7, i64 128, !8, i64 136, !8, i64 137, !7, i64 144, !34, i64 152} -!34 = !{!"", !21, i64 0, !21, i64 8, !7, i64 16, !8, i64 24} -!35 = !{!36, !21, i64 16} -!36 = !{!"rb_control_frame_struct", !21, i64 0, !21, i64 8, !21, i64 16, !7, i64 24, !21, i64 32, !21, i64 40, !21, i64 48} -!37 = !{!36, !21, i64 32} -!38 = !DILocation(line: 0, scope: !11) -!39 = !DILocation(line: 5, column: 1, scope: !11) -!40 = !DILocation(line: 0, scope: !18, inlinedAt: !41) -!41 = distinct !DILocation(line: 5, column: 1, scope: !11) -!42 = !DILocation(line: 7, column: 3, scope: !18, inlinedAt: !41) -!43 = !DILocation(line: 6, column: 3, scope: !18, inlinedAt: !41) -!44 = !{!28, !28, i64 0} -!45 = !{!"branch_weights", i32 1, i32 10000} -!46 = !DILocation(line: 8, column: 3, scope: !18, inlinedAt: !41) -!47 = distinct !DISubprogram(name: "A#foo", linkageName: "func_A#3foo", scope: null, file: !4, line: 8, type: !12, scopeLine: 8, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!48 = !DILocation(line: 8, column: 3, scope: !47) -!49 = !{!"branch_weights", i32 1, i32 2000} -!50 = !DILocation(line: 0, scope: !47) -!51 = !{!36, !7, i64 24} diff --git a/test/testdata/compiler/splat_assign.opt.ll.exp b/test/testdata/compiler/splat_assign.opt.ll.exp deleted file mode 100644 index 3dabccb56a..0000000000 --- a/test/testdata/compiler/splat_assign.opt.ll.exp +++ /dev/null @@ -1,575 +0,0 @@ -; ModuleID = 'payload' -source_filename = "llvm-link" -target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } -%struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.__pthread_cond_s } -%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } -%union.anon = type { i64 } -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } -%struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } -%struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } -%struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } -%struct.anon.0 = type { i16, [2 x i8] } -%struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } -%struct.rb_iseq_location_struct = type { i64, i64, i64, i64, i32, %struct.rb_code_location_struct } -%struct.rb_code_location_struct = type { %struct.rb_code_position_struct, %struct.rb_code_position_struct } -%struct.rb_code_position_struct = type { i32, i32 } -%struct.iseq_insn_info = type { %struct.iseq_insn_info_entry*, i32*, i32, %struct.succ_index_table* } -%struct.iseq_insn_info_entry = type opaque -%struct.succ_index_table = type opaque -%struct.iseq_catch_table = type opaque -%union.iseq_inline_storage_entry = type { %struct.iseq_inline_cache_entry } -%struct.iseq_inline_cache_entry = type { i64, %struct.rb_cref_struct*, i64 } -%struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } -%struct.rb_scope_visi_struct = type { i8, [3 x i8] } -%struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } -%struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } -%union.anon.13 = type { %struct.rb_method_cfunc_struct } -%struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%union.anon.15 = type { i32 } -%struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.16 = type { i64, i64, i64, i64* } -%union.anon.17 = type { %struct.anon.18 } -%struct.anon.18 = type { i64, i32 } -%struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } -%struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } -%struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } -%struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } -%struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } -%struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } -%struct.__sigset_t = type { [16 x i64] } -%struct.rb_calling_info = type { i64, i64, i32, i32 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } -%union.anon.9 = type { %union.pthread_cond_t } -%struct.rb_unblock_callback = type { void (i8*)*, i8* } -%struct.rb_mutex_struct = type opaque -%struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.10 = type { %struct.anon.11 } -%struct.anon.11 = type { i64, i64, i32 } -%struct.rb_fiber_struct = type opaque -%struct.anon.5 = type { [65 x i64] } -%struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } -%struct.rb_event_hook_struct = type opaque -%struct.rb_postponed_job_struct = type opaque -%struct.list_head = type { %struct.list_node } -%union.pthread_mutex_t = type { %struct.__pthread_mutex_s } -%struct.__pthread_mutex_s = type { i32, i32, i32, i32, i32, i16, i16, %struct.__pthread_internal_list } -%struct.__pthread_internal_list = type { %struct.__pthread_internal_list*, %struct.__pthread_internal_list* } -%struct.rb_objspace = type opaque -%struct.rb_at_exit_list = type { void (%struct.rb_vm_struct*)*, %struct.rb_at_exit_list* } -%struct.st_table = type { i8, i8, i8, i32, %struct.st_hash_type*, i64, i64*, i64, i64, %struct.st_table_entry* } -%struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } -%struct.st_table_entry = type opaque -%struct.rb_builtin_function = type opaque -%struct.anon.6 = type { i64, i64, i64, i64 } -%struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } -%struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } -%struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } -%struct.rb_call_info_with_kwarg = type { %struct.rb_call_info, %struct.rb_call_info_kw_arg* } -%struct.rb_call_info_kw_arg = type { i32, [1 x i64] } - -@ruby_current_vm_ptr = external local_unnamed_addr global %struct.rb_vm_struct*, align 8 -@ruby_current_execution_context_ptr = external local_unnamed_addr global %struct.rb_execution_context_struct*, align 8 -@rb_eRuntimeError = external local_unnamed_addr global i64, align 8 -@.str.9 = private unnamed_addr constant [95 x i8] c"sorbet_getBuildSCMRevision: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@.str.10 = private unnamed_addr constant [93 x i8] c"sorbet_getIsReleaseBuild: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@"stackFramePrecomputed_func_.13" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@iseqEncodedArray = internal global [13 x i64] zeroinitializer -@fileLineNumberInfo = internal global %struct.SorbetLineNumberInfo zeroinitializer -@ic_puts = internal global %struct.FunctionInlineCache zeroinitializer -@ic_puts.6 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_puts.10 = internal global %struct.FunctionInlineCache zeroinitializer -@sorbet_moduleStringTable = internal unnamed_addr constant [100 x i8] c"\00test/testdata/compiler/splat_assign.rb\00\00\00[]\00puts\00master\00", align 1 -@sorbet_moduleIDTable = internal unnamed_addr global [5 x i64] zeroinitializer, align 8 -@sorbet_moduleIDDescriptors = internal unnamed_addr constant [5 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 16 }, %struct.rb_code_position_struct { i32 56, i32 13 }, %struct.rb_code_position_struct { i32 70, i32 14 }, %struct.rb_code_position_struct { i32 85, i32 2 }, %struct.rb_code_position_struct { i32 88, i32 4 }], align 8 -@sorbet_moduleRubyStringTable = internal unnamed_addr global [2 x i64] zeroinitializer, align 8 -@sorbet_moduleRubyStringDescriptors = internal unnamed_addr constant [2 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 16 }, %struct.rb_code_position_struct { i32 17, i32 38 }], align 8 - -declare %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64, i64, i64, i64, %struct.rb_iseq_struct*, i32, i32, %struct.SorbetLineNumberInfo*, i64*, i32, i32) local_unnamed_addr #0 - -declare void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo*, i64*, i32) local_unnamed_addr #0 - -declare i64 @sorbet_readRealpath() local_unnamed_addr #0 - -declare void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) local_unnamed_addr #0 - -declare i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache*, i64) local_unnamed_addr #0 - -declare void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_iseq_struct*) local_unnamed_addr #0 - -declare i64 @sorbet_vm_expandSplatIntrinsic(i64, i64, i64) local_unnamed_addr #0 - -declare void @sorbet_vm_intern_ids(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #0 - -declare void @sorbet_vm_init_string_table(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #0 - -declare i64 @rb_ary_new_from_values(i64, i64*) local_unnamed_addr #0 - -; Function Attrs: noreturn -declare void @rb_raise(i64, i8*, ...) local_unnamed_addr #1 - -declare i64 @rb_ary_entry(i64, i64) local_unnamed_addr #0 - -; Function Attrs: inaccessiblememonly nofree nosync nounwind willreturn -declare void @llvm.experimental.noalias.scope.decl(metadata) #2 - -declare i64 @rb_ary_new() local_unnamed_addr #0 - -declare i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct*, i32) local_unnamed_addr #0 - -; Function Attrs: nounwind ssp uwtable -define weak i32 @sorbet_getIsReleaseBuild() local_unnamed_addr #3 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([93 x i8], [93 x i8]* @.str.10, i64 0, i64 0)) #7 - unreachable -} - -; Function Attrs: nounwind ssp uwtable -define weak i8* @sorbet_getBuildSCMRevision() local_unnamed_addr #3 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([95 x i8], [95 x i8]* @.str.9, i64 0, i64 0)) #7 - unreachable -} - -define internal fastcc void @sorbet_globalConstructors(i64 %realpath) unnamed_addr { -allocRubyIds: - tail call void @sorbet_vm_intern_ids(i64* noundef getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleIDTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([5 x %struct.rb_code_position_struct], [5 x %struct.rb_code_position_struct]* @sorbet_moduleIDDescriptors, i32 0, i32 0), i32 noundef 5, i8* noundef getelementptr inbounds ([100 x i8], [100 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_vm_init_string_table(i64* noundef getelementptr inbounds ([2 x i64], [2 x i64]* @sorbet_moduleRubyStringTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([2 x %struct.rb_code_position_struct], [2 x %struct.rb_code_position_struct]* @sorbet_moduleRubyStringDescriptors, i32 0, i32 0), i32 noundef 2, i8* noundef getelementptr inbounds ([100 x i8], [100 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef getelementptr inbounds ([13 x i64], [13 x i64]* @iseqEncodedArray, i32 0, i32 0), i32 noundef 13) - tail call fastcc void @"Constr_stackFramePrecomputed_func_.13"(i64 %realpath) - %rubyId_puts = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleIDTable, i64 0, i64 4), align 8, !dbg !10, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts, i64 %rubyId_puts, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !10 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts.6, i64 %rubyId_puts, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !15 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts.10, i64 %rubyId_puts, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !16 - ret void -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_.13"(i64 %realpath) unnamed_addr #4 { -entryInitializers: - %"rubyId_" = load i64, i64* getelementptr inbounds ([5 x i64], [5 x i64]* @sorbet_moduleIDTable, i64 0, i64 0), align 8, !invariant.load !5 - %"rubyStr_" = load i64, i64* getelementptr inbounds ([2 x i64], [2 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 0), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/splat_assign.rb" = load i64, i64* getelementptr inbounds ([2 x i64], [2 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_", i64 %"rubyId_", i64 %"rubyStr_test/testdata/compiler/splat_assign.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 0, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 8) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - ret void -} - -; Function Attrs: sspreq -define void @Init_splat_assign() local_unnamed_addr #5 { -entry: - %callArgs.i = alloca [8 x i64], align 8 - %realpath = tail call i64 @sorbet_readRealpath() - tail call fastcc void @sorbet_globalConstructors(i64 %realpath) - %0 = load %struct.rb_vm_struct*, %struct.rb_vm_struct** @ruby_current_vm_ptr, align 8, !tbaa !17 - %1 = getelementptr inbounds %struct.rb_vm_struct, %struct.rb_vm_struct* %0, i64 0, i32 18 - %2 = load i64, i64* %1, align 8, !tbaa !19 - %3 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !17 - %4 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %3, i64 0, i32 2 - %5 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %4, align 8, !tbaa !29 - %6 = bitcast [8 x i64]* %callArgs.i to i8* - call void @llvm.lifetime.start.p0i8(i64 64, i8* nonnull %6) - %stackFrame.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - %7 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %4, align 8, !tbaa !29 - %8 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %7, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame.i, %struct.rb_iseq_struct** %8, align 8, !tbaa !32 - %9 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %7, i64 0, i32 4 - %10 = load i64*, i64** %9, align 8, !tbaa !34 - %11 = load i64, i64* %10, align 8, !tbaa !6 - %12 = and i64 %11, -33 - store i64 %12, i64* %10, align 8, !tbaa !6 - tail call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %3, %struct.rb_control_frame_struct* %7, %struct.rb_iseq_struct* %stackFrame.i) #8 - %13 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 0 - store i64* getelementptr inbounds ([13 x i64], [13 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %13, align 8, !dbg !35, !tbaa !17 - %callArgs0Addr.i = getelementptr [8 x i64], [8 x i64]* %callArgs.i, i64 0, i64 0, !dbg !36 - %callArgs1Addr.i = getelementptr [8 x i64], [8 x i64]* %callArgs.i, i64 0, i64 1, !dbg !36 - %callArgs2Addr.i = getelementptr [8 x i64], [8 x i64]* %callArgs.i, i64 0, i64 2, !dbg !36 - %14 = bitcast i64* %callArgs0Addr.i to <4 x i64>*, !dbg !36 - store <4 x i64> , <4 x i64>* %14, align 8, !dbg !36 - %callArgs4Addr.i = getelementptr [8 x i64], [8 x i64]* %callArgs.i, i64 0, i64 4, !dbg !36 - %15 = bitcast i64* %callArgs4Addr.i to <2 x i64>*, !dbg !36 - store <2 x i64> , <2 x i64>* %15, align 8, !dbg !36 - %callArgs6Addr.i = getelementptr [8 x i64], [8 x i64]* %callArgs.i, i64 0, i64 6, !dbg !36 - store i64 15, i64* %callArgs6Addr.i, align 8, !dbg !36 - tail call void @llvm.experimental.noalias.scope.decl(metadata !37) #8, !dbg !36 - %16 = call i64 @rb_ary_new_from_values(i64 noundef 7, i64* noundef nonnull align 8 %callArgs0Addr.i) #8, !dbg !36 - store i64 %16, i64* %callArgs0Addr.i, align 8, !dbg !40 - %17 = bitcast i64* %callArgs1Addr.i to <2 x i64>*, !dbg !40 - store <2 x i64> , <2 x i64>* %17, align 8, !dbg !40 - call void @llvm.experimental.noalias.scope.decl(metadata !41) #8, !dbg !40 - %18 = getelementptr inbounds i64, i64* %callArgs0Addr.i, i64 1, !dbg !40 - %19 = getelementptr inbounds i64, i64* %callArgs0Addr.i, i64 2, !dbg !40 - %20 = call i64 @sorbet_vm_expandSplatIntrinsic(i64 %16, i64 3, i64 5) #8, !dbg !40, !noalias !41 - store i64 1, i64* %callArgs0Addr.i, align 8, !dbg !40 - call void @llvm.experimental.noalias.scope.decl(metadata !44) #8, !dbg !40 - %21 = call i64 @rb_ary_entry(i64 %20, i64 0) #8, !dbg !40 - %22 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !40, !tbaa !17 - %23 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %22, i64 0, i32 5, !dbg !40 - %24 = load i32, i32* %23, align 8, !dbg !40, !tbaa !47 - %25 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %22, i64 0, i32 6, !dbg !40 - %26 = load i32, i32* %25, align 4, !dbg !40, !tbaa !48 - %27 = xor i32 %26, -1, !dbg !40 - %28 = and i32 %27, %24, !dbg !40 - %29 = icmp eq i32 %28, 0, !dbg !40 - br i1 %29, label %sorbet_rb_array_square_br.exit370.i, label %30, !dbg !40, !prof !49 - -30: ; preds = %entry - %31 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %22, i64 0, i32 8, !dbg !40 - %32 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %31, align 8, !dbg !40, !tbaa !50 - %33 = call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %32, i32 noundef 0) #8, !dbg !40 - br label %sorbet_rb_array_square_br.exit370.i, !dbg !40 - -sorbet_rb_array_square_br.exit370.i: ; preds = %30, %entry - store i64 -3, i64* %callArgs0Addr.i, align 8, !dbg !40 - call void @llvm.experimental.noalias.scope.decl(metadata !51) #8, !dbg !40 - %34 = call i64 @rb_ary_entry(i64 %20, i64 -2) #8, !dbg !40 - %35 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !40, !tbaa !17 - %36 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %35, i64 0, i32 5, !dbg !40 - %37 = load i32, i32* %36, align 8, !dbg !40, !tbaa !47 - %38 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %35, i64 0, i32 6, !dbg !40 - %39 = load i32, i32* %38, align 4, !dbg !40, !tbaa !48 - %40 = xor i32 %39, -1, !dbg !40 - %41 = and i32 %40, %37, !dbg !40 - %42 = icmp eq i32 %41, 0, !dbg !40 - br i1 %42, label %sorbet_rb_array_square_br.exit371.i, label %43, !dbg !40, !prof !49 - -43: ; preds = %sorbet_rb_array_square_br.exit370.i - %44 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %35, i64 0, i32 8, !dbg !40 - %45 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %44, align 8, !dbg !40, !tbaa !50 - %46 = call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %45, i32 noundef 0) #8, !dbg !40 - br label %sorbet_rb_array_square_br.exit371.i, !dbg !40 - -sorbet_rb_array_square_br.exit371.i: ; preds = %43, %sorbet_rb_array_square_br.exit370.i - store i64 -1, i64* %callArgs0Addr.i, align 8, !dbg !40 - call void @llvm.experimental.noalias.scope.decl(metadata !54) #8, !dbg !40 - %47 = call i64 @rb_ary_entry(i64 %20, i64 -1) #8, !dbg !40 - %48 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !40, !tbaa !17 - %49 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %48, i64 0, i32 5, !dbg !40 - %50 = load i32, i32* %49, align 8, !dbg !40, !tbaa !47 - %51 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %48, i64 0, i32 6, !dbg !40 - %52 = load i32, i32* %51, align 4, !dbg !40, !tbaa !48 - %53 = xor i32 %52, -1, !dbg !40 - %54 = and i32 %53, %50, !dbg !40 - %55 = icmp eq i32 %54, 0, !dbg !40 - br i1 %55, label %sorbet_rb_array_square_br.exit372.i, label %56, !dbg !40, !prof !49 - -56: ; preds = %sorbet_rb_array_square_br.exit371.i - %57 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %48, i64 0, i32 8, !dbg !40 - %58 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %57, align 8, !dbg !40, !tbaa !50 - %59 = call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %58, i32 noundef 0) #8, !dbg !40 - br label %sorbet_rb_array_square_br.exit372.i, !dbg !40 - -sorbet_rb_array_square_br.exit372.i: ; preds = %56, %sorbet_rb_array_square_br.exit371.i - store i64* getelementptr inbounds ([13 x i64], [13 x i64]* @iseqEncodedArray, i64 0, i64 6), i64** %13, align 8, !dbg !35, !tbaa !17 - store i64 %21, i64* %callArgs0Addr.i, align 8, !dbg !57 - store i64 %34, i64* %callArgs1Addr.i, align 8, !dbg !57 - store i64 %47, i64* %callArgs2Addr.i, align 8, !dbg !57 - call void @llvm.experimental.noalias.scope.decl(metadata !58) #8, !dbg !57 - %60 = call i64 @rb_ary_new_from_values(i64 noundef 3, i64* noundef nonnull %callArgs0Addr.i) #8, !dbg !57 - %61 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !10 - %62 = load i64*, i64** %61, align 8, !dbg !10 - store i64 %2, i64* %62, align 8, !dbg !10, !tbaa !6 - %63 = getelementptr inbounds i64, i64* %62, i64 1, !dbg !10 - store i64 %60, i64* %63, align 8, !dbg !10, !tbaa !6 - %64 = getelementptr inbounds i64, i64* %63, i64 1, !dbg !10 - store i64* %64, i64** %61, align 8, !dbg !10 - %send = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts, i64 0), !dbg !10 - store i64* getelementptr inbounds ([13 x i64], [13 x i64]* @iseqEncodedArray, i64 0, i64 8), i64** %13, align 8, !dbg !10, !tbaa !17 - %65 = bitcast i64* %callArgs0Addr.i to <2 x i64>*, !dbg !61 - store <2 x i64> , <2 x i64>* %65, align 8, !dbg !61 - call void @llvm.experimental.noalias.scope.decl(metadata !62) #8, !dbg !61 - %66 = call i64 @rb_ary_new_from_values(i64 noundef 2, i64* noundef nonnull %callArgs0Addr.i) #8, !dbg !61 - store i64 %66, i64* %callArgs0Addr.i, align 8, !dbg !65 - store <2 x i64> , <2 x i64>* %17, align 8, !dbg !65 - call void @llvm.experimental.noalias.scope.decl(metadata !66) #8, !dbg !65 - %67 = call i64 @sorbet_vm_expandSplatIntrinsic(i64 %66, i64 3, i64 5) #8, !dbg !65, !noalias !66 - store i64 1, i64* %callArgs0Addr.i, align 8, !dbg !65 - call void @llvm.experimental.noalias.scope.decl(metadata !69) #8, !dbg !65 - %68 = call i64 @rb_ary_entry(i64 %67, i64 0) #8, !dbg !65 - %69 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !65, !tbaa !17 - %70 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %69, i64 0, i32 5, !dbg !65 - %71 = load i32, i32* %70, align 8, !dbg !65, !tbaa !47 - %72 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %69, i64 0, i32 6, !dbg !65 - %73 = load i32, i32* %72, align 4, !dbg !65, !tbaa !48 - %74 = xor i32 %73, -1, !dbg !65 - %75 = and i32 %74, %71, !dbg !65 - %76 = icmp eq i32 %75, 0, !dbg !65 - br i1 %76, label %sorbet_rb_array_square_br.exit373.i, label %77, !dbg !65, !prof !49 - -77: ; preds = %sorbet_rb_array_square_br.exit372.i - %78 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %69, i64 0, i32 8, !dbg !65 - %79 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %78, align 8, !dbg !65, !tbaa !50 - %80 = call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %79, i32 noundef 0) #8, !dbg !65 - br label %sorbet_rb_array_square_br.exit373.i, !dbg !65 - -sorbet_rb_array_square_br.exit373.i: ; preds = %77, %sorbet_rb_array_square_br.exit372.i - store i64 -3, i64* %callArgs0Addr.i, align 8, !dbg !65 - call void @llvm.experimental.noalias.scope.decl(metadata !72) #8, !dbg !65 - %81 = call i64 @rb_ary_entry(i64 %67, i64 -2) #8, !dbg !65 - %82 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !65, !tbaa !17 - %83 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %82, i64 0, i32 5, !dbg !65 - %84 = load i32, i32* %83, align 8, !dbg !65, !tbaa !47 - %85 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %82, i64 0, i32 6, !dbg !65 - %86 = load i32, i32* %85, align 4, !dbg !65, !tbaa !48 - %87 = xor i32 %86, -1, !dbg !65 - %88 = and i32 %87, %84, !dbg !65 - %89 = icmp eq i32 %88, 0, !dbg !65 - br i1 %89, label %sorbet_rb_array_square_br.exit374.i, label %90, !dbg !65, !prof !49 - -90: ; preds = %sorbet_rb_array_square_br.exit373.i - %91 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %82, i64 0, i32 8, !dbg !65 - %92 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %91, align 8, !dbg !65, !tbaa !50 - %93 = call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %92, i32 noundef 0) #8, !dbg !65 - br label %sorbet_rb_array_square_br.exit374.i, !dbg !65 - -sorbet_rb_array_square_br.exit374.i: ; preds = %90, %sorbet_rb_array_square_br.exit373.i - store i64 -1, i64* %callArgs0Addr.i, align 8, !dbg !65 - call void @llvm.experimental.noalias.scope.decl(metadata !75) #8, !dbg !65 - %94 = call i64 @rb_ary_entry(i64 %67, i64 -1) #8, !dbg !65 - %95 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !65, !tbaa !17 - %96 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %95, i64 0, i32 5, !dbg !65 - %97 = load i32, i32* %96, align 8, !dbg !65, !tbaa !47 - %98 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %95, i64 0, i32 6, !dbg !65 - %99 = load i32, i32* %98, align 4, !dbg !65, !tbaa !48 - %100 = xor i32 %99, -1, !dbg !65 - %101 = and i32 %100, %97, !dbg !65 - %102 = icmp eq i32 %101, 0, !dbg !65 - br i1 %102, label %sorbet_rb_array_square_br.exit375.i, label %103, !dbg !65, !prof !49 - -103: ; preds = %sorbet_rb_array_square_br.exit374.i - %104 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %95, i64 0, i32 8, !dbg !65 - %105 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %104, align 8, !dbg !65, !tbaa !50 - %106 = call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %105, i32 noundef 0) #8, !dbg !65 - br label %sorbet_rb_array_square_br.exit375.i, !dbg !65 - -sorbet_rb_array_square_br.exit375.i: ; preds = %103, %sorbet_rb_array_square_br.exit374.i - store i64* getelementptr inbounds ([13 x i64], [13 x i64]* @iseqEncodedArray, i64 0, i64 9), i64** %13, align 8, !dbg !35, !tbaa !17 - store i64 %68, i64* %callArgs0Addr.i, align 8, !dbg !78 - store i64 %81, i64* %callArgs1Addr.i, align 8, !dbg !78 - store i64 %94, i64* %callArgs2Addr.i, align 8, !dbg !78 - call void @llvm.experimental.noalias.scope.decl(metadata !79) #8, !dbg !78 - %107 = call i64 @rb_ary_new_from_values(i64 noundef 3, i64* noundef nonnull %callArgs0Addr.i) #8, !dbg !78 - %108 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !15 - %109 = load i64*, i64** %108, align 8, !dbg !15 - store i64 %2, i64* %109, align 8, !dbg !15, !tbaa !6 - %110 = getelementptr inbounds i64, i64* %109, i64 1, !dbg !15 - store i64 %107, i64* %110, align 8, !dbg !15, !tbaa !6 - %111 = getelementptr inbounds i64, i64* %110, i64 1, !dbg !15 - store i64* %111, i64** %108, align 8, !dbg !15 - %send2 = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts.6, i64 0), !dbg !15 - store i64* getelementptr inbounds ([13 x i64], [13 x i64]* @iseqEncodedArray, i64 0, i64 11), i64** %13, align 8, !dbg !35, !tbaa !17 - call void @llvm.experimental.noalias.scope.decl(metadata !82) #8, !dbg !85 - %112 = call i64 @rb_ary_new() #8, !dbg !85 - store i64 %112, i64* %callArgs0Addr.i, align 8, !dbg !86 - store <2 x i64> , <2 x i64>* %17, align 8, !dbg !86 - call void @llvm.experimental.noalias.scope.decl(metadata !87) #8, !dbg !86 - %113 = call i64 @sorbet_vm_expandSplatIntrinsic(i64 %112, i64 3, i64 5) #8, !dbg !86, !noalias !87 - store i64 1, i64* %callArgs0Addr.i, align 8, !dbg !86 - call void @llvm.experimental.noalias.scope.decl(metadata !90) #8, !dbg !86 - %114 = call i64 @rb_ary_entry(i64 %113, i64 0) #8, !dbg !86 - %115 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !86, !tbaa !17 - %116 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %115, i64 0, i32 5, !dbg !86 - %117 = load i32, i32* %116, align 8, !dbg !86, !tbaa !47 - %118 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %115, i64 0, i32 6, !dbg !86 - %119 = load i32, i32* %118, align 4, !dbg !86, !tbaa !48 - %120 = xor i32 %119, -1, !dbg !86 - %121 = and i32 %120, %117, !dbg !86 - %122 = icmp eq i32 %121, 0, !dbg !86 - br i1 %122, label %sorbet_rb_array_square_br.exit368.i, label %123, !dbg !86, !prof !49 - -123: ; preds = %sorbet_rb_array_square_br.exit375.i - %124 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %115, i64 0, i32 8, !dbg !86 - %125 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %124, align 8, !dbg !86, !tbaa !50 - %126 = call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %125, i32 noundef 0) #8, !dbg !86 - br label %sorbet_rb_array_square_br.exit368.i, !dbg !86 - -sorbet_rb_array_square_br.exit368.i: ; preds = %123, %sorbet_rb_array_square_br.exit375.i - store i64 -3, i64* %callArgs0Addr.i, align 8, !dbg !86 - call void @llvm.experimental.noalias.scope.decl(metadata !93) #8, !dbg !86 - %127 = call i64 @rb_ary_entry(i64 %113, i64 -2) #8, !dbg !86 - %128 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !86, !tbaa !17 - %129 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %128, i64 0, i32 5, !dbg !86 - %130 = load i32, i32* %129, align 8, !dbg !86, !tbaa !47 - %131 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %128, i64 0, i32 6, !dbg !86 - %132 = load i32, i32* %131, align 4, !dbg !86, !tbaa !48 - %133 = xor i32 %132, -1, !dbg !86 - %134 = and i32 %133, %130, !dbg !86 - %135 = icmp eq i32 %134, 0, !dbg !86 - br i1 %135, label %sorbet_rb_array_square_br.exit.i, label %136, !dbg !86, !prof !49 - -136: ; preds = %sorbet_rb_array_square_br.exit368.i - %137 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %128, i64 0, i32 8, !dbg !86 - %138 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %137, align 8, !dbg !86, !tbaa !50 - %139 = call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %138, i32 noundef 0) #8, !dbg !86 - br label %sorbet_rb_array_square_br.exit.i, !dbg !86 - -sorbet_rb_array_square_br.exit.i: ; preds = %136, %sorbet_rb_array_square_br.exit368.i - store i64 -1, i64* %callArgs0Addr.i, align 8, !dbg !86 - call void @llvm.experimental.noalias.scope.decl(metadata !96) #8, !dbg !86 - %140 = call i64 @rb_ary_entry(i64 %113, i64 -1) #8, !dbg !86 - %141 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !86, !tbaa !17 - %142 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %141, i64 0, i32 5, !dbg !86 - %143 = load i32, i32* %142, align 8, !dbg !86, !tbaa !47 - %144 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %141, i64 0, i32 6, !dbg !86 - %145 = load i32, i32* %144, align 4, !dbg !86, !tbaa !48 - %146 = xor i32 %145, -1, !dbg !86 - %147 = and i32 %146, %143, !dbg !86 - %148 = icmp eq i32 %147, 0, !dbg !86 - br i1 %148, label %"func_.13.exit", label %149, !dbg !86, !prof !49 - -149: ; preds = %sorbet_rb_array_square_br.exit.i - %150 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %141, i64 0, i32 8, !dbg !86 - %151 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %150, align 8, !dbg !86, !tbaa !50 - %152 = call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %151, i32 noundef 0) #8, !dbg !86 - br label %"func_.13.exit", !dbg !86 - -"func_.13.exit": ; preds = %sorbet_rb_array_square_br.exit.i, %149 - store i64* getelementptr inbounds ([13 x i64], [13 x i64]* @iseqEncodedArray, i64 0, i64 12), i64** %13, align 8, !dbg !35, !tbaa !17 - store i64 %114, i64* %callArgs0Addr.i, align 8, !dbg !99 - store i64 %127, i64* %callArgs1Addr.i, align 8, !dbg !99 - store i64 %140, i64* %callArgs2Addr.i, align 8, !dbg !99 - call void @llvm.experimental.noalias.scope.decl(metadata !100) #8, !dbg !99 - %153 = call i64 @rb_ary_new_from_values(i64 noundef 3, i64* noundef nonnull %callArgs0Addr.i) #8, !dbg !99 - %154 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !16 - %155 = load i64*, i64** %154, align 8, !dbg !16 - store i64 %2, i64* %155, align 8, !dbg !16, !tbaa !6 - %156 = getelementptr inbounds i64, i64* %155, i64 1, !dbg !16 - store i64 %153, i64* %156, align 8, !dbg !16, !tbaa !6 - %157 = getelementptr inbounds i64, i64* %156, i64 1, !dbg !16 - store i64* %157, i64** %154, align 8, !dbg !16 - %send4 = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts.10, i64 0), !dbg !16 - call void @llvm.lifetime.end.p0i8(i64 64, i8* nonnull %6) - ret void -} - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #6 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #6 - -attributes #0 = { "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #1 = { noreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #2 = { inaccessiblememonly nofree nosync nounwind willreturn } -attributes #3 = { nounwind ssp uwtable "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #4 = { ssp } -attributes #5 = { sspreq } -attributes #6 = { argmemonly nofree nosync nounwind willreturn } -attributes #7 = { noreturn nounwind } -attributes #8 = { nounwind } - -!llvm.module.flags = !{!0, !1, !2} -!llvm.dbg.cu = !{!3} - -!0 = !{i32 2, !"Debug Info Version", i32 3} -!1 = !{i32 4, !"cf-protection-return", i32 1} -!2 = !{i32 4, !"cf-protection-branch", i32 1} -!3 = distinct !DICompileUnit(language: DW_LANG_C, file: !4, producer: "Sorbet LLVM", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !5) -!4 = !DIFile(filename: "test/testdata/compiler/splat_assign.rb", directory: ".") -!5 = !{} -!6 = !{!7, !7, i64 0} -!7 = !{!"long", !8, i64 0} -!8 = !{!"omnipotent char", !9, i64 0} -!9 = !{!"Simple C/C++ TBAA"} -!10 = !DILocation(line: 6, column: 1, scope: !11) -!11 = distinct !DISubprogram(name: ".", linkageName: "func_.13", scope: null, file: !4, line: 5, type: !12, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!12 = !DISubroutineType(types: !13) -!13 = !{!14} -!14 = !DIBasicType(name: "VALUE", size: 64, encoding: DW_ATE_signed) -!15 = !DILocation(line: 9, column: 1, scope: !11) -!16 = !DILocation(line: 12, column: 1, scope: !11) -!17 = !{!18, !18, i64 0} -!18 = !{!"any pointer", !8, i64 0} -!19 = !{!20, !7, i64 400} -!20 = !{!"rb_vm_struct", !7, i64 0, !21, i64 8, !18, i64 192, !18, i64 200, !18, i64 208, !25, i64 216, !8, i64 224, !22, i64 264, !22, i64 280, !22, i64 296, !22, i64 312, !7, i64 328, !24, i64 336, !24, i64 340, !24, i64 344, !24, i64 344, !24, i64 344, !24, i64 344, !24, i64 348, !7, i64 352, !8, i64 360, !7, i64 400, !7, i64 408, !7, i64 416, !7, i64 424, !7, i64 432, !7, i64 440, !7, i64 448, !18, i64 456, !18, i64 464, !26, i64 472, !27, i64 992, !18, i64 1016, !18, i64 1024, !24, i64 1032, !24, i64 1036, !22, i64 1040, !8, i64 1056, !7, i64 1096, !7, i64 1104, !7, i64 1112, !7, i64 1120, !7, i64 1128, !24, i64 1136, !18, i64 1144, !18, i64 1152, !18, i64 1160, !18, i64 1168, !18, i64 1176, !18, i64 1184, !24, i64 1192, !28, i64 1200, !8, i64 1232} -!21 = !{!"rb_global_vm_lock_struct", !18, i64 0, !8, i64 8, !22, i64 48, !18, i64 64, !24, i64 72, !8, i64 80, !8, i64 128, !24, i64 176, !24, i64 180} -!22 = !{!"list_head", !23, i64 0} -!23 = !{!"list_node", !18, i64 0, !18, i64 8} -!24 = !{!"int", !8, i64 0} -!25 = !{!"long long", !8, i64 0} -!26 = !{!"", !8, i64 0} -!27 = !{!"rb_hook_list_struct", !18, i64 0, !24, i64 8, !24, i64 12, !24, i64 16} -!28 = !{!"", !7, i64 0, !7, i64 8, !7, i64 16, !7, i64 24} -!29 = !{!30, !18, i64 16} -!30 = !{!"rb_execution_context_struct", !18, i64 0, !7, i64 8, !18, i64 16, !18, i64 24, !18, i64 32, !24, i64 40, !24, i64 44, !18, i64 48, !18, i64 56, !18, i64 64, !7, i64 72, !7, i64 80, !18, i64 88, !7, i64 96, !18, i64 104, !18, i64 112, !7, i64 120, !7, i64 128, !8, i64 136, !8, i64 137, !7, i64 144, !31, i64 152} -!31 = !{!"", !18, i64 0, !18, i64 8, !7, i64 16, !8, i64 24} -!32 = !{!33, !18, i64 16} -!33 = !{!"rb_control_frame_struct", !18, i64 0, !18, i64 8, !18, i64 16, !7, i64 24, !18, i64 32, !18, i64 40, !18, i64 48} -!34 = !{!33, !18, i64 32} -!35 = !DILocation(line: 0, scope: !11) -!36 = !DILocation(line: 5, column: 11, scope: !11) -!37 = !{!38} -!38 = distinct !{!38, !39, !"sorbet_buildArrayIntrinsic: argument 0"} -!39 = distinct !{!39, !"sorbet_buildArrayIntrinsic"} -!40 = !DILocation(line: 5, column: 1, scope: !11) -!41 = !{!42} -!42 = distinct !{!42, !43, !"sorbet_expandSplatIntrinsic: argument 0"} -!43 = distinct !{!43, !"sorbet_expandSplatIntrinsic"} -!44 = !{!45} -!45 = distinct !{!45, !46, !"sorbet_rb_array_square_br: argument 0"} -!46 = distinct !{!46, !"sorbet_rb_array_square_br"} -!47 = !{!30, !24, i64 40} -!48 = !{!30, !24, i64 44} -!49 = !{!"branch_weights", i32 2000, i32 1} -!50 = !{!30, !18, i64 56} -!51 = !{!52} -!52 = distinct !{!52, !53, !"sorbet_rb_array_square_br: argument 0"} -!53 = distinct !{!53, !"sorbet_rb_array_square_br"} -!54 = !{!55} -!55 = distinct !{!55, !56, !"sorbet_rb_array_square_br: argument 0"} -!56 = distinct !{!56, !"sorbet_rb_array_square_br"} -!57 = !DILocation(line: 6, column: 6, scope: !11) -!58 = !{!59} -!59 = distinct !{!59, !60, !"sorbet_buildArrayIntrinsic: argument 0"} -!60 = distinct !{!60, !"sorbet_buildArrayIntrinsic"} -!61 = !DILocation(line: 8, column: 11, scope: !11) -!62 = !{!63} -!63 = distinct !{!63, !64, !"sorbet_buildArrayIntrinsic: argument 0"} -!64 = distinct !{!64, !"sorbet_buildArrayIntrinsic"} -!65 = !DILocation(line: 8, column: 1, scope: !11) -!66 = !{!67} -!67 = distinct !{!67, !68, !"sorbet_expandSplatIntrinsic: argument 0"} -!68 = distinct !{!68, !"sorbet_expandSplatIntrinsic"} -!69 = !{!70} -!70 = distinct !{!70, !71, !"sorbet_rb_array_square_br: argument 0"} -!71 = distinct !{!71, !"sorbet_rb_array_square_br"} -!72 = !{!73} -!73 = distinct !{!73, !74, !"sorbet_rb_array_square_br: argument 0"} -!74 = distinct !{!74, !"sorbet_rb_array_square_br"} -!75 = !{!76} -!76 = distinct !{!76, !77, !"sorbet_rb_array_square_br: argument 0"} -!77 = distinct !{!77, !"sorbet_rb_array_square_br"} -!78 = !DILocation(line: 9, column: 6, scope: !11) -!79 = !{!80} -!80 = distinct !{!80, !81, !"sorbet_buildArrayIntrinsic: argument 0"} -!81 = distinct !{!81, !"sorbet_buildArrayIntrinsic"} -!82 = !{!83} -!83 = distinct !{!83, !84, !"sorbet_buildArrayIntrinsic: argument 0"} -!84 = distinct !{!84, !"sorbet_buildArrayIntrinsic"} -!85 = !DILocation(line: 11, column: 11, scope: !11) -!86 = !DILocation(line: 11, column: 1, scope: !11) -!87 = !{!88} -!88 = distinct !{!88, !89, !"sorbet_expandSplatIntrinsic: argument 0"} -!89 = distinct !{!89, !"sorbet_expandSplatIntrinsic"} -!90 = !{!91} -!91 = distinct !{!91, !92, !"sorbet_rb_array_square_br: argument 0"} -!92 = distinct !{!92, !"sorbet_rb_array_square_br"} -!93 = !{!94} -!94 = distinct !{!94, !95, !"sorbet_rb_array_square_br: argument 0"} -!95 = distinct !{!95, !"sorbet_rb_array_square_br"} -!96 = !{!97} -!97 = distinct !{!97, !98, !"sorbet_rb_array_square_br: argument 0"} -!98 = distinct !{!98, !"sorbet_rb_array_square_br"} -!99 = !DILocation(line: 12, column: 6, scope: !11) -!100 = !{!101} -!101 = distinct !{!101, !102, !"sorbet_buildArrayIntrinsic: argument 0"} -!102 = distinct !{!102, !"sorbet_buildArrayIntrinsic"} diff --git a/test/testdata/compiler/stack_frame_exception_arguments.rb b/test/testdata/compiler/stack_frame_exception_arguments.rb index e057ab8615..e9ce9060f3 100644 --- a/test/testdata/compiler/stack_frame_exception_arguments.rb +++ b/test/testdata/compiler/stack_frame_exception_arguments.rb @@ -12,7 +12,7 @@ class Main sig {params(x: String).void} def self.takes_string(x) - # NOTE: we need to use x, as we only type check arguments that get used + # NOTE, we need to use x, as we only type check arguments that get used T.unsafe(x) end end diff --git a/test/testdata/compiler/static_method.rb b/test/testdata/compiler/static_method.rb index 8ec4964f8d..0a5373d014 100644 --- a/test/testdata/compiler/static_method.rb +++ b/test/testdata/compiler/static_method.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL module Foo def self.bar @@ -10,7 +9,3 @@ def self.bar end puts Foo.bar -# INITIAL-LABEL: define internal i64 @"func_Foo.13 -# INITIAL: [[VAR:%[a-zA-Z0-9_]+]] = load i8*, i8** @addr_str_bar{{.*}} -# INITIAL: call void @sorbet_defineMethodSingleton({{.*}}[[VAR]] -# INITIAL{LITERAL}: } diff --git a/test/testdata/compiler/t_let_constant.rb b/test/testdata/compiler/t_let_constant.rb index 8024f121bc..8eee772e7a 100644 --- a/test/testdata/compiler/t_let_constant.rb +++ b/test/testdata/compiler/t_let_constant.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true # typed: true # compiled: true -# run_filecheck: INITIAL def f hash = T.let({a: 1, b: 1, c: 2, d: 3, e: 5, f: 8, g: 13}, T::Hash[Symbol, Integer]) @@ -14,17 +13,3 @@ def g end p g - -# INITIAL-LABEL: define internal i64 @"func_Object#1f" -# INITIAL: [[DUP:%[a-zA-Z]+]] = call i64 @sorbet_globalConstDupHash{{.*}} -# INITIAL-NEXT: [[COND:%[0-9]+]] = call i1 @sorbet_i_isa_Hash(i64 [[DUP]]{{.*}} -# INITIAL-NEXT: call void @llvm.assume(i1 [[COND]]){{.*}} -# INITIAL-NEXT: br i1 true, label %typeTestSuccess, label %typeTestFail{{.*}} -# INITIAL{LITERAL}: } - -# INITIAL-LABEL: define internal i64 @"func_Object#1g" -# INITIAL: [[DUP:%[a-zA-Z]+]] = call i64 @sorbet_buildArrayIntrinsic{{.*}} -# INITIAL-NEXT: [[COND:%[0-9]+]] = call i1 @sorbet_i_isa_Array(i64 [[DUP]]{{.*}} -# INITIAL-NEXT: call void @llvm.assume(i1 [[COND]]){{.*}} -# INITIAL-NEXT: br i1 true, label %typeTestSuccess, label %typeTestFail{{.*}} -# INITIAL{LITERAL}: } diff --git a/test/testdata/compiler/t_private_compiler.rb b/test/testdata/compiler/t_private_compiler.rb deleted file mode 100644 index ba1bfc1589..0000000000 --- a/test/testdata/compiler/t_private_compiler.rb +++ /dev/null @@ -1,20 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -running_compiled = T::Private::Compiler.running_compiled? -case running_compiled -when false, true - puts 'ok: T::Private::Compiler.running_compiled?' -else - T.absurd(running_compiled) -end - -compiler_version = T::Private::Compiler.compiler_version -if running_compiled && compiler_version.is_a?(String) - puts 'ok: T::Private::Compiler.compiler_version' -elsif !running_compiled && compiler_version.nil? - puts 'ok: T::Private::Compiler.compiler_version' -else - raise "compiler_version does not match running_compiled?" -end diff --git a/test/testdata/compiler/unsafe.opt.ll.exp b/test/testdata/compiler/unsafe.opt.ll.exp deleted file mode 100644 index 214d2e6e8a..0000000000 --- a/test/testdata/compiler/unsafe.opt.ll.exp +++ /dev/null @@ -1,259 +0,0 @@ -; ModuleID = 'payload' -source_filename = "llvm-link" -target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } -%struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.__pthread_cond_s } -%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } -%union.anon = type { i64 } -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } -%struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } -%struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } -%struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } -%struct.anon.0 = type { i16, [2 x i8] } -%struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } -%struct.rb_iseq_location_struct = type { i64, i64, i64, i64, i32, %struct.rb_code_location_struct } -%struct.rb_code_location_struct = type { %struct.rb_code_position_struct, %struct.rb_code_position_struct } -%struct.rb_code_position_struct = type { i32, i32 } -%struct.iseq_insn_info = type { %struct.iseq_insn_info_entry*, i32*, i32, %struct.succ_index_table* } -%struct.iseq_insn_info_entry = type opaque -%struct.succ_index_table = type opaque -%struct.iseq_catch_table = type opaque -%union.iseq_inline_storage_entry = type { %struct.iseq_inline_cache_entry } -%struct.iseq_inline_cache_entry = type { i64, %struct.rb_cref_struct*, i64 } -%struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } -%struct.rb_scope_visi_struct = type { i8, [3 x i8] } -%struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } -%struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } -%union.anon.13 = type { %struct.rb_method_cfunc_struct } -%struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%union.anon.15 = type { i32 } -%struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.16 = type { i64, i64, i64, i64* } -%union.anon.17 = type { %struct.anon.18 } -%struct.anon.18 = type { i64, i32 } -%struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } -%struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } -%struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } -%struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } -%struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } -%struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } -%struct.__sigset_t = type { [16 x i64] } -%struct.rb_calling_info = type { i64, i64, i32, i32 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } -%union.anon.9 = type { %union.pthread_cond_t } -%struct.rb_unblock_callback = type { void (i8*)*, i8* } -%struct.rb_mutex_struct = type opaque -%struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.10 = type { %struct.anon.11 } -%struct.anon.11 = type { i64, i64, i32 } -%struct.rb_fiber_struct = type opaque -%struct.anon.5 = type { [65 x i64] } -%struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } -%struct.rb_event_hook_struct = type opaque -%struct.rb_postponed_job_struct = type opaque -%struct.list_head = type { %struct.list_node } -%union.pthread_mutex_t = type { %struct.__pthread_mutex_s } -%struct.__pthread_mutex_s = type { i32, i32, i32, i32, i32, i16, i16, %struct.__pthread_internal_list } -%struct.__pthread_internal_list = type { %struct.__pthread_internal_list*, %struct.__pthread_internal_list* } -%struct.rb_objspace = type opaque -%struct.rb_at_exit_list = type { void (%struct.rb_vm_struct*)*, %struct.rb_at_exit_list* } -%struct.st_table = type { i8, i8, i8, i32, %struct.st_hash_type*, i64, i64*, i64, i64, %struct.st_table_entry* } -%struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } -%struct.st_table_entry = type opaque -%struct.rb_builtin_function = type opaque -%struct.anon.6 = type { i64, i64, i64, i64 } -%struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } -%struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } -%struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } -%struct.rb_call_info_with_kwarg = type { %struct.rb_call_info, %struct.rb_call_info_kw_arg* } -%struct.rb_call_info_kw_arg = type { i32, [1 x i64] } - -@ruby_current_vm_ptr = external local_unnamed_addr global %struct.rb_vm_struct*, align 8 -@ruby_current_execution_context_ptr = external local_unnamed_addr global %struct.rb_execution_context_struct*, align 8 -@rb_eRuntimeError = external local_unnamed_addr global i64, align 8 -@.str.9 = private unnamed_addr constant [95 x i8] c"sorbet_getBuildSCMRevision: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@.str.10 = private unnamed_addr constant [93 x i8] c"sorbet_getIsReleaseBuild: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@"stackFramePrecomputed_func_.13" = internal unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@iseqEncodedArray = internal global [5 x i64] zeroinitializer -@fileLineNumberInfo = internal global %struct.SorbetLineNumberInfo zeroinitializer -@ic_puts = internal global %struct.FunctionInlineCache zeroinitializer -@sorbet_moduleStringTable = internal unnamed_addr constant [71 x i8] c"\00test/testdata/compiler/unsafe.rb\00T\00unsafe\00puts\00master\00", align 1 -@sorbet_moduleIDTable = internal unnamed_addr global [3 x i64] zeroinitializer, align 8 -@sorbet_moduleIDDescriptors = internal unnamed_addr constant [3 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 16 }, %struct.rb_code_position_struct { i32 52, i32 6 }, %struct.rb_code_position_struct { i32 59, i32 4 }], align 8 -@sorbet_moduleRubyStringTable = internal unnamed_addr global [2 x i64] zeroinitializer, align 8 -@sorbet_moduleRubyStringDescriptors = internal unnamed_addr constant [2 x %struct.rb_code_position_struct] [%struct.rb_code_position_struct { i32 0, i32 16 }, %struct.rb_code_position_struct { i32 17, i32 32 }], align 8 - -declare %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64, i64, i64, i64, %struct.rb_iseq_struct*, i32, i32, %struct.SorbetLineNumberInfo*, i64*, i32, i32) local_unnamed_addr #0 - -declare void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo*, i64*, i32) local_unnamed_addr #0 - -declare i64 @sorbet_readRealpath() local_unnamed_addr #0 - -declare void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) local_unnamed_addr #0 - -declare i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache*, i64) local_unnamed_addr #0 - -declare void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_iseq_struct*) local_unnamed_addr #0 - -declare void @sorbet_vm_intern_ids(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #0 - -declare void @sorbet_vm_init_string_table(i64*, %struct.rb_code_position_struct*, i32, i8*) local_unnamed_addr #0 - -; Function Attrs: noreturn -declare void @rb_raise(i64, i8*, ...) local_unnamed_addr #1 - -; Function Attrs: inaccessiblememonly nofree nosync nounwind willreturn -declare void @llvm.experimental.noalias.scope.decl(metadata) #2 - -declare i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct*, i32) local_unnamed_addr #0 - -; Function Attrs: nounwind ssp uwtable -define weak i32 @sorbet_getIsReleaseBuild() local_unnamed_addr #3 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([93 x i8], [93 x i8]* @.str.10, i64 0, i64 0)) #6 - unreachable -} - -; Function Attrs: nounwind ssp uwtable -define weak i8* @sorbet_getBuildSCMRevision() local_unnamed_addr #3 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([95 x i8], [95 x i8]* @.str.9, i64 0, i64 0)) #6 - unreachable -} - -define internal fastcc void @sorbet_globalConstructors(i64 %realpath) unnamed_addr { -allocRubyIds: - tail call void @sorbet_vm_intern_ids(i64* noundef getelementptr inbounds ([3 x i64], [3 x i64]* @sorbet_moduleIDTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([3 x %struct.rb_code_position_struct], [3 x %struct.rb_code_position_struct]* @sorbet_moduleIDDescriptors, i32 0, i32 0), i32 noundef 3, i8* noundef getelementptr inbounds ([71 x i8], [71 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_vm_init_string_table(i64* noundef getelementptr inbounds ([2 x i64], [2 x i64]* @sorbet_moduleRubyStringTable, i32 0, i32 0), %struct.rb_code_position_struct* noundef getelementptr inbounds ([2 x %struct.rb_code_position_struct], [2 x %struct.rb_code_position_struct]* @sorbet_moduleRubyStringDescriptors, i32 0, i32 0), i32 noundef 2, i8* noundef getelementptr inbounds ([71 x i8], [71 x i8]* @sorbet_moduleStringTable, i32 0, i32 0)) - tail call void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef getelementptr inbounds ([5 x i64], [5 x i64]* @iseqEncodedArray, i32 0, i32 0), i32 noundef 5) - tail call fastcc void @"Constr_stackFramePrecomputed_func_.13"(i64 %realpath) - %rubyId_puts = load i64, i64* getelementptr inbounds ([3 x i64], [3 x i64]* @sorbet_moduleIDTable, i64 0, i64 2), align 8, !dbg !10, !invariant.load !5 - tail call void (%struct.FunctionInlineCache*, i64, i32, i32, i32, ...) @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts, i64 %rubyId_puts, i32 noundef 20, i32 noundef 1, i32 noundef 0), !dbg !10 - ret void -} - -; Function Attrs: ssp -define internal fastcc void @"Constr_stackFramePrecomputed_func_.13"(i64 %realpath) unnamed_addr #4 { -entryInitializers: - %"rubyId_" = load i64, i64* getelementptr inbounds ([3 x i64], [3 x i64]* @sorbet_moduleIDTable, i64 0, i64 0), align 8, !invariant.load !5 - %"rubyStr_" = load i64, i64* getelementptr inbounds ([2 x i64], [2 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 0), align 8, !invariant.load !5 - %"rubyStr_test/testdata/compiler/unsafe.rb" = load i64, i64* getelementptr inbounds ([2 x i64], [2 x i64]* @sorbet_moduleRubyStringTable, i64 0, i64 1), align 8, !invariant.load !5 - %locals = alloca i64, i32 0, align 8 - %0 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_", i64 %"rubyId_", i64 %"rubyStr_test/testdata/compiler/unsafe.rb", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 0, i32 noundef 4, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals, i32 noundef 0, i32 noundef 2) - store %struct.rb_iseq_struct* %0, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - ret void -} - -; Function Attrs: sspreq -define void @Init_unsafe() local_unnamed_addr #5 { -entry: - %realpath = tail call i64 @sorbet_readRealpath() - tail call fastcc void @sorbet_globalConstructors(i64 %realpath) - %0 = load %struct.rb_vm_struct*, %struct.rb_vm_struct** @ruby_current_vm_ptr, align 8, !tbaa !15 - %1 = getelementptr inbounds %struct.rb_vm_struct, %struct.rb_vm_struct* %0, i64 0, i32 18 - %2 = load i64, i64* %1, align 8, !tbaa !17 - %3 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !15 - %4 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %3, i64 0, i32 2 - %5 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %4, align 8, !tbaa !27 - %stackFrame.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.13", align 8 - %6 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame.i, %struct.rb_iseq_struct** %6, align 8, !tbaa !30 - %7 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 4 - %8 = load i64*, i64** %7, align 8, !tbaa !32 - %9 = load i64, i64* %8, align 8, !tbaa !6 - %10 = and i64 %9, -33 - store i64 %10, i64* %8, align 8, !tbaa !6 - tail call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %3, %struct.rb_control_frame_struct* %5, %struct.rb_iseq_struct* %stackFrame.i) #7 - %11 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 0 - store i64* getelementptr inbounds ([5 x i64], [5 x i64]* @iseqEncodedArray, i64 0, i64 4), i64** %11, align 8, !dbg !33, !tbaa !15 - tail call void @llvm.experimental.noalias.scope.decl(metadata !34) #7, !dbg !37 - %12 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !37, !tbaa !15 - %13 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %12, i64 0, i32 5, !dbg !37 - %14 = load i32, i32* %13, align 8, !dbg !37, !tbaa !38 - %15 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %12, i64 0, i32 6, !dbg !37 - %16 = load i32, i32* %15, align 4, !dbg !37, !tbaa !39 - %17 = xor i32 %16, -1, !dbg !37 - %18 = and i32 %17, %14, !dbg !37 - %19 = icmp eq i32 %18, 0, !dbg !37 - br i1 %19, label %"func_.13.exit", label %20, !dbg !37, !prof !40 - -20: ; preds = %entry - %21 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %12, i64 0, i32 8, !dbg !37 - %22 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %21, align 8, !dbg !37, !tbaa !41 - %23 = tail call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %22, i32 noundef 0) #7, !dbg !37 - br label %"func_.13.exit", !dbg !37 - -"func_.13.exit": ; preds = %entry, %20 - %24 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %5, i64 0, i32 1, !dbg !10 - %25 = load i64*, i64** %24, align 8, !dbg !10 - store i64 %2, i64* %25, align 8, !dbg !10, !tbaa !6 - %26 = getelementptr inbounds i64, i64* %25, i64 1, !dbg !10 - store i64 3, i64* %26, align 8, !dbg !10, !tbaa !6 - %27 = getelementptr inbounds i64, i64* %26, i64 1, !dbg !10 - store i64* %27, i64** %24, align 8, !dbg !10 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts, i64 0), !dbg !10 - ret void -} - -attributes #0 = { "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #1 = { noreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #2 = { inaccessiblememonly nofree nosync nounwind willreturn } -attributes #3 = { nounwind ssp uwtable "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #4 = { ssp } -attributes #5 = { sspreq } -attributes #6 = { noreturn nounwind } -attributes #7 = { nounwind } - -!llvm.module.flags = !{!0, !1, !2} -!llvm.dbg.cu = !{!3} - -!0 = !{i32 2, !"Debug Info Version", i32 3} -!1 = !{i32 4, !"cf-protection-return", i32 1} -!2 = !{i32 4, !"cf-protection-branch", i32 1} -!3 = distinct !DICompileUnit(language: DW_LANG_C, file: !4, producer: "Sorbet LLVM", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !5) -!4 = !DIFile(filename: "test/testdata/compiler/unsafe.rb", directory: ".") -!5 = !{} -!6 = !{!7, !7, i64 0} -!7 = !{!"long", !8, i64 0} -!8 = !{!"omnipotent char", !9, i64 0} -!9 = !{!"Simple C/C++ TBAA"} -!10 = !DILocation(line: 4, column: 1, scope: !11) -!11 = distinct !DISubprogram(name: ".", linkageName: "func_.13", scope: null, file: !4, line: 4, type: !12, scopeLine: 4, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!12 = !DISubroutineType(types: !13) -!13 = !{!14} -!14 = !DIBasicType(name: "VALUE", size: 64, encoding: DW_ATE_signed) -!15 = !{!16, !16, i64 0} -!16 = !{!"any pointer", !8, i64 0} -!17 = !{!18, !7, i64 400} -!18 = !{!"rb_vm_struct", !7, i64 0, !19, i64 8, !16, i64 192, !16, i64 200, !16, i64 208, !23, i64 216, !8, i64 224, !20, i64 264, !20, i64 280, !20, i64 296, !20, i64 312, !7, i64 328, !22, i64 336, !22, i64 340, !22, i64 344, !22, i64 344, !22, i64 344, !22, i64 344, !22, i64 348, !7, i64 352, !8, i64 360, !7, i64 400, !7, i64 408, !7, i64 416, !7, i64 424, !7, i64 432, !7, i64 440, !7, i64 448, !16, i64 456, !16, i64 464, !24, i64 472, !25, i64 992, !16, i64 1016, !16, i64 1024, !22, i64 1032, !22, i64 1036, !20, i64 1040, !8, i64 1056, !7, i64 1096, !7, i64 1104, !7, i64 1112, !7, i64 1120, !7, i64 1128, !22, i64 1136, !16, i64 1144, !16, i64 1152, !16, i64 1160, !16, i64 1168, !16, i64 1176, !16, i64 1184, !22, i64 1192, !26, i64 1200, !8, i64 1232} -!19 = !{!"rb_global_vm_lock_struct", !16, i64 0, !8, i64 8, !20, i64 48, !16, i64 64, !22, i64 72, !8, i64 80, !8, i64 128, !22, i64 176, !22, i64 180} -!20 = !{!"list_head", !21, i64 0} -!21 = !{!"list_node", !16, i64 0, !16, i64 8} -!22 = !{!"int", !8, i64 0} -!23 = !{!"long long", !8, i64 0} -!24 = !{!"", !8, i64 0} -!25 = !{!"rb_hook_list_struct", !16, i64 0, !22, i64 8, !22, i64 12, !22, i64 16} -!26 = !{!"", !7, i64 0, !7, i64 8, !7, i64 16, !7, i64 24} -!27 = !{!28, !16, i64 16} -!28 = !{!"rb_execution_context_struct", !16, i64 0, !7, i64 8, !16, i64 16, !16, i64 24, !16, i64 32, !22, i64 40, !22, i64 44, !16, i64 48, !16, i64 56, !16, i64 64, !7, i64 72, !7, i64 80, !16, i64 88, !7, i64 96, !16, i64 104, !16, i64 112, !7, i64 120, !7, i64 128, !8, i64 136, !8, i64 137, !7, i64 144, !29, i64 152} -!29 = !{!"", !16, i64 0, !16, i64 8, !7, i64 16, !8, i64 24} -!30 = !{!31, !16, i64 16} -!31 = !{!"rb_control_frame_struct", !16, i64 0, !16, i64 8, !16, i64 16, !7, i64 24, !16, i64 32, !16, i64 40, !16, i64 48} -!32 = !{!31, !16, i64 32} -!33 = !DILocation(line: 0, scope: !11) -!34 = !{!35} -!35 = distinct !{!35, !36, !"sorbet_T_unsafe: argument 0"} -!36 = distinct !{!36, !"sorbet_T_unsafe"} -!37 = !DILocation(line: 4, column: 6, scope: !11) -!38 = !{!28, !22, i64 40} -!39 = !{!28, !22, i64 44} -!40 = !{!"branch_weights", i32 2000, i32 1} -!41 = !{!28, !16, i64 56} diff --git a/test/testdata/core/fuzz_unparseable.rb b/test/testdata/core/fuzz_unparsable.rb similarity index 100% rename from test/testdata/core/fuzz_unparseable.rb rename to test/testdata/core/fuzz_unparsable.rb diff --git a/test/testdata/definition_validator/abstract_loc_crash__1.rb b/test/testdata/definition_validator/abstract_loc_crash__1.rb new file mode 100644 index 0000000000..742ca10adf --- /dev/null +++ b/test/testdata/definition_validator/abstract_loc_crash__1.rb @@ -0,0 +1,27 @@ +# typed: strict + +# This is a rather large comment at the start of the file which exposes a bug +# where we were accidentally turning a LocOffset from this file into a Loc for +# the other file. With a sufficiently long comment, that causes an array access +# out of bounds. +# This is a rather large comment at the start of the file which exposes a bug +# where we were accidentally turning a LocOffset from this file into a Loc for +# the other file. With a sufficiently long comment, that causes an array access +# out of bounds. +# This is a rather large comment at the start of the file which exposes a bug +# where we were accidentally turning a LocOffset from this file into a Loc for +# the other file. With a sufficiently long comment, that causes an array access +# out of bounds. + +class Parent + extend T::Sig + extend T::Helpers + abstract! + + sig { abstract.void } + def example; end +end + +# The canonical `loc()` of Child is here +class Child < Parent # error: Missing definition for abstract method +end diff --git a/test/testdata/definition_validator/abstract_loc_crash__2.rb b/test/testdata/definition_validator/abstract_loc_crash__2.rb new file mode 100644 index 0000000000..615f79c2d6 --- /dev/null +++ b/test/testdata/definition_validator/abstract_loc_crash__2.rb @@ -0,0 +1,6 @@ +# typed: true + +# This being typed: true means that this is not the canonical loc of `Child` + +class Child < Parent # error: Missing definition for abstract method +end diff --git a/test/testdata/definition_validator/missing_abstract_method_empty_body.rb b/test/testdata/definition_validator/missing_abstract_method_empty_body.rb new file mode 100644 index 0000000000..ba4bc9ce40 --- /dev/null +++ b/test/testdata/definition_validator/missing_abstract_method_empty_body.rb @@ -0,0 +1,13 @@ +# typed: true + +class Parent + extend T::Sig + extend T::Helpers + abstract! + + sig {abstract.void} + def foo; end +end + + class Child < Parent; end +# ^^^^^^^^^^^^^^^^^^^^ error: Missing definition for abstract method diff --git a/test/testdata/definition_validator/missing_abstract_method_empty_body.rb.autocorrects.exp b/test/testdata/definition_validator/missing_abstract_method_empty_body.rb.autocorrects.exp new file mode 100644 index 0000000000..c1dc2b46b8 --- /dev/null +++ b/test/testdata/definition_validator/missing_abstract_method_empty_body.rb.autocorrects.exp @@ -0,0 +1,18 @@ +# -- test/testdata/definition_validator/missing_abstract_method_empty_body.rb -- +# typed: true + +class Parent + extend T::Sig + extend T::Helpers + abstract! + + sig {abstract.void} + def foo; end +end + + class Child < Parent + sig { override.void } + def foo; end + end +# ^^^^^^^^^^^^^^^^^^^^ error: Missing definition for abstract method +# ------------------------------ diff --git a/test/testdata/definition_validator/missing_abstract_method_existing_class_body.rb b/test/testdata/definition_validator/missing_abstract_method_existing_class_body.rb new file mode 100644 index 0000000000..6412adc688 --- /dev/null +++ b/test/testdata/definition_validator/missing_abstract_method_existing_class_body.rb @@ -0,0 +1,25 @@ +# typed: true + +class Parent + extend T::Sig + extend T::Helpers + abstract! + + sig {abstract.void} + def foo; end + + sig {abstract.void} + def bar; end + + sig {abstract.void} + def baz; end +end + + class Child < Parent +# ^^^^^^^^^^^^^^^^^^^^ error: Missing definition for abstract method + sig {override.void} + def foo; end + + sig {override.void} + def baz; end + end diff --git a/test/testdata/definition_validator/missing_abstract_method_existing_class_body.rb.autocorrects.exp b/test/testdata/definition_validator/missing_abstract_method_existing_class_body.rb.autocorrects.exp new file mode 100644 index 0000000000..ed84987578 --- /dev/null +++ b/test/testdata/definition_validator/missing_abstract_method_existing_class_body.rb.autocorrects.exp @@ -0,0 +1,29 @@ +# -- test/testdata/definition_validator/missing_abstract_method_existing_class_body.rb -- +# typed: true + +class Parent + extend T::Sig + extend T::Helpers + abstract! + + sig {abstract.void} + def foo; end + + sig {abstract.void} + def bar; end + + sig {abstract.void} + def baz; end +end + + class Child < Parent +# ^^^^^^^^^^^^^^^^^^^^ error: Missing definition for abstract method + sig {override.void} + def foo; end + + sig {override.void} + def baz; end + sig { override.void } + def bar; end + end +# ------------------------------ diff --git a/test/testdata/definition_validator/missing_abstract_method_long_sig.rb b/test/testdata/definition_validator/missing_abstract_method_long_sig.rb new file mode 100644 index 0000000000..86c059f875 --- /dev/null +++ b/test/testdata/definition_validator/missing_abstract_method_long_sig.rb @@ -0,0 +1,18 @@ +# typed: true + +class Parent + extend T::Sig + extend T::Helpers + abstract! + + sig do + abstract + .params(bar: T::Array[Integer], baz: Symbol, qux: String, quux: T.nilable(Float), corge: T.nilable(T::Boolean)) + .returns(T.nilable(T::Hash[Symbol, T.untyped])) + end + def foo(bar, baz, qux, quux = nil, corge: true); end +end + + class Child < Parent +# ^^^^^^^^^^^^^^^^^^^^ error: Missing definition for abstract method + end diff --git a/test/testdata/definition_validator/missing_abstract_method_long_sig.rb.autocorrects.exp b/test/testdata/definition_validator/missing_abstract_method_long_sig.rb.autocorrects.exp new file mode 100644 index 0000000000..99e78da155 --- /dev/null +++ b/test/testdata/definition_validator/missing_abstract_method_long_sig.rb.autocorrects.exp @@ -0,0 +1,32 @@ +# -- test/testdata/definition_validator/missing_abstract_method_long_sig.rb -- +# typed: true + +class Parent + extend T::Sig + extend T::Helpers + abstract! + + sig do + abstract + .params(bar: T::Array[Integer], baz: Symbol, qux: String, quux: T.nilable(Float), corge: T.nilable(T::Boolean)) + .returns(T.nilable(T::Hash[Symbol, T.untyped])) + end + def foo(bar, baz, qux, quux = nil, corge: true); end +end + + class Child < Parent +# ^^^^^^^^^^^^^^^^^^^^ error: Missing definition for abstract method + sig do + override + .params( + bar: T::Array[Integer], + baz: Symbol, + qux: String, + quux: T.nilable(Float), + corge: T.nilable(T::Boolean) + ) + .returns(T.nilable(T::Hash[Symbol, T.untyped])) + end + def foo(bar, baz, qux, quux, corge:); end + end +# ------------------------------ diff --git a/test/testdata/definition_validator/missing_abstract_method_nested.rb b/test/testdata/definition_validator/missing_abstract_method_nested.rb new file mode 100644 index 0000000000..6006cd3dd0 --- /dev/null +++ b/test/testdata/definition_validator/missing_abstract_method_nested.rb @@ -0,0 +1,24 @@ +# typed: true + +class Grandparent + extend T::Sig + extend T::Helpers + abstract! + + sig {abstract.void} + def foo; end + + sig {abstract.void} + def bar; end +end + +class Parent < Grandparent + abstract! + + sig {override.void} + def foo; end +end + + class Child < Parent +# ^^^^^^^^^^^^^^^^^^^^ error: Missing definition for abstract method + end diff --git a/test/testdata/definition_validator/missing_abstract_method_nested.rb.autocorrects.exp b/test/testdata/definition_validator/missing_abstract_method_nested.rb.autocorrects.exp new file mode 100644 index 0000000000..55c07878c0 --- /dev/null +++ b/test/testdata/definition_validator/missing_abstract_method_nested.rb.autocorrects.exp @@ -0,0 +1,28 @@ +# -- test/testdata/definition_validator/missing_abstract_method_nested.rb -- +# typed: true + +class Grandparent + extend T::Sig + extend T::Helpers + abstract! + + sig {abstract.void} + def foo; end + + sig {abstract.void} + def bar; end +end + +class Parent < Grandparent + abstract! + + sig {override.void} + def foo; end +end + + class Child < Parent +# ^^^^^^^^^^^^^^^^^^^^ error: Missing definition for abstract method + sig { override.void } + def bar; end + end +# ------------------------------ diff --git a/test/testdata/definition_validator/missing_abstract_method_short_sig.rb b/test/testdata/definition_validator/missing_abstract_method_short_sig.rb new file mode 100644 index 0000000000..164885b6fd --- /dev/null +++ b/test/testdata/definition_validator/missing_abstract_method_short_sig.rb @@ -0,0 +1,14 @@ +# typed: true + +class Parent + extend T::Sig + extend T::Helpers + abstract! + + sig {abstract.params(bar: Integer).returns(String)} + def foo(bar); end +end + + class Child < Parent +# ^^^^^^^^^^^^^^^^^^^^ error: Missing definition for abstract method + end diff --git a/test/testdata/definition_validator/missing_abstract_method_short_sig.rb.autocorrects.exp b/test/testdata/definition_validator/missing_abstract_method_short_sig.rb.autocorrects.exp new file mode 100644 index 0000000000..1e65ae2efa --- /dev/null +++ b/test/testdata/definition_validator/missing_abstract_method_short_sig.rb.autocorrects.exp @@ -0,0 +1,18 @@ +# -- test/testdata/definition_validator/missing_abstract_method_short_sig.rb -- +# typed: true + +class Parent + extend T::Sig + extend T::Helpers + abstract! + + sig {abstract.params(bar: Integer).returns(String)} + def foo(bar); end +end + + class Child < Parent +# ^^^^^^^^^^^^^^^^^^^^ error: Missing definition for abstract method + sig { override.params(bar: Integer).returns(String) } + def foo(bar); end + end +# ------------------------------ diff --git a/test/testdata/definition_validator/missing_abstract_via_extend.rb b/test/testdata/definition_validator/missing_abstract_via_extend.rb new file mode 100644 index 0000000000..4c39b136e3 --- /dev/null +++ b/test/testdata/definition_validator/missing_abstract_via_extend.rb @@ -0,0 +1,15 @@ +# typed: true +class Module; include T::Sig; end + +module IFoo + extend T::Helpers + abstract! + + sig {abstract.void} + def foo + end +end + +class A # error: Missing definition for abstract method `IFoo#foo` + extend IFoo +end diff --git a/test/testdata/definition_validator/missing_abstract_via_extend.rb.autocorrects.exp b/test/testdata/definition_validator/missing_abstract_via_extend.rb.autocorrects.exp new file mode 100644 index 0000000000..a3392ca3b2 --- /dev/null +++ b/test/testdata/definition_validator/missing_abstract_via_extend.rb.autocorrects.exp @@ -0,0 +1,19 @@ +# -- test/testdata/definition_validator/missing_abstract_via_extend.rb -- +# typed: true +class Module; include T::Sig; end + +module IFoo + extend T::Helpers + abstract! + + sig {abstract.void} + def foo + end +end + +class A # error: Missing definition for abstract method `IFoo#foo` + extend IFoo + sig { override.void } + def self.foo; end +end +# ------------------------------ diff --git a/test/testdata/definition_validator/subclass_t_struct_multi_file.autocorrects.exp b/test/testdata/definition_validator/subclass_t_struct_multi_file.autocorrects.exp new file mode 100644 index 0000000000..c2ac31f6d5 --- /dev/null +++ b/test/testdata/definition_validator/subclass_t_struct_multi_file.autocorrects.exp @@ -0,0 +1,21 @@ +# -- test/testdata/definition_validator/subclass_t_struct_multi_file__1.rb -- +# typed: true +# enable-suggest-unsafe: true + +class Parent < T::InexactStruct +end +# ------------------------------ +# -- test/testdata/definition_validator/subclass_t_struct_multi_file__2.rb -- +# typed: true + +class Child2 < Parent # error: Subclassing `Parent` is not allowed +end + +# ------------------------------ +# -- test/testdata/definition_validator/subclass_t_struct_multi_file__3.rb -- +# typed: true + +class Child3 < Parent # error: Subclassing `Parent` is not allowed +end + +# ------------------------------ diff --git a/test/testdata/definition_validator/subclass_t_struct_multi_file__1.rb b/test/testdata/definition_validator/subclass_t_struct_multi_file__1.rb new file mode 100644 index 0000000000..8a23284323 --- /dev/null +++ b/test/testdata/definition_validator/subclass_t_struct_multi_file__1.rb @@ -0,0 +1,5 @@ +# typed: true +# enable-suggest-unsafe: true + +class Parent < T::Struct +end diff --git a/test/testdata/definition_validator/subclass_t_struct_multi_file__2.rb b/test/testdata/definition_validator/subclass_t_struct_multi_file__2.rb new file mode 100644 index 0000000000..5ba2451959 --- /dev/null +++ b/test/testdata/definition_validator/subclass_t_struct_multi_file__2.rb @@ -0,0 +1,5 @@ +# typed: true + +class Child2 < Parent # error: Subclassing `Parent` is not allowed +end + diff --git a/test/testdata/definition_validator/subclass_t_struct_multi_file__3.rb b/test/testdata/definition_validator/subclass_t_struct_multi_file__3.rb new file mode 100644 index 0000000000..4c44247675 --- /dev/null +++ b/test/testdata/definition_validator/subclass_t_struct_multi_file__3.rb @@ -0,0 +1,5 @@ +# typed: true + +class Child3 < Parent # error: Subclassing `Parent` is not allowed +end + diff --git a/test/testdata/definition_validator/subclass_t_struct_simple.rb b/test/testdata/definition_validator/subclass_t_struct_simple.rb new file mode 100644 index 0000000000..802a8fa541 --- /dev/null +++ b/test/testdata/definition_validator/subclass_t_struct_simple.rb @@ -0,0 +1,12 @@ +# typed: true +# enable-suggest-unsafe: true + +class Parent < T::Struct +end + +class Child < Parent # error: Subclassing `Parent` is not allowed +end + +# autocorrect not implemented +Child2 = Class.new(Parent) do # error: Subclassing `Parent` is not allowed +end diff --git a/test/testdata/definition_validator/subclass_t_struct_simple.rb.autocorrects.exp b/test/testdata/definition_validator/subclass_t_struct_simple.rb.autocorrects.exp new file mode 100644 index 0000000000..9a47f37b7f --- /dev/null +++ b/test/testdata/definition_validator/subclass_t_struct_simple.rb.autocorrects.exp @@ -0,0 +1,14 @@ +# -- test/testdata/definition_validator/subclass_t_struct_simple.rb -- +# typed: true +# enable-suggest-unsafe: true + +class Parent < T::InexactStruct +end + +class Child < Parent # error: Subclassing `Parent` is not allowed +end + +# autocorrect not implemented +Child2 = Class.new(Parent) do # error: Subclassing `Parent` is not allowed +end +# ------------------------------ diff --git a/test/testdata/definition_validator/superclass_class_loc__1.rb b/test/testdata/definition_validator/superclass_class_loc__1.rb new file mode 100644 index 0000000000..9afa5050d3 --- /dev/null +++ b/test/testdata/definition_validator/superclass_class_loc__1.rb @@ -0,0 +1,7 @@ +# typed: true + +module Parent +end + +class Child < Parent # error: does not derive from `Class` +end diff --git a/test/testdata/definition_validator/superclass_class_loc__2.rbi b/test/testdata/definition_validator/superclass_class_loc__2.rbi new file mode 100644 index 0000000000..140f3d51e7 --- /dev/null +++ b/test/testdata/definition_validator/superclass_class_loc__2.rbi @@ -0,0 +1,18 @@ +# typed: true + +# This is a rather large comment at the start of the file which exposes a bug +# where we were accidentally turning a LocOffset from this file into a Loc for +# the other file. With a sufficiently long comment, that causes an array access +# out of bounds. +# This is a rather large comment at the start of the file which exposes a bug +# where we were accidentally turning a LocOffset from this file into a Loc for +# the other file. With a sufficiently long comment, that causes an array access +# out of bounds. +# This is a rather large comment at the start of the file which exposes a bug +# where we were accidentally turning a LocOffset from this file into a Loc for +# the other file. With a sufficiently long comment, that causes an array access +# out of bounds. + +class Child < Parent # error: does not derive from `Class` + def example; end +end diff --git a/test/testdata/definition_validator/void_anything.rb b/test/testdata/definition_validator/void_anything.rb new file mode 100644 index 0000000000..96ba179efd --- /dev/null +++ b/test/testdata/definition_validator/void_anything.rb @@ -0,0 +1,26 @@ +# typed: true + +class Parent + extend T::Sig, T::Helpers + abstract! + + sig { abstract.void } + def example1; end + + sig { abstract.returns(T.anything) } + def example2; end + + sig { abstract.returns(Integer) } + def example3; end +end + +class Child < Parent + sig { override.returns(T.anything) } + def example1; end + + sig { override.void } + def example2; end + + sig { override.void } + def example3; end # error: Return type `void` does not match return type of abstract method `Parent#example3` +end diff --git a/test/testdata/desugar/assign_empty_stmts.rb b/test/testdata/desugar/assign_empty_stmts.rb index 277e31daa9..d166ab5e84 100644 --- a/test/testdata/desugar/assign_empty_stmts.rb +++ b/test/testdata/desugar/assign_empty_stmts.rb @@ -1,3 +1,3 @@ # typed: strict -U = begin # error: Constants must have type annotations with `T.let` when specifying `# typed: strict` +U = begin end diff --git a/test/testdata/desugar/destructure.rb.flatten-tree.exp b/test/testdata/desugar/destructure.rb.flatten-tree.exp index 54c4cc82e5..51e1bb0428 100644 --- a/test/testdata/desugar/destructure.rb.flatten-tree.exp +++ b/test/testdata/desugar/destructure.rb.flatten-tree.exp @@ -1,12 +1,9 @@ begin - class <>> < (::) + + def self.<$CENSORED>() - begin - - ::Sorbet::Private::Static.keep_for_ide(::Destructure) - - end + end end class ::Destructure<> < (::) diff --git a/test/testdata/desugar/ensure_without_rescue.rb.cfg-text.exp b/test/testdata/desugar/ensure_without_rescue.rb.cfg-text.exp index e6a07aeab3..cd12539d07 100644 --- a/test/testdata/desugar/ensure_without_rescue.rb.cfg-text.exp +++ b/test/testdata/desugar/ensure_without_rescue.rb.cfg-text.exp @@ -3,8 +3,8 @@ method ::Object#main { bb0[rubyRegionId=0, firstDead=-1](): : Object = cast(: NilClass, Object); $5: T.class_of() = alias > - $3: T.untyped = - $3 -> (T.untyped ? bb3 : bb4) + $3: T.nilable(Exception) = + $3 -> (T.nilable(Exception) ? bb3 : bb4) # backedges # - bb6(rubyRegionId=3) @@ -23,8 +23,8 @@ bb3[rubyRegionId=2, firstDead=-1](: Object, $2: T.untype # - bb0(rubyRegionId=0) bb4[rubyRegionId=1, firstDead=-1](: Object): $2: T.untyped = : Object.a() - $3: T.untyped = - $3 -> (T.untyped ? bb3 : bb5) + $3: T.nilable(Exception) = + $3 -> (T.nilable(Exception) ? bb3 : bb5) # backedges # - bb4(rubyRegionId=1) diff --git a/test/testdata/desugar/for.rb.cfg-text.exp b/test/testdata/desugar/for.rb.cfg-text.exp index d90568fbe5..7e9fa0b208 100644 --- a/test/testdata/desugar/for.rb.cfg-text.exp +++ b/test/testdata/desugar/for.rb.cfg-text.exp @@ -1,19 +1,10 @@ method ::># { -bb0[rubyRegionId=0, firstDead=13](): +bb0[rubyRegionId=0, firstDead=4](): : T.class_of() = cast(: NilClass, T.class_of()); - $7: T.class_of(Sorbet::Private::Static) = alias - $9: T.class_of(A) = alias - $5: Sorbet::Private::Static::Void = $7: T.class_of(Sorbet::Private::Static).keep_for_ide($9: T.class_of(A)) - $14: T.class_of(Sorbet::Private::Static) = alias - $16: T.class_of(E) = alias - $12: Sorbet::Private::Static::Void = $14: T.class_of(Sorbet::Private::Static).keep_for_ide($16: T.class_of(E)) - $21: T.class_of(Sorbet::Private::Static) = alias - $23: T.class_of(Main) = alias - $19: Sorbet::Private::Static::Void = $21: T.class_of(Sorbet::Private::Static).keep_for_ide($23: T.class_of(Main)) - $26: T.class_of(Main) = alias - $24: T.untyped = $26: T.class_of(Main).main() - : T.noreturn = return $2: NilClass + $4: T.class_of(Main) = alias + $2: T.untyped = $4: T.class_of(Main).main() + : T.noreturn = return $2: T.untyped -> bb1 # backedges diff --git a/test/testdata/desugar/magic_super.rb b/test/testdata/desugar/magic_super.rb index 20cc5ba037..da95b49f71 100644 --- a/test/testdata/desugar/magic_super.rb +++ b/test/testdata/desugar/magic_super.rb @@ -15,11 +15,7 @@ def nullary end def unary(x) - # Reporting no error here is actually incorrect, but consistently incorrect - # - # (it doesn't have to do with conflating `super()` and `()` but - # rather that we don't type check `` at all. - # See https://github.com/sorbet/sorbet/issues/1068) super(1, 2) + # ^ error: Too many arguments provided for method `Parent#unary` end end diff --git a/test/testdata/desugar/multi_assign.rb b/test/testdata/desugar/multi_assign.rb index 5466016f97..93606992dc 100644 --- a/test/testdata/desugar/multi_assign.rb +++ b/test/testdata/desugar/multi_assign.rb @@ -10,5 +10,7 @@ def some_method(array) a, b, *c, d, e = array a, * = array a.x, b = array + a, *b = *T.unsafe(array) + *a, b = *T.unsafe(array) end end diff --git a/test/testdata/desugar/multi_assign.rb.desugar-tree.exp b/test/testdata/desugar/multi_assign.rb.desugar-tree.exp index 8757ebd65a..b8ddc1d9ff 100644 --- a/test/testdata/desugar/multi_assign.rb.desugar-tree.exp +++ b/test/testdata/desugar/multi_assign.rb.desugar-tree.exp @@ -36,13 +36,13 @@ class <>> < (::) $8 = array $9 = ::.($8, 1, 0) a = $9.[](0) - b = $9.slice(::Range.new(1, -1, false)) + b = $9.to_ary() $8 end begin $10 = array $11 = ::.($10, 0, 1) - a = $11.slice(::Range.new(0, -1, true)) + a = $11.to_ary() b = $11.[](-1) $10 end @@ -51,7 +51,7 @@ class <>> < (::) $13 = ::.($12, 2, 2) a = $13.[](0) b = $13.[](1) - c = $13.slice(::Range.new(2, -2, true)) + c = $13.to_ary() d = $13.[](-2) e = $13.[](-1) $12 @@ -69,6 +69,20 @@ class <>> < (::) b = $17.[](1) $16 end + begin + $18 = ::.(::.unsafe(array)) + $19 = ::.($18, 1, 0) + a = $19.[](0) + b = $19.to_ary() + $18 + end + begin + $20 = ::.(::.unsafe(array)) + $21 = ::.($20, 0, 1) + a = $21.to_ary() + b = $21.[](-1) + $20 + end end end end diff --git a/test/testdata/desugar/multi_assign.rb.symbol-table-raw.exp b/test/testdata/desugar/multi_assign.rb.symbol-table-raw.exp index b8886ef5b9..341de21880 100644 --- a/test/testdata/desugar/multi_assign.rb.symbol-table-raw.exp +++ b/test/testdata/desugar/multi_assign.rb.symbol-table-raw.exp @@ -1,6 +1,6 @@ class >> < > () class >> $1>[>>] < > $1> () - method >> $1>#> $CENSORED> () @ Loc {file=test/testdata/desugar/multi_assign.rb start=2:1 end=14:4} + method >> $1>#> $CENSORED> () @ Loc {file=test/testdata/desugar/multi_assign.rb start=2:1 end=16:4} argument @ Loc {file=test/testdata/desugar/multi_assign.rb start=??? end=???} static-field > @ Loc {file=test/testdata/desugar/multi_assign.rb start=2:1 end=2:2} static-field > @ Loc {file=test/testdata/desugar/multi_assign.rb start=2:4 end=2:5} @@ -11,6 +11,6 @@ class >> < > () argument @ Loc {file=test/testdata/desugar/multi_assign.rb start=??? end=???} class > $1>[>>] < > $1> () @ Loc {file=test/testdata/desugar/multi_assign.rb start=4:1 end=4:11} type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=Test) @ Loc {file=test/testdata/desugar/multi_assign.rb start=4:1 end=4:11} - method > $1>#> () @ Loc {file=test/testdata/desugar/multi_assign.rb start=4:1 end=14:4} + method > $1>#> () @ Loc {file=test/testdata/desugar/multi_assign.rb start=4:1 end=16:4} argument @ Loc {file=test/testdata/desugar/multi_assign.rb start=??? end=???} diff --git a/test/testdata/desugar/op_eq.rb.flatten-tree.exp b/test/testdata/desugar/op_eq.rb.flatten-tree.exp index 6b54833bfd..e6944b9406 100644 --- a/test/testdata/desugar/op_eq.rb.flatten-tree.exp +++ b/test/testdata/desugar/op_eq.rb.flatten-tree.exp @@ -1,12 +1,9 @@ begin - class <>> < (::) + + def self.<$CENSORED>() - begin - - ::Sorbet::Private::Static.keep_for_ide(::OpEq) - - end + end end class ::OpEq<> < (::) diff --git a/test/testdata/desugar/pattern_matching_assign.rb b/test/testdata/desugar/pattern_matching_assign.rb index 2fdcd66411..d086396154 100644 --- a/test/testdata/desugar/pattern_matching_assign.rb +++ b/test/testdata/desugar/pattern_matching_assign.rb @@ -10,7 +10,7 @@ in "b" => x # local var different from the assign line 6 T.reveal_type(x) # error: Revealed type: `T.untyped` 1 -in "c" => expr # local var hidding the assign line 3 +in "c" => expr # local var hiding the assign line 3 T.reveal_type(expr) # error: Revealed type: `T.untyped` 1 in "d" diff --git a/test/testdata/desugar/sclass.rb.flatten-tree.exp b/test/testdata/desugar/sclass.rb.flatten-tree.exp index 054b2b5436..41437a9a0a 100644 --- a/test/testdata/desugar/sclass.rb.flatten-tree.exp +++ b/test/testdata/desugar/sclass.rb.flatten-tree.exp @@ -1,5 +1,4 @@ begin - class <>> < (::) def main() begin @@ -14,44 +13,23 @@ begin end end + + + + + + + + + + + + + + def self.<$CENSORED>() begin - begin - - ::Sorbet::Private::Static.keep_for_ide(::A) - - end - begin - - ::Sorbet::Private::Static.keep_for_ide(::B) - - end $c = ::Object.new() - begin - - ::Sorbet::Private::Static.keep_for_ide(::D) - - end - begin - - ::Sorbet::Private::Static.keep_for_ide(::E) - - end - begin - - ::Sorbet::Private::Static.keep_for_ide(::F) - - end - begin - - ::Sorbet::Private::Static.keep_for_ide(::G) - - end - begin - - ::Sorbet::Private::Static.keep_for_ide(::H) - - end .main() @@ -64,11 +42,10 @@ begin end end class ::B<> < (::) + + def self.() - begin - - - end + end end class <> < () @@ -81,19 +58,17 @@ begin end end class ::D<> < (::) + + def self.() - begin - - - end + end end class <> < () + + def self.() - begin - - - end + end end class <>> < () @@ -106,15 +81,10 @@ begin end end class ::E<> < (::) + + def self.() - begin - begin - - - end - .wrapper() - - end + .wrapper() end end class <> < () @@ -131,11 +101,10 @@ begin end end class ::F<> < (::) + + def self.() - begin - - - end + end end class <> < () @@ -164,16 +133,14 @@ begin .inner() end + + def self.g() "g" end def self.() begin - begin - - - end @@ -190,20 +157,17 @@ begin end end class ::H<> < (::) + + def self.() - begin - - - end + end end class <> < () + + def self.() - begin - - ::Sorbet::Private::Static.keep_for_ide(::::H2) - - end + end end class ::::H2<> < (::) diff --git a/test/testdata/desugar/sclass.rb.symbol-table-raw.exp b/test/testdata/desugar/sclass.rb.symbol-table-raw.exp index f799ac1825..66a61dd1a7 100644 --- a/test/testdata/desugar/sclass.rb.symbol-table-raw.exp +++ b/test/testdata/desugar/sclass.rb.symbol-table-raw.exp @@ -92,7 +92,7 @@ class >> < > () type-member(+) > $1> $1>::>> -> LambdaParam(> $1> $1>::>>, lower=T.noreturn, upper=AppliedType { klass = > $1> targs = [ >> = H ] }) @ Loc {file=test/testdata/desugar/sclass.rb start=75:5 end=75:10} method > $1> $1>#> () @ Loc {file=test/testdata/desugar/sclass.rb start=75:5 end=81:8} argument @ Loc {file=test/testdata/desugar/sclass.rb start=??? end=???} - class > < > (>) @ Loc {file=https://github.com/sorbet/sorbet/tree/master/rbi/core/object.rbi start=removed end=removed} + class > < > (>, >) @ (Loc {file=https://github.com/sorbet/sorbet/tree/master/rbi/stdlib/json.rbi start=removed end=removed}, Loc {file=https://github.com/sorbet/sorbet/tree/master/rbi/core/object.rbi start=removed end=removed}) method ># : private () @ Loc {file=test/testdata/desugar/sclass.rb start=84:1 end=84:9} argument @ Loc {file=test/testdata/desugar/sclass.rb start=??? end=???} diff --git a/test/testdata/desugar/sclass_inheritance.rb.flatten-tree.exp b/test/testdata/desugar/sclass_inheritance.rb.flatten-tree.exp index cf9148add9..b652dcdbd4 100644 --- a/test/testdata/desugar/sclass_inheritance.rb.flatten-tree.exp +++ b/test/testdata/desugar/sclass_inheritance.rb.flatten-tree.exp @@ -1,5 +1,4 @@ begin - class <>> < (::) def main() begin @@ -9,29 +8,16 @@ begin end end + + + + + + + + def self.<$CENSORED>() begin - begin - - ::Sorbet::Private::Static.keep_for_ide(::MM) - - end - begin - - ::Sorbet::Private::Static.keep_for_ide(::A) - - end - begin - - ::Sorbet::Private::Static.keep_for_ide(::B) - - end - begin - - ::Sorbet::Private::Static.keep_for_ide(::C) - ::Sorbet::Private::Static.keep_for_ide(::A) - - end .main() @@ -44,12 +30,10 @@ begin end end class ::A<> < (::) + + def self.() - begin - - ::Sorbet::Private::Static.keep_for_ide(::MM) - - end + end end class <> < (::MM) @@ -79,11 +63,10 @@ begin end end class ::C<> < (::A) + + def self.() - begin - - - end + end end class <> < () diff --git a/test/testdata/desugar/sclass_inheritance.rb.symbol-table-raw.exp b/test/testdata/desugar/sclass_inheritance.rb.symbol-table-raw.exp index 93ac75aee1..6f1eb0a204 100644 --- a/test/testdata/desugar/sclass_inheritance.rb.symbol-table-raw.exp +++ b/test/testdata/desugar/sclass_inheritance.rb.symbol-table-raw.exp @@ -35,7 +35,7 @@ class >> < > () class > $1> < > () @ Loc {file=test/testdata/desugar/sclass_inheritance.rb start=2:1 end=2:10} method > $1>#> () @ Loc {file=test/testdata/desugar/sclass_inheritance.rb start=2:1 end=2:15} argument @ Loc {file=test/testdata/desugar/sclass_inheritance.rb start=??? end=???} - class > < > (>) @ Loc {file=https://github.com/sorbet/sorbet/tree/master/rbi/core/object.rbi start=removed end=removed} + class > < > (>, >) @ (Loc {file=https://github.com/sorbet/sorbet/tree/master/rbi/stdlib/json.rbi start=removed end=removed}, Loc {file=https://github.com/sorbet/sorbet/tree/master/rbi/core/object.rbi start=removed end=removed}) method ># : private () @ Loc {file=test/testdata/desugar/sclass_inheritance.rb start=30:1 end=30:9} argument @ Loc {file=test/testdata/desugar/sclass_inheritance.rb start=??? end=???} diff --git a/test/testdata/deviations/keyword_method_names.rb b/test/testdata/deviations/keyword_method_names.rb index 32ce5a3dd1..11f568a133 100644 --- a/test/testdata/deviations/keyword_method_names.rb +++ b/test/testdata/deviations/keyword_method_names.rb @@ -6,7 +6,7 @@ # keyword was both (1) being used as a method name and (2) broken across two # lines like this. # -# We decided that this was a worthwile tradeoff, because it makes it far easier +# We decided that this was a worthwhile tradeoff, because it makes it far easier # for the parser to recover gracefully and locally from syntax errors. def method_named_alias(x) diff --git a/test/testdata/deviations/non_ruby_names.rb.flatten-tree.exp b/test/testdata/deviations/non_ruby_names.rb.flatten-tree.exp index 83e12670c1..ded0b1082b 100644 --- a/test/testdata/deviations/non_ruby_names.rb.flatten-tree.exp +++ b/test/testdata/deviations/non_ruby_names.rb.flatten-tree.exp @@ -1,20 +1,11 @@ begin - class <>> < (::) + + + + def self.<$CENSORED>() - begin - begin - - ::Sorbet::Private::Static.keep_for_ide(::B) - - end - begin - - ::Sorbet::Private::Static.keep_for_ide(::A) - - end - - end + end end module ::B<> < () @@ -23,12 +14,10 @@ begin end end module ::A<> < () + + def self.() - begin - - ::Sorbet::Private::Static.keep_for_ide(::A::B::C) - - end + end end class ::A::B::C<> < (::) diff --git a/test/testdata/deviations/superclass_implicit.rb.symbol-table-raw.exp b/test/testdata/deviations/superclass_implicit.rb.symbol-table-raw.exp index 438b6028eb..ad5b9ad555 100644 --- a/test/testdata/deviations/superclass_implicit.rb.symbol-table-raw.exp +++ b/test/testdata/deviations/superclass_implicit.rb.symbol-table-raw.exp @@ -4,8 +4,8 @@ class >> < > () argument @ Loc {file=test/testdata/deviations/superclass_implicit.rb start=??? end=???} class > < > () @ Loc {file=test/testdata/deviations/superclass_implicit.rb start=14:1 end=14:12} class > $1>[>>] < > $1> () @ Loc {file=test/testdata/deviations/superclass_implicit.rb start=14:1 end=14:12} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=A) @ Loc {file=test/testdata/deviations/superclass_implicit.rb start=11:1 end=11:8} - method > $1>#> () @ Loc {file=test/testdata/deviations/superclass_implicit.rb start=11:1 end=12:4} + type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=A) @ Loc {file=test/testdata/deviations/superclass_implicit.rb start=14:1 end=14:12} + method > $1>#> () @ Loc {file=test/testdata/deviations/superclass_implicit.rb start=14:1 end=15:4} argument @ Loc {file=test/testdata/deviations/superclass_implicit.rb start=??? end=???} class > < > () @ Loc {file=test/testdata/deviations/superclass_implicit.rb start=8:1 end=8:8} class > $1>[>>] < > $1> () @ Loc {file=test/testdata/deviations/superclass_implicit.rb start=8:1 end=8:8} diff --git a/test/testdata/infer/any_all_module_type_paramter.rb b/test/testdata/infer/any_all_module_type_parameter.rb similarity index 100% rename from test/testdata/infer/any_all_module_type_paramter.rb rename to test/testdata/infer/any_all_module_type_parameter.rb diff --git a/test/testdata/infer/arg_matching.rb b/test/testdata/infer/arg_matching.rb index 5a866e3b98..27576c162d 100644 --- a/test/testdata/infer/arg_matching.rb +++ b/test/testdata/infer/arg_matching.rb @@ -48,7 +48,7 @@ def call_kwarg kwarg(1, b: 2) kwarg(1, b: 2, c: 3) - # ^^^^^^^^^^^^^^^^^^^^ error: Unrecognized keyword argument `c` + # ^^^^ error: Unrecognized keyword argument `c` kwarg(1, {}) # ^^^^^ error: Missing required keyword argument `b` kwarg(1, b: "hi") diff --git a/test/testdata/infer/arg_matching.rb.autocorrects.exp b/test/testdata/infer/arg_matching.rb.autocorrects.exp new file mode 100644 index 0000000000..e9a972c1c9 --- /dev/null +++ b/test/testdata/infer/arg_matching.rb.autocorrects.exp @@ -0,0 +1,117 @@ +# -- test/testdata/infer/arg_matching.rb -- +# typed: true +class TestArgs + extend T::Sig + + def any; end + + sig {returns(T::Hash[Integer, Integer])} + def a_hash; end # error: Expected `T::Hash[Integer, Integer]` but found `NilClass` for method result type + + def required(a, b) + end + + def call_required + required(1) + # ^ error: Not enough arguments + required(1, 2) + required(1, 2) + # ^ error: Expected: `2`, got: `3` + end + + def optional(a, b=1) + end + + def call_optional + optional(1) + optional(1, 2) + optional(1, 2) + # ^ error: Expected: `1..2`, got: `3` + end + + sig do + params( + a: Integer, + b: Integer, + ) + .returns(NilClass) + end + def kwarg(a, b:) + end + + def call_kwarg + # "too many arguments" and "missing keyword argument b" + kwarg(1) + # ^ error: Too many positional arguments provided for method `TestArgs#kwarg`. Expected: `1`, got: `2` + # ^^^^ error: Missing required keyword argument `b` for method `TestArgs#kwarg` + kwarg(1) + # ^ error: Missing required keyword argument `b` + + kwarg(1, b: 2) + kwarg(1, b: 2) + # ^^^^ error: Unrecognized keyword argument `c` + kwarg(1, {}) + # ^^^^^ error: Missing required keyword argument `b` + kwarg(1, b: "hi") + # ^^^^ error: Expected `Integer` but found `String("hi")` for argument `b` + kwarg(1, any) + kwarg(1, **T.unsafe(a_hash)) + # ^^^^^^ error: Cannot call `TestArgs#kwarg` with a `Hash` keyword splat because the method has required keyword parameters + end + + sig do + params( + x: Integer + ) + .returns(NilClass) + end + def repeated(*x) + end + + def call_repeated + repeated + repeated(1, 2, 3) + repeated(1, "hi") + # ^^^^ error: Expected `Integer` but found `String("hi")` for argument `x` + + # We error on each incorrect argument + repeated("hi", "there") + # ^^^^ error: Expected `Integer` but found `String("hi")` for argument `x` + # ^^^^^^^ error: Expected `Integer` but found `String("there")` for argument `x` + end + + sig do + params( + x: Integer, + y: Integer, + z: T::Hash[Integer, Integer], + w: String, + u: Integer, + v: Integer + ) + .returns(NilClass) + end + def mixed(x, y=T.unsafe(nil), z=T.unsafe(nil), *w, u:, v: 0) + end + + def call_mixed + mixed(0, u: 1) + mixed(0, 1, u: 1) + mixed(0, 1, {z: 1}, u: 1) + mixed(0, 1, {z: 1}, "hi", "there", u: 1, v: 0) + end + + def optkw(x, y=T.unsafe(nil), u:) + end + + def call_optkw + # There's ambiguity here about whether to report `u` or `x` as + # missing; We follow Ruby in complaining about `u`. + optkw(u: 1) + # ^^^^ error: Missing required keyword argument `u` + optkw(1, 2) + # ^^^^^^^ error: Missing required keyword argument `u` for method `TestArgs#optkw` + # ^ error: Too many positional arguments provided for method `TestArgs#optkw`. Expected: `1..2`, got: `3` + end +end +# ------------------------------ diff --git a/test/testdata/infer/block_arg.rb b/test/testdata/infer/block_arg.rb index 4047884db2..fb327f8bcb 100644 --- a/test/testdata/infer/block_arg.rb +++ b/test/testdata/infer/block_arg.rb @@ -57,7 +57,7 @@ def initialize(&blk) # Constructors are dispatched via a different code path; ensure that # it knows how to enter blocks. def test_constructors - NoConstructor.new do |x| + NoConstructor.new do |x| # error: `BasicObject#initialize` does not take a block x + 1 end diff --git a/test/testdata/infer/blocks2.rb.cfg-text.exp b/test/testdata/infer/blocks2.rb.cfg-text.exp index 1fda8b74e2..36446e254d 100644 --- a/test/testdata/infer/blocks2.rb.cfg-text.exp +++ b/test/testdata/infer/blocks2.rb.cfg-text.exp @@ -1,10 +1,7 @@ method ::># { -bb0[rubyRegionId=0, firstDead=5](): +bb0[rubyRegionId=0, firstDead=2](): : T.class_of() = cast(: NilClass, T.class_of()); - $6: T.class_of(Sorbet::Private::Static) = alias - $8: T.class_of(Foo) = alias - $4: Sorbet::Private::Static::Void = $6: T.class_of(Sorbet::Private::Static).keep_for_ide($8: T.class_of(Foo)) : T.noreturn = return $2: NilClass -> bb1 diff --git a/test/testdata/infer/bound_proc.rb b/test/testdata/infer/bound_proc.rb index b4621f03ca..f6df97d188 100644 --- a/test/testdata/infer/bound_proc.rb +++ b/test/testdata/infer/bound_proc.rb @@ -175,3 +175,13 @@ def baz end end end + +class UntypedBind + def foo + T.reveal_type(self) # error: type: `UntypedBind` + T.bind(self, T.untyped) + T.reveal_type(self) # error: type: `T.untyped` + T.bind(self, UntypedBind) + T.reveal_type(self) # error: type: `UntypedBind` + end +end diff --git a/test/testdata/infer/bound_proc.rb.cfg-text.exp b/test/testdata/infer/bound_proc.rb.cfg-text.exp index 9d5476c7dc..c58de05268 100644 --- a/test/testdata/infer/bound_proc.rb.cfg-text.exp +++ b/test/testdata/infer/bound_proc.rb.cfg-text.exp @@ -2,42 +2,9 @@ method ::># { bb0[rubyRegionId=0, firstDead=-1](): : T.class_of() = cast(: NilClass, T.class_of()); - $7: T.class_of(Sorbet::Private::Static) = alias - $9: T.class_of(Base) = alias - $5: Sorbet::Private::Static::Void = $7: T.class_of(Sorbet::Private::Static).keep_for_ide($9: T.class_of(Base)) - $14: T.class_of(Sorbet::Private::Static) = alias - $16: T.class_of(Concern) = alias - $12: Sorbet::Private::Static::Void = $14: T.class_of(Sorbet::Private::Static).keep_for_ide($16: T.class_of(Concern)) - $21: T.class_of(Sorbet::Private::Static) = alias - $23: T.class_of(Readable) = alias - $19: Sorbet::Private::Static::Void = $21: T.class_of(Sorbet::Private::Static).keep_for_ide($23: T.class_of(Readable)) - $28: T.class_of(Sorbet::Private::Static) = alias - $30: T.class_of(Writable) = alias - $26: Sorbet::Private::Static::Void = $28: T.class_of(Sorbet::Private::Static).keep_for_ide($30: T.class_of(Writable)) - $35: T.class_of(Sorbet::Private::Static) = alias - $37: T.class_of(Shareable) = alias - $33: Sorbet::Private::Static::Void = $35: T.class_of(Sorbet::Private::Static).keep_for_ide($37: T.class_of(Shareable)) - $42: T.class_of(Sorbet::Private::Static) = alias - $44: T.class_of(Post) = alias - $40: Sorbet::Private::Static::Void = $42: T.class_of(Sorbet::Private::Static).keep_for_ide($44: T.class_of(Post)) - $47: T.class_of(Sorbet::Private::Static) = alias - $49: T.class_of(Base) = alias - $45: Sorbet::Private::Static::Void = $47: T.class_of(Sorbet::Private::Static).keep_for_ide($49: T.class_of(Base)) - $54: T.class_of(Sorbet::Private::Static) = alias - $56: T.class_of(Article) = alias - $52: Sorbet::Private::Static::Void = $54: T.class_of(Sorbet::Private::Static).keep_for_ide($56: T.class_of(Article)) - $59: T.class_of(Sorbet::Private::Static) = alias - $61: T.class_of(Base) = alias - $57: Sorbet::Private::Static::Void = $59: T.class_of(Sorbet::Private::Static).keep_for_ide($61: T.class_of(Base)) - $66: T.class_of(Sorbet::Private::Static) = alias - $68: T.class_of(A) = alias - $64: Sorbet::Private::Static::Void = $66: T.class_of(Sorbet::Private::Static).keep_for_ide($68: T.class_of(A)) - $73: T.class_of(Sorbet::Private::Static) = alias - $75: T.class_of(B) = alias - $71: Sorbet::Private::Static::Void = $73: T.class_of(Sorbet::Private::Static).keep_for_ide($75: T.class_of(B)) - $78: T.class_of(B) = alias - $79: Sorbet::Private::Static::Void = $78: T.class_of(B).class_helper() - $80: T.class_of() = + $5: T.class_of(B) = alias + $6: Sorbet::Private::Static::Void = $5: T.class_of(B).class_helper() + $7: T.class_of() = -> bb2 # backedges @@ -48,46 +15,34 @@ bb1[rubyRegionId=0, firstDead=-1](): # backedges # - bb0(rubyRegionId=0) # - bb5(rubyRegionId=1) -bb2[rubyRegionId=1, firstDead=-1](: T.class_of(), $79: Sorbet::Private::Static::Void, $80: T.class_of()): +bb2[rubyRegionId=1, firstDead=-1](: T.class_of(), $6: Sorbet::Private::Static::Void, $7: T.class_of()): # outerLoops: 1 -> (NilClass ? bb5 : bb3) # backedges # - bb2(rubyRegionId=1) -bb3[rubyRegionId=0, firstDead=17]($79: Sorbet::Private::Static::Void, $80: T.class_of()): - $76: Sorbet::Private::Static::Void = Solve<$79, class_helper> - : T.class_of() = $80 - $94: T.class_of(T) = alias - $92: T.class_of() = $94: T.class_of(T).reveal_type(: T.class_of()) - $100: T.class_of(Sorbet::Private::Static) = alias - $102: T.class_of(N) = alias - $98: Sorbet::Private::Static::Void = $100: T.class_of(Sorbet::Private::Static).keep_for_ide($102: T.class_of(N)) - $107: T.class_of(Sorbet::Private::Static) = alias - $109: T.class_of(M) = alias - $105: Sorbet::Private::Static::Void = $107: T.class_of(Sorbet::Private::Static).keep_for_ide($109: T.class_of(M)) - $114: T.class_of(Sorbet::Private::Static) = alias - $116: T.class_of(ThisSelf) = alias - $112: Sorbet::Private::Static::Void = $114: T.class_of(Sorbet::Private::Static).keep_for_ide($116: T.class_of(ThisSelf)) - $121: T.class_of(Sorbet::Private::Static) = alias - $123: T.class_of(Rescues) = alias - $119: Sorbet::Private::Static::Void = $121: T.class_of(Sorbet::Private::Static).keep_for_ide($123: T.class_of(Rescues)) +bb3[rubyRegionId=0, firstDead=5]($6: Sorbet::Private::Static::Void, $7: T.class_of()): + $3: Sorbet::Private::Static::Void = Solve<$6, class_helper> + : T.class_of() = $7 + $21: T.class_of(T) = alias + $19: T.class_of() = $21: T.class_of(T).reveal_type(: T.class_of()) : T.noreturn = return $2: NilClass -> bb1 # backedges # - bb2(rubyRegionId=1) -bb5[rubyRegionId=1, firstDead=10](: T.class_of(), $79: Sorbet::Private::Static::Void, $80: T.class_of()): +bb5[rubyRegionId=1, firstDead=10](: T.class_of(), $6: Sorbet::Private::Static::Void, $7: T.class_of()): # outerLoops: 1 : T.class_of() = loadSelf(class_helper) - $84: T.class_of(B) = alias - keep_for_ide$83: T.class_of(B) = $84 - keep_for_ide$83: T.untyped = keep_for_ide$83 - $85: T.class_of() = - : B = cast($85: T.class_of(), B); - $88: T.class_of(T) = alias - $86: B = $88: T.class_of(T).reveal_type(: B) - $81: T.untyped = : B.instance_helper() - $91: T.noreturn = blockreturn $81: T.untyped + $11: T.class_of(B) = alias + keep_for_ide$10: T.class_of(B) = $11 + keep_for_ide$10: T.untyped = keep_for_ide$10 + $12: T.class_of() = + : B = cast($12: T.class_of(), B); + $15: T.class_of(T) = alias + $13: B = $15: T.class_of(T).reveal_type(: B) + $8: T.untyped = : B.instance_helper() + $18: T.noreturn = blockreturn $8: T.untyped -> bb2 } @@ -779,9 +734,9 @@ method ::Rescues#foo { bb0[rubyRegionId=0, firstDead=-1](): : Rescues = cast(: NilClass, Rescues); $11: T.class_of() = alias > - $3: T.untyped = + $3: T.nilable(Exception) = : String = cast($7: NilClass, String); - $3 -> (T.untyped ? bb3 : bb4) + $3 -> (T.nilable(Exception) ? bb3 : bb4) # backedges # - bb6(rubyRegionId=3) @@ -792,10 +747,10 @@ bb1[rubyRegionId=0, firstDead=-1](): # backedges # - bb0(rubyRegionId=0) # - bb4(rubyRegionId=1) -bb3[rubyRegionId=2, firstDead=-1](: String, $2: T.nilable(String), $3: T.untyped, $11: T.class_of()): - $14: T.class_of(StandardError) = alias - $15: T::Boolean = $14: T.class_of(StandardError).===($3: T.untyped) - $15 -> (T::Boolean ? bb7 : bb8) +bb3[rubyRegionId=2, firstDead=-1](: String, $2: T.nilable(String), $3: Exception, $11: T.class_of()): + $17: T.class_of(StandardError) = alias + $18: T::Boolean = $17: T.class_of(StandardError).===($3: Exception) + $18 -> (T::Boolean ? bb7 : bb8) # backedges # - bb0(rubyRegionId=0) @@ -807,36 +762,38 @@ bb4[rubyRegionId=1, firstDead=-1](: String, $11: T.class_of( : String = cast($7: String, String); $9: T.class_of(T) = alias $2: String = $9: T.class_of(T).reveal_type(: String) - $3: T.untyped = - $3 -> (T.untyped ? bb3 : bb5) + $3: T.nilable(Exception) = + $3 -> (T.nilable(Exception) ? bb3 : bb5) # backedges # - bb4(rubyRegionId=1) -bb5[rubyRegionId=4, firstDead=-1](: String, $2: String): +bb5[rubyRegionId=4, firstDead=-1](: String, $2: String, $3: NilClass): -> bb6 # backedges # - bb5(rubyRegionId=4) # - bb7(rubyRegionId=2) # - bb8(rubyRegionId=2) -bb6[rubyRegionId=3, firstDead=-1](: String, $2: T.nilable(String), $19: T.nilable(TrueClass)): - $22: T.class_of(T) = alias - $20: String = $22: T.class_of(T).reveal_type(: String) - $19 -> (T.nilable(TrueClass) ? bb1 : bb9) +bb6[rubyRegionId=3, firstDead=-1](: String, $2: T.nilable(String), $3: T.nilable(Exception), $22: T.nilable(TrueClass)): + $13: T.class_of(T) = alias + $2: T.untyped = $13: T.class_of(T).unsafe($3: T.nilable(Exception)) + $25: T.class_of(T) = alias + $23: String = $25: T.class_of(T).reveal_type(: String) + $22 -> (T.nilable(TrueClass) ? bb1 : bb9) # backedges # - bb3(rubyRegionId=2) -bb7[rubyRegionId=2, firstDead=-1](: String, $11: T.class_of()): +bb7[rubyRegionId=2, firstDead=-1](: String, $3: StandardError, $11: T.class_of()): $3: NilClass = nil - $12: Sorbet::Private::Static::Void = $11: T.class_of().($3: NilClass) - $17: T.class_of(T) = alias - $2: String = $17: T.class_of(T).reveal_type(: String) + $15: Sorbet::Private::Static::Void = $11: T.class_of().($3: NilClass) + $20: T.class_of(T) = alias + $2: String = $20: T.class_of(T).reveal_type(: String) -> bb6 # backedges # - bb3(rubyRegionId=2) -bb8[rubyRegionId=2, firstDead=-1](: String, $2: T.nilable(String)): - $19: TrueClass = true +bb8[rubyRegionId=2, firstDead=-1](: String, $2: T.nilable(String), $3: Exception): + $22: TrueClass = true -> bb6 # backedges @@ -852,11 +809,11 @@ method ::Rescues#bar { bb0[rubyRegionId=0, firstDead=-1](): : Rescues = cast(: NilClass, Rescues); $11: T.class_of() = alias > - $3: T.untyped = + $3: T.nilable(Exception) = : String = cast($7: NilClass, String); - : Integer = cast($19: NilClass, Integer); - : Float = cast($28: NilClass, Float); - $3 -> (T.untyped ? bb3 : bb4) + : Integer = cast($22: NilClass, Integer); + : Float = cast($31: NilClass, Float); + $3 -> (T.nilable(Exception) ? bb3 : bb4) # backedges # - bb6(rubyRegionId=3) @@ -867,10 +824,10 @@ bb1[rubyRegionId=0, firstDead=-1](): # backedges # - bb0(rubyRegionId=0) # - bb4(rubyRegionId=1) -bb3[rubyRegionId=2, firstDead=-1](: T.any(Float, String), $2: T.nilable(String), $3: T.untyped, $11: T.class_of()): - $14: T.class_of(StandardError) = alias - $15: T::Boolean = $14: T.class_of(StandardError).===($3: T.untyped) - $15 -> (T::Boolean ? bb7 : bb8) +bb3[rubyRegionId=2, firstDead=-1](: T.any(Float, String), $2: T.nilable(String), $3: Exception, $11: T.class_of()): + $17: T.class_of(StandardError) = alias + $18: T::Boolean = $17: T.class_of(StandardError).===($3: Exception) + $18 -> (T::Boolean ? bb7 : bb8) # backedges # - bb0(rubyRegionId=0) @@ -882,46 +839,48 @@ bb4[rubyRegionId=1, firstDead=-1](: Float, $11: T.class_of() : String = cast($7: Float, String); $9: T.class_of(T) = alias $2: String = $9: T.class_of(T).reveal_type(: String) - $3: T.untyped = - $3 -> (T.untyped ? bb3 : bb5) + $3: T.nilable(Exception) = + $3 -> (T.nilable(Exception) ? bb3 : bb5) # backedges # - bb4(rubyRegionId=1) -bb5[rubyRegionId=4, firstDead=-1](: String, $2: String): +bb5[rubyRegionId=4, firstDead=-1](: String, $2: String, $3: NilClass): -> bb6 # backedges # - bb5(rubyRegionId=4) # - bb7(rubyRegionId=2) # - bb8(rubyRegionId=2) -bb6[rubyRegionId=3, firstDead=-1](: T.any(Float, String, Integer), $2: T.nilable(T.any(String, Integer)), $23: T.nilable(TrueClass)): - $27: T.class_of(Float) = alias - keep_for_ide$26: T.class_of(Float) = $27 - keep_for_ide$26: T.untyped = keep_for_ide$26 - $28: T.any(Float, String, Integer) = - : Float = cast($28: T.any(Float, String, Integer), Float); - $30: T.class_of(T) = alias - $24: Float = $30: T.class_of(T).reveal_type(: Float) - $23 -> (T.nilable(TrueClass) ? bb1 : bb9) +bb6[rubyRegionId=3, firstDead=-1](: T.any(Float, String, Integer), $2: T.nilable(T.any(String, Integer)), $3: T.nilable(Exception), $26: T.nilable(TrueClass)): + $13: T.class_of(T) = alias + $2: T.untyped = $13: T.class_of(T).unsafe($3: T.nilable(Exception)) + $30: T.class_of(Float) = alias + keep_for_ide$29: T.class_of(Float) = $30 + keep_for_ide$29: T.untyped = keep_for_ide$29 + $31: T.any(Float, String, Integer) = + : Float = cast($31: T.any(Float, String, Integer), Float); + $33: T.class_of(T) = alias + $27: Float = $33: T.class_of(T).reveal_type(: Float) + $26 -> (T.nilable(TrueClass) ? bb1 : bb9) # backedges # - bb3(rubyRegionId=2) -bb7[rubyRegionId=2, firstDead=-1](: T.any(Float, String), $11: T.class_of()): +bb7[rubyRegionId=2, firstDead=-1](: T.any(Float, String), $3: StandardError, $11: T.class_of()): $3: NilClass = nil - $12: Sorbet::Private::Static::Void = $11: T.class_of().($3: NilClass) - $18: T.class_of(Integer) = alias - keep_for_ide$17: T.class_of(Integer) = $18 - keep_for_ide$17: T.untyped = keep_for_ide$17 - $19: T.any(Float, String) = - : Integer = cast($19: T.any(Float, String), Integer); - $21: T.class_of(T) = alias - $2: Integer = $21: T.class_of(T).reveal_type(: Integer) + $15: Sorbet::Private::Static::Void = $11: T.class_of().($3: NilClass) + $21: T.class_of(Integer) = alias + keep_for_ide$20: T.class_of(Integer) = $21 + keep_for_ide$20: T.untyped = keep_for_ide$20 + $22: T.any(Float, String) = + : Integer = cast($22: T.any(Float, String), Integer); + $24: T.class_of(T) = alias + $2: Integer = $24: T.class_of(T).reveal_type(: Integer) -> bb6 # backedges # - bb3(rubyRegionId=2) -bb8[rubyRegionId=2, firstDead=-1](: T.any(Float, String), $2: T.nilable(String)): - $23: TrueClass = true +bb8[rubyRegionId=2, firstDead=-1](: T.any(Float, String), $2: T.nilable(String), $3: Exception): + $26: TrueClass = true -> bb6 # backedges @@ -939,9 +898,9 @@ bb0[rubyRegionId=0, firstDead=-1](): $5: T.class_of(T) = alias $3: Rescues = $5: T.class_of(T).reveal_type(: Rescues) $15: T.class_of() = alias > - $7: T.untyped = + $7: T.nilable(Exception) = : String = cast($11: NilClass, String); - $7 -> (T.untyped ? bb3 : bb4) + $7 -> (T.nilable(Exception) ? bb3 : bb4) # backedges # - bb6(rubyRegionId=3) @@ -952,9 +911,9 @@ bb1[rubyRegionId=0, firstDead=-1](): # backedges # - bb0(rubyRegionId=0) # - bb4(rubyRegionId=1) -bb3[rubyRegionId=2, firstDead=-1](: String, $2: T.nilable(String), $7: T.untyped, $15: T.class_of()): +bb3[rubyRegionId=2, firstDead=-1](: String, $2: T.nilable(String), $7: Exception, $15: T.class_of()): $18: T.class_of(StandardError) = alias - $19: T::Boolean = $18: T.class_of(StandardError).===($7: T.untyped) + $19: T::Boolean = $18: T.class_of(StandardError).===($7: Exception) $19 -> (T::Boolean ? bb7 : bb8) # backedges @@ -967,8 +926,8 @@ bb4[rubyRegionId=1, firstDead=-1](: String, $15: T.class_of( : String = cast($11: String, String); $13: T.class_of(T) = alias $2: String = $13: T.class_of(T).reveal_type(: String) - $7: T.untyped = - $7 -> (T.untyped ? bb3 : bb5) + $7: T.nilable(Exception) = + $7 -> (T.nilable(Exception) ? bb3 : bb5) # backedges # - bb4(rubyRegionId=1) @@ -984,7 +943,7 @@ bb6[rubyRegionId=3, firstDead=-1]($2: T.nilable(String), : String, $15: T.class_of()): +bb7[rubyRegionId=2, firstDead=-1](: String, $7: StandardError, $15: T.class_of()): $7: NilClass = nil $16: Sorbet::Private::Static::Void = $15: T.class_of().($7: NilClass) $21: T.class_of(T) = alias @@ -1039,17 +998,17 @@ bb5[rubyRegionId=1, firstDead=-1](: T.class_of(Rescues), : T.class_of(Rescues) = loadSelf(takes_block) $17: T.class_of() = alias > - $9: T.untyped = + $9: T.nilable(Exception) = : Integer = cast($13: NilClass, Integer); - $9 -> (T.untyped ? bb7 : bb8) + $9 -> (T.nilable(Exception) ? bb7 : bb8) # backedges # - bb5(rubyRegionId=1) # - bb8(rubyRegionId=2) -bb7[rubyRegionId=3, firstDead=-1](: Integer, $6: Sorbet::Private::Static::Void, $7: T.class_of(Rescues), $8: T.nilable(Integer), $9: T.untyped, $13: T.nilable(Integer), $17: T.class_of(), $25: NilClass): +bb7[rubyRegionId=3, firstDead=-1](: Integer, $6: Sorbet::Private::Static::Void, $7: T.class_of(Rescues), $8: T.nilable(Integer), $9: Exception, $13: T.nilable(Integer), $17: T.class_of(), $25: NilClass): # outerLoops: 1 $20: T.class_of(StandardError) = alias - $21: T::Boolean = $20: T.class_of(StandardError).===($9: T.untyped) + $21: T::Boolean = $20: T.class_of(StandardError).===($9: Exception) $21 -> (T::Boolean ? bb11 : bb12) # backedges @@ -1063,8 +1022,8 @@ bb8[rubyRegionId=2, firstDead=-1](: Integer, $6: Sorb : Integer = cast($13: Integer, Integer); $15: T.class_of(T) = alias $8: Integer = $15: T.class_of(T).reveal_type(: Integer) - $9: T.untyped = - $9 -> (T.untyped ? bb7 : bb9) + $9: T.nilable(Exception) = + $9 -> (T.nilable(Exception) ? bb7 : bb9) # backedges # - bb8(rubyRegionId=2) @@ -1082,7 +1041,7 @@ bb10[rubyRegionId=4, firstDead=-1](: Integer, $6: Sor # backedges # - bb7(rubyRegionId=3) -bb11[rubyRegionId=3, firstDead=-1](: Integer, $6: Sorbet::Private::Static::Void, $7: T.class_of(Rescues), $13: T.nilable(Integer), $17: T.class_of(), $25: NilClass): +bb11[rubyRegionId=3, firstDead=-1](: Integer, $6: Sorbet::Private::Static::Void, $7: T.class_of(Rescues), $9: StandardError, $13: T.nilable(Integer), $17: T.class_of(), $25: NilClass): # outerLoops: 1 $9: NilClass = nil $18: Sorbet::Private::Static::Void = $17: T.class_of().($9: NilClass) @@ -1106,3 +1065,48 @@ bb13[rubyRegionId=1, firstDead=1](: Integer, $6: Sorb } +method ::UntypedBind#foo { + +bb0[rubyRegionId=0, firstDead=18](): + : UntypedBind = cast(: NilClass, UntypedBind); + $5: T.class_of(T) = alias + $3: UntypedBind = $5: T.class_of(T).reveal_type(: UntypedBind) + $10: T.class_of(T) = alias + keep_for_ide$8: Runtime object representing type: T.untyped = $10: T.class_of(T).untyped() + keep_for_ide$8: T.untyped = keep_for_ide$8 + $11: UntypedBind = + : T.untyped = cast($11: UntypedBind, T.untyped); + $14: T.class_of(T) = alias + $12: T.untyped = $14: T.class_of(T).reveal_type(: T.untyped) + $18: T.class_of(UntypedBind) = alias + keep_for_ide$17: T.class_of(UntypedBind) = $18 + keep_for_ide$17: T.untyped = keep_for_ide$17 + $19: T.untyped = + : UntypedBind = cast($19: T.untyped, UntypedBind); + $21: T.class_of(T) = alias + $2: UntypedBind = $21: T.class_of(T).reveal_type(: UntypedBind) + : T.noreturn = return $2: UntypedBind + -> bb1 + +# backedges +# - bb0(rubyRegionId=0) +bb1[rubyRegionId=0, firstDead=-1](): + -> bb1 + +} + +method ::# { + +bb0[rubyRegionId=0, firstDead=3](): + : T.class_of(UntypedBind) = cast(: NilClass, T.class_of(UntypedBind)); + $2: Symbol(:foo) = :foo + : T.noreturn = return $2: Symbol(:foo) + -> bb1 + +# backedges +# - bb0(rubyRegionId=0) +bb1[rubyRegionId=0, firstDead=-1](): + -> bb1 + +} + diff --git a/test/testdata/infer/bound_proc_strict.rb b/test/testdata/infer/bound_proc_strict.rb new file mode 100644 index 0000000000..10b0ffd8c6 --- /dev/null +++ b/test/testdata/infer/bound_proc_strict.rb @@ -0,0 +1,14 @@ +# typed: strict + +class UntypedBind + extend T::Sig + + sig { void } + def foo + T.reveal_type(self) # error: type: `UntypedBind` + T.bind(self, T.untyped) + T.reveal_type(self) # error: type: `T.untyped` + T.bind(self, UntypedBind) + T.reveal_type(self) # error: type: `UntypedBind` + end +end diff --git a/test/testdata/infer/branch_on_void.rb b/test/testdata/infer/branch_on_void.rb new file mode 100644 index 0000000000..db0d89d97c --- /dev/null +++ b/test/testdata/infer/branch_on_void.rb @@ -0,0 +1,108 @@ +# typed: true +extend T::Sig + +sig { void } +def returns_void +end + +def example1 + x = returns_void + + if x + #^ error: Branching on `void` value + puts("Hello") + end +end + +def example2 + result = + if [true, false].sample + returns_void + else + nil + end + + if result + #^^^^^^ error: Branching on `void` + puts("Hello") + end +end + +def example3 + result = + if [true, false].sample + returns_void + else + returns_void + end + + if result + #^^^^^^ error: Branching on `void` + #^^^^^^ error: Branching on `void` + puts("Hello") + end +end + +def example4 + result = + if [true, false].sample + returns_void + else + nil + end + + puts('force control flow to join') + + if result + #^^^^^^ error: Branching on `void` value + puts("Hello") + end +end + +def example5 + result = + if [true, false].sample + returns_void + else + 0 + end + + puts('force control flow to join') + + if result + #^^^^^^ error: Branching on `void` value + puts("Hello") + end +end + +def example6 + result = + if [true, false].sample + returns_void + else + T.unsafe(0) + end + + # This is weird: what happens is that the simplifier makes each `if` branch + # jump directly to the block that starts the second block condition, so + # there's no intermediate block which joins the control flow and forces the + # `result` type to collapse, so despite the `result` variable having type + # `T.untyped` once we get to that block, when we check one of the two + # branches into that if-entry-block, there's an intermediate state where + # Sorbet can see `void` + + if result + #^^^^^^ error: Branching on `void` value + puts("Hello") + end +end + +def example7 + xs = Array(returns_void) + T.reveal_type(xs) # error: `T::Array[Sorbet::Private::Static::Void]` + + if xs + # No error for void inside applied type + puts(xs) + end +end diff --git a/test/testdata/infer/casts.rb.cfg-text.exp b/test/testdata/infer/casts.rb.cfg-text.exp index 30ee38d3f0..dee5d4734f 100644 --- a/test/testdata/infer/casts.rb.cfg-text.exp +++ b/test/testdata/infer/casts.rb.cfg-text.exp @@ -1,10 +1,7 @@ method ::># { -bb0[rubyRegionId=0, firstDead=5](): +bb0[rubyRegionId=0, firstDead=2](): : T.class_of() = cast(: NilClass, T.class_of()); - $6: T.class_of(Sorbet::Private::Static) = alias - $8: T.class_of(TestCasts) = alias - $4: Sorbet::Private::Static::Void = $6: T.class_of(Sorbet::Private::Static).keep_for_ide($8: T.class_of(TestCasts)) : T.noreturn = return $2: NilClass -> bb1 diff --git a/test/testdata/infer/casts.rb.flatten-tree.exp b/test/testdata/infer/casts.rb.flatten-tree.exp index 7551d6475e..6a131c839d 100644 --- a/test/testdata/infer/casts.rb.flatten-tree.exp +++ b/test/testdata/infer/casts.rb.flatten-tree.exp @@ -1,12 +1,9 @@ begin - class <>> < (::) + + def self.<$CENSORED>() - begin - - ::Sorbet::Private::Static.keep_for_ide(::TestCasts) - - end + end end class ::TestCasts<> < (::) diff --git a/test/testdata/infer/class_new_block.rb b/test/testdata/infer/class_new_block.rb new file mode 100644 index 0000000000..de84c03374 --- /dev/null +++ b/test/testdata/infer/class_new_block.rb @@ -0,0 +1,23 @@ +# typed: true + +class A + extend T::Sig + + klass = Class.new do + sig { params(n: Integer, blk: T.proc.void).void } + def initialize(n, &blk) + end + end + T.reveal_type(klass) # error: `T::Class[T.untyped]` + + blk = ->(){} + + inst = klass.new + T.reveal_type(inst) # error: `T.untyped` + + inst = klass.new(0) + T.reveal_type(inst) # error: `T.untyped` + + inst = klass.new(0, &blk) + T.reveal_type(inst) # error: `T.untyped` +end diff --git a/test/testdata/infer/constructors.rb b/test/testdata/infer/constructors.rb index 73e1e7b7f6..b0ce5c3ab9 100644 --- a/test/testdata/infer/constructors.rb +++ b/test/testdata/infer/constructors.rb @@ -22,8 +22,10 @@ def foo Foo1.new(1) {} # ^ error: Wrong number of arguments for constructor + # ^^^ error: Method `BasicObject#initialize` does not take a block Foo1.new 1 do end # ^ error: Wrong number of arguments for constructor + # ^^^^^^^ error: Method `BasicObject#initialize` does not take a block end end diff --git a/test/testdata/infer/constructors.rb.autocorrects.exp b/test/testdata/infer/constructors.rb.autocorrects.exp index abd20bbad5..b88bfd9eec 100644 --- a/test/testdata/infer/constructors.rb.autocorrects.exp +++ b/test/testdata/infer/constructors.rb.autocorrects.exp @@ -21,17 +21,19 @@ class Bar Foo2.new(1) Foo3.new(1, 2) - Foo1.new() {} + Foo1.new() # ^ error: Wrong number of arguments for constructor - Foo1.new do end + # ^^^ error: Method `BasicObject#initialize` does not take a block + Foo1.new # ^ error: Wrong number of arguments for constructor + # ^^^^^^^ error: Method `BasicObject#initialize` does not take a block end end class InstanceMethod def test_imethod_new o = Object.new - o.new # error: Method `new` does not exist + o.class.new # error: Method `new` does not exist end def _; end diff --git a/test/testdata/infer/control_flow/complex_implication_1.rb.cfg-text.exp b/test/testdata/infer/control_flow/complex_implication_1.rb.cfg-text.exp index e62d9d87fd..6816080662 100644 --- a/test/testdata/infer/control_flow/complex_implication_1.rb.cfg-text.exp +++ b/test/testdata/infer/control_flow/complex_implication_1.rb.cfg-text.exp @@ -1,10 +1,7 @@ method ::># { -bb0[rubyRegionId=0, firstDead=5](): +bb0[rubyRegionId=0, firstDead=2](): : T.class_of() = cast(: NilClass, T.class_of()); - $6: T.class_of(Sorbet::Private::Static) = alias - $8: T.class_of(ModuleMethods) = alias - $4: Sorbet::Private::Static::Void = $6: T.class_of(Sorbet::Private::Static).keep_for_ide($8: T.class_of(ModuleMethods)) : T.noreturn = return $2: NilClass -> bb1 @@ -22,8 +19,8 @@ bb0[rubyRegionId=0, firstDead=-1](): final_attempt: T.untyped = load_arg(final_attempt) foo: T.untyped = load_arg(foo) $5: T.class_of() = alias > - $4: T.untyped = - $4 -> (T.untyped ? bb3 : bb4) + $4: T.nilable(Exception) = + $4 -> (T.nilable(Exception) ? bb3 : bb4) # backedges # - bb6(rubyRegionId=3) @@ -34,17 +31,16 @@ bb1[rubyRegionId=0, firstDead=-1](): # backedges # - bb0(rubyRegionId=0) # - bb4(rubyRegionId=1) -bb3[rubyRegionId=2, firstDead=-1](final_attempt: T.untyped, foo: T.untyped, $4: T.untyped, $5: T.class_of()): - e: T.untyped = $4 +bb3[rubyRegionId=2, firstDead=-1](final_attempt: T.untyped, foo: T.untyped, $4: Exception, $5: T.class_of()): $8: T.class_of(StandardError) = alias - $9: T::Boolean = $8: T.class_of(StandardError).===(e: T.untyped) + $9: T::Boolean = $8: T.class_of(StandardError).===($4: Exception) $9 -> (T::Boolean ? bb7 : bb8) # backedges # - bb0(rubyRegionId=0) bb4[rubyRegionId=1, firstDead=-1](final_attempt: T.untyped, foo: T.untyped, $5: T.class_of()): - $4: T.untyped = - $4 -> (T.untyped ? bb3 : bb5) + $4: T.nilable(Exception) = + $4 -> (T.nilable(Exception) ? bb3 : bb5) # backedges # - bb4(rubyRegionId=1) @@ -60,7 +56,8 @@ bb6[rubyRegionId=3, firstDead=-1](final_attempt: T.untyped, foo: T.untyped, err: # backedges # - bb3(rubyRegionId=2) -bb7[rubyRegionId=2, firstDead=-1](final_attempt: T.untyped, foo: T.untyped, $5: T.class_of(), e: StandardError): +bb7[rubyRegionId=2, firstDead=-1](final_attempt: T.untyped, foo: T.untyped, $4: StandardError, $5: T.class_of()): + e: StandardError = $4 $4: NilClass = nil $6: Sorbet::Private::Static::Void = $5: T.class_of().($4: NilClass) err: StandardError = e diff --git a/test/testdata/infer/control_flow/module_less_than.rb b/test/testdata/infer/control_flow/module_less_than.rb new file mode 100644 index 0000000000..18e3d1e535 --- /dev/null +++ b/test/testdata/infer/control_flow/module_less_than.rb @@ -0,0 +1,16 @@ +# typed: true +extend T::Sig + +class A + def self.foo(x) + x.to_s + end +end + +sig {params(y: Object).void} +def main(y) + if y.is_a?(Module) && y < A + y.foo("bar") + end +end + diff --git a/test/testdata/infer/control_flow/normalize_params.rb.cfg-text.exp b/test/testdata/infer/control_flow/normalize_params.rb.cfg-text.exp index 2750f7cda4..51503fc970 100644 --- a/test/testdata/infer/control_flow/normalize_params.rb.cfg-text.exp +++ b/test/testdata/infer/control_flow/normalize_params.rb.cfg-text.exp @@ -1,10 +1,7 @@ method ::># { -bb0[rubyRegionId=0, firstDead=5](): +bb0[rubyRegionId=0, firstDead=2](): : T.class_of() = cast(: NilClass, T.class_of()); - $6: T.class_of(Sorbet::Private::Static) = alias - $8: T.class_of(Test) = alias - $4: Sorbet::Private::Static::Void = $6: T.class_of(Sorbet::Private::Static).keep_for_ide($8: T.class_of(Test)) : T.noreturn = return $2: NilClass -> bb1 diff --git a/test/testdata/infer/control_flow/simple.rb.cfg-text.exp b/test/testdata/infer/control_flow/simple.rb.cfg-text.exp index 1a85fc4815..73188ffced 100644 --- a/test/testdata/infer/control_flow/simple.rb.cfg-text.exp +++ b/test/testdata/infer/control_flow/simple.rb.cfg-text.exp @@ -1,10 +1,7 @@ method ::># { -bb0[rubyRegionId=0, firstDead=5](): +bb0[rubyRegionId=0, firstDead=2](): : T.class_of() = cast(: NilClass, T.class_of()); - $6: T.class_of(Sorbet::Private::Static) = alias - $8: T.class_of(ControlFlow) = alias - $4: Sorbet::Private::Static::Void = $6: T.class_of(Sorbet::Private::Static).keep_for_ide($8: T.class_of(ControlFlow)) : T.noreturn = return $2: NilClass -> bb1 diff --git a/test/testdata/infer/exception_ensure.rb b/test/testdata/infer/exception_ensure.rb new file mode 100644 index 0000000000..f1ced4d4e4 --- /dev/null +++ b/test/testdata/infer/exception_ensure.rb @@ -0,0 +1,29 @@ +# typed: true + +# For the time being, getting well-typed exception values in the presence of +# `ensure` blocks is a bit too tricky, because of how Sorbet desugars +# exceptional control flow. + +def example1 + begin + puts + rescue => e + puts + ensure + puts + end + + T.reveal_type(e) # error: `T.untyped` +end + +def example2 + begin + puts + rescue => e + puts + end + + T.reveal_type(e) # error: `T.nilable(StandardError)` +end + + diff --git a/test/testdata/infer/exception_knowledge.rb b/test/testdata/infer/exception_knowledge.rb new file mode 100644 index 0000000000..aab31d8484 --- /dev/null +++ b/test/testdata/infer/exception_knowledge.rb @@ -0,0 +1,45 @@ +# typed: true +extend T::Sig + +def example1 + begin + x = 0 + rescue TypeError => e + rescue => e + raise + end + + T.reveal_type(x) # error: `T.nilable(Integer)` + T.reveal_type(e) # error: `T.nilable(TypeError)` +end + +def example2 + begin + x = 0 + rescue TypeError => e + raise + rescue => e + end + + T.reveal_type(x) # error: `T.nilable(Integer)` + T.reveal_type(e) # error: `T.nilable(StandardError)` +end + +sig { params(x: T::Boolean).void } +def example3(x:) + begin + puts + rescue TypeError => e + if x + raise + else + return + end + rescue => e + raise + end + + # all paths through the function that set `e` raise or return, so `e` is + # effectively uninitialized + T.reveal_type(e) # error: `NilClass` +end diff --git a/test/testdata/infer/fields.rb.cfg-text.exp b/test/testdata/infer/fields.rb.cfg-text.exp index 9a58dcc5d6..2d695a7738 100644 --- a/test/testdata/infer/fields.rb.cfg-text.exp +++ b/test/testdata/infer/fields.rb.cfg-text.exp @@ -1,10 +1,7 @@ method ::># { -bb0[rubyRegionId=0, firstDead=5](): +bb0[rubyRegionId=0, firstDead=2](): : T.class_of() = cast(: NilClass, T.class_of()); - $6: T.class_of(Sorbet::Private::Static) = alias - $8: T.class_of(Foo) = alias - $4: Sorbet::Private::Static::Void = $6: T.class_of(Sorbet::Private::Static).keep_for_ide($8: T.class_of(Foo)) : T.noreturn = return $2: NilClass -> bb1 diff --git a/test/testdata/infer/flatten_methods.rb.flatten-tree.exp b/test/testdata/infer/flatten_methods.rb.flatten-tree.exp index 98c1f8419d..a1e5adc2b7 100644 --- a/test/testdata/infer/flatten_methods.rb.flatten-tree.exp +++ b/test/testdata/infer/flatten_methods.rb.flatten-tree.exp @@ -1,21 +1,11 @@ begin - class <>> < (::) + + + + def self.<$CENSORED>() - begin - begin - - ::Sorbet::Private::Static.keep_for_ide(::Parent) - - end - begin - - ::Sorbet::Private::Static.keep_for_ide(::Child) - ::Sorbet::Private::Static.keep_for_ide(::Parent) - - end - - end + end end class ::Parent<> < (::) diff --git a/test/testdata/infer/flatten_private.rb.flatten-tree.exp b/test/testdata/infer/flatten_private.rb.flatten-tree.exp index 60f8e9f07a..ead3c4ebe9 100644 --- a/test/testdata/infer/flatten_private.rb.flatten-tree.exp +++ b/test/testdata/infer/flatten_private.rb.flatten-tree.exp @@ -1,12 +1,9 @@ begin - class <>> < (::) + + def self.<$CENSORED>() - begin - - ::Sorbet::Private::Static.keep_for_ide(::A) - - end + end end class ::A<> < (::) diff --git a/test/testdata/infer/fuzz_nonexistant_method.rb b/test/testdata/infer/fuzz_nonexistent_method.rb similarity index 100% rename from test/testdata/infer/fuzz_nonexistant_method.rb rename to test/testdata/infer/fuzz_nonexistent_method.rb diff --git a/test/testdata/infer/generics/box_class_of.rb b/test/testdata/infer/generics/box_class_of.rb index 7f2de6f0ae..37d6b9cc7e 100644 --- a/test/testdata/infer/generics/box_class_of.rb +++ b/test/testdata/infer/generics/box_class_of.rb @@ -49,13 +49,8 @@ def example2(klass) klass.new # error: Not enough arguments provided for method `Box#initialize` - # These two are bugs in our Class_new intrinsic. We dispatch on - # attachedClass->externalType() instead of on the attached class type - # argument. This doesn't happen for the klass[Integer].new calls because the - # [] call creates a MetaType, and then the `new` dispatchCall goes to - # `initialize` on the the MetaType's wrapped type. - klass.new(A.new) - klass.new(0) + klass.new(A.new) # error: Expected `T.all(A, M)` but found `A` for argument `val` + klass.new(0) # error: Expected `T.all(A, M)` but found `Integer(0)` for argument `val` # But I guess this still works, where you can just apply it to another type? diff --git a/test/testdata/infer/generics/object_class.rb b/test/testdata/infer/generics/object_class.rb index 10eef4615b..50e1d20615 100644 --- a/test/testdata/infer/generics/object_class.rb +++ b/test/testdata/infer/generics/object_class.rb @@ -37,3 +37,19 @@ def example(x) T.reveal_type(x.class) # error: `T.class_of(A)[T.all(A, M)]` T.reveal_type(x.class.new) # error: `T.all(A, M)` end + +sig do + type_parameters(:U) + .params(x: T.type_parameter(:U)) + .returns(T.type_parameter(:U)) +end +def example2(x) + case x + when A + T.reveal_type(x) # error: `T.all(A, T.type_parameter(:U) (of Object#example2))` + T.reveal_type(x.class) # error: `T.class_of(A)[T.all(A, T.type_parameter(:U) (of Object#example2))]` + T.reveal_type(x.class.new) # error: T.all(A, T.type_parameter(:U) (of Object#example2)) + else + x + end +end diff --git a/test/testdata/infer/generics/overloads_test.rb b/test/testdata/infer/generics/overloads_test.rb new file mode 100644 index 0000000000..91112f4cc8 --- /dev/null +++ b/test/testdata/infer/generics/overloads_test.rb @@ -0,0 +1,51 @@ +# typed: strict +class Module; include T::Sig; end + +sig do + type_parameters(:U) + .params(x: T::Array[T.type_parameter(:U)]) + .returns(T.nilable(T.type_parameter(:U))) +end +sig do + params(x: String) + .returns(T.nilable(String)) +end +def example(x) # error: against an overloaded signature +end + +xs = T::Array[Integer].new + +res = example(xs) +T.reveal_type(res) # error: `T.nilable(Integer)` + +res = example('') +T.reveal_type(res) # error: `T.nilable(String)` + +sig do + params( + type: T::Class[Integer], + args: T.untyped, + blk: T.nilable(T.proc.params(arg0: Integer).void) + ) + .void +end +sig do + params( + args: T.untyped, + blk: T.untyped + ) + .void +end +def self.example2(type=T.unsafe(nil), *args, &blk) # error: against an overloaded signature +end + +example2(Integer, "--jobs=") do |x| + T.reveal_type(x) # error: `Integer` +end +example2(Integer, "-j", "--jobs=") do |x| + T.reveal_type(x) # error: `Integer` +end +example2("foo", /bar/) do |x| + T.reveal_type(x) # error: `T.untyped` +end + diff --git a/test/testdata/infer/generics/t_class_of.rb b/test/testdata/infer/generics/t_class_of.rb index 5a94001590..b50f52eea9 100644 --- a/test/testdata/infer/generics/t_class_of.rb +++ b/test/testdata/infer/generics/t_class_of.rb @@ -144,7 +144,7 @@ def example5(klass) T.reveal_type(a) # error: `T.type_parameter(:U) (of Object#example5)` # This error message is unfortunate; had the user been able to omit the # `` argument in `T.class_of`, it would have defaulted to `A` - # and then been interesected with `T.type_parameter(:U)`. + # and then been intersected with `T.type_parameter(:U)`. # # (The thing that's annoying is just the verbosity--if you want to use # T.type_parameter with `T::Class`, there's no upper bound so you don't need diff --git a/test/testdata/infer/generics/type_var_positive_polarity_lower_bound.rb b/test/testdata/infer/generics/type_var_positive_polarity_lower_bound.rb new file mode 100644 index 0000000000..ce5dfe000a --- /dev/null +++ b/test/testdata/infer/generics/type_var_positive_polarity_lower_bound.rb @@ -0,0 +1,75 @@ +# typed: true +extend T::Sig + +sig do + type_parameters(:U) + .params( + x: T.type_parameter(:U), + blk: T.proc.returns(T.type_parameter(:U)) + ) + .returns(T.type_parameter(:U)) +end +def example(x, &blk) + if [true, false].sample + return x + else + return blk.call + end +end + +f = T.let(->() { 0 }, T.proc.returns(Integer)) +res = example('', &f) # error: Expected `T.proc.returns(String)` but found `T.proc.returns(Integer)` for block argument +T.reveal_type(res) # error: `T.any(String, Integer)` + + +# --- more complicated example --- + +sig do + type_parameters(:U) + .params( + exn_class: T.class_of(StandardError)[T.all(StandardError, T.type_parameter(:U))], + blk: T.proc.params( + raiser: T.proc.params(exn: T.all(StandardError, T.type_parameter(:U))).void + ).void + ) + .void +end +def complicated(exn_class, &blk) + raiser = T.let( + -> (exn) do + T.reveal_type(exn) # error: `T.all(StandardError, T.type_parameter(:U) (of Object#complicated))` + raise exn + end, + T.proc.params(exn: T.all(StandardError, T.type_parameter(:U))).void + ) + + bad_raiser = T.let( + -> (x) do + raise x + # ^ error: Expected `T.any(T::Class[T.anything], Exception, String)` but found `T.type_parameter(:U) (of Object#complicated)` for argument `arg0` + end, + T.proc.params(x: T.type_parameter(:U)).void + ) + + begin + yield raiser + rescue exn_class => exn + T.reveal_type(exn_class) # error: `T.class_of(StandardError)[T.all(StandardError, T.type_parameter(:U) (of Object#complicated))]` + end + + # This is fine by variance rules. The user will be limited to passing in + # values for `x` that are instances of `exn_class`, but that's an artificial + # limitation--this function accepts more values than required (because + # `T.type_parameter(:U) <: T.anything`). + technically_good_raiser = T.let( + -> (x) { raise }, + T.proc.params(x: T.anything).void + ) + yield technically_good_raiser +end + +complicated(TypeError) do |raiser| + T.reveal_type(raiser) # error: `T.proc.params(arg0: TypeError).void` + raiser.call(TypeError.new) + raiser.call(ArgumentError.new) # error: Expected `TypeError` but found `ArgumentError` for argument `arg0` +end diff --git a/test/testdata/infer/generics/unwrap_type.rb b/test/testdata/infer/generics/unwrap_type.rb new file mode 100644 index 0000000000..afced5d562 --- /dev/null +++ b/test/testdata/infer/generics/unwrap_type.rb @@ -0,0 +1,73 @@ +# typed: true + +class A + extend T::Sig + sig do + type_parameters(:U) + .params(x: T.type_parameter(:U)) + .void + end + def f1(x) + T.reveal_type(x) # error: `T.type_parameter(:U) (of A#f1)` + + type = T::Array[x] + # ^ error: Unexpected bare `T.type_parameter(:U) (of A#f1)` value found in type position + T.reveal_type(type) # error: `Runtime object representing type: T::Array[T.untyped]` + + type = T.class_of(x) + # ^ error: Unexpected bare `T.type_parameter(:U) (of A#f1)` value found in type position + T.reveal_type(type) # error: `Runtime object representing type: T.untyped` + + type = T.any(x, x) + # ^ error: Unexpected bare `T.type_parameter(:U) (of A#f1)` value found in type position + # ^ error: Unexpected bare `T.type_parameter(:U) (of A#f1)` value found in type position + T.reveal_type(type) # error: `Runtime object representing type: T.untyped` + + type = T.all(x, x) + # ^ error: Unexpected bare `T.type_parameter(:U) (of A#f1)` value found in type position + # ^ error: Unexpected bare `T.type_parameter(:U) (of A#f1)` value found in type position + T.reveal_type(type) # error: `Runtime object representing type: T.untyped` + + type = T.nilable(x) + # ^ error: Unexpected bare `T.type_parameter(:U) (of A#f1)` value found in type position + T.reveal_type(type) # error: `Runtime object representing type: T.untyped` + + type = T.proc.params(arg0: x).void + # ^ error: Unexpected bare `T.type_parameter(:U) (of A#f1)` value found in type position + T.reveal_type(type) # error: `Runtime object representing type: T.proc.params(arg0: T.untyped).void` + + type = T.proc.returns(x) + # ^ error: Unexpected bare `T.type_parameter(:U) (of A#f1)` value found in type position + T.reveal_type(type) # error: `Runtime object representing type: T.proc.returns(T.untyped)` + end + + sig do + type_parameters(:U) + .params(x: T.type_parameter(:U)) + .void + end + def f2(x) + T.reveal_type(x) # error: `T.type_parameter(:U) (of A#f2)` + f = T.let( + -> (x) { + T.reveal_type(x) # error: `T.untyped` + }, + T.proc.params(arg0: x).void + # ^ error: Unsupported type syntax + # ^ error: Unexpected bare `T.type_parameter(:U) (of A#f2)` value found in type position + ) + T.reveal_type(f) # error: `T.proc.params(arg0: T.untyped).void` + end + + sig do + type_parameters(:U) + .params(type: T::Types::Base) + .void + end + def f3(type) + T.nilable(type) + # ^^^^ error: Unexpected bare `T::Types::Base` value found in type position + T.nilable(T.unsafe(type)) + end +end + diff --git a/test/testdata/infer/generics/validator.rb b/test/testdata/infer/generics/validator.rb new file mode 100644 index 0000000000..3d2df4ac72 --- /dev/null +++ b/test/testdata/infer/generics/validator.rb @@ -0,0 +1,138 @@ +# typed: strict +class Module; include T::Sig; end + +sig { params(x: T.anything).void } +def example(x) + int_validator = Validator::Cls.make(Integer) + res = int_validator.parse!(x) + T.reveal_type(res) # error: `Integer` + + str_validator = Validator::Cls.make(String) + res = str_validator.parse!(x) + T.reveal_type(res) # error: `String` + + res = Validator::Any.make(int_validator, str_validator).parse!(x) + T.reveal_type(res) # error: `T.any(Integer, String)` +end + + +module Validator + extend T::Generic + abstract! + + Type = type_member + + class ParseResult < StandardError + sig { params(msg: T.nilable(String)).void } + def initialize(msg = nil) + super + end + end + + sig do + abstract + .params(x: T.anything) + .returns(T.any(Type, ParseResult)) + end + def try_parse(x); end + + sig(:final) {params(x: T.anything).returns(Type)} + def parse!(x) + case (res = self.try_parse(x)) + when ParseResult then Kernel.raise(res) + else res + end + end + + class Cls + extend T::Generic + include Validator + + Type = type_member + + # TODO(jez) T::Module? + sig { params(klass: T::Class[Type]).void } + def initialize(klass) + @klass = klass + end + + private_class_method :new + sig do + type_parameters(:Type) + .params(klass: T::Class[T.type_parameter(:Type)]) + .returns(T.all(T.attached_class, Cls[T.type_parameter(:Type)])) + end + def self.make(klass) + self.new(klass) + end + + sig { override.params(x: T.anything).returns(T.any(Type, ParseResult)) } + def try_parse(x) + case x + when @klass + T.reveal_type(x) # error: `Validator::Cls::Type` + return x + else + # In real code, might not want to print arbitrary value, might just + # want to print the class (PII, etc.). + return ParseResult.new("#{x} is not an instance of #{@klass}") + end + end + end + + class Any + extend T::Generic + include Validator + + # TODO(jez) Would like to be able to use fixed here + TypeReal = type_member { {fixed: T.any(Left, Right)} } + # ^^^^ error: is not allowed in this context + # ^^^^^ error: is not allowed in this context + Type = type_member + Left = type_member + Right = type_member + + sig { params(left: Validator[Left], right: Validator[Right]).void } + def initialize(left, right) + @left = left + @right = right + end + + private_class_method :new + sig do + type_parameters(:Left, :Right) + .params( + left: Validator[T.type_parameter(:Left)], + right: Validator[T.type_parameter(:Right)] + ) + .returns(T.all( + T.attached_class, + Any[ + T.any(T.type_parameter(:Left), T.type_parameter(:Right)), + T.type_parameter(:Left), + T.type_parameter(:Right) + ] + )) + end + def self.make(left, right) + self.new(left, right) + end + + sig { override.params(x: T.anything).returns(T.any(Type, ParseResult)) } + def try_parse(x) + # TODO(jez) Would be able to avoid `T.cast` here if we could fix `Type` + # to `T.any(Left, Right)` + case (res = @left.try_parse(x)) + when ParseResult + res = @right.try_parse(x) + T.reveal_type(res) # error: `T.any(Validator::ParseResult, Validator::Any::Right)` + return T.cast(res, T.any(Type, ParseResult)) + else + T.reveal_type(res) # error: `Validator::Any::Left` + return T.cast(res, Type) + end + end + end +end + + diff --git a/test/testdata/infer/hashes.rb.cfg-text.exp b/test/testdata/infer/hashes.rb.cfg-text.exp index 868b3211bd..5831ca1de9 100644 --- a/test/testdata/infer/hashes.rb.cfg-text.exp +++ b/test/testdata/infer/hashes.rb.cfg-text.exp @@ -1,10 +1,7 @@ method ::># { -bb0[rubyRegionId=0, firstDead=5](): +bb0[rubyRegionId=0, firstDead=2](): : T.class_of() = cast(: NilClass, T.class_of()); - $6: T.class_of(Sorbet::Private::Static) = alias - $8: T.class_of(Foo) = alias - $4: Sorbet::Private::Static::Void = $6: T.class_of(Sorbet::Private::Static).keep_for_ide($8: T.class_of(Foo)) : T.noreturn = return $2: NilClass -> bb1 diff --git a/test/testdata/infer/huge_unions.rb.cfg-text.exp b/test/testdata/infer/huge_unions.rb.cfg-text.exp index 0ddd0baaaf..d861886354 100644 --- a/test/testdata/infer/huge_unions.rb.cfg-text.exp +++ b/test/testdata/infer/huge_unions.rb.cfg-text.exp @@ -1,88 +1,7 @@ method ::># { -bb0[rubyRegionId=0, firstDead=83](): +bb0[rubyRegionId=0, firstDead=2](): : T.class_of() = cast(: NilClass, T.class_of()); - $7: T.class_of(Sorbet::Private::Static) = alias - $9: T.class_of(C1) = alias - $5: Sorbet::Private::Static::Void = $7: T.class_of(Sorbet::Private::Static).keep_for_ide($9: T.class_of(C1)) - $14: T.class_of(Sorbet::Private::Static) = alias - $16: T.class_of(C2) = alias - $12: Sorbet::Private::Static::Void = $14: T.class_of(Sorbet::Private::Static).keep_for_ide($16: T.class_of(C2)) - $21: T.class_of(Sorbet::Private::Static) = alias - $23: T.class_of(C3) = alias - $19: Sorbet::Private::Static::Void = $21: T.class_of(Sorbet::Private::Static).keep_for_ide($23: T.class_of(C3)) - $28: T.class_of(Sorbet::Private::Static) = alias - $30: T.class_of(C4) = alias - $26: Sorbet::Private::Static::Void = $28: T.class_of(Sorbet::Private::Static).keep_for_ide($30: T.class_of(C4)) - $35: T.class_of(Sorbet::Private::Static) = alias - $37: T.class_of(C5) = alias - $33: Sorbet::Private::Static::Void = $35: T.class_of(Sorbet::Private::Static).keep_for_ide($37: T.class_of(C5)) - $42: T.class_of(Sorbet::Private::Static) = alias - $44: T.class_of(C6) = alias - $40: Sorbet::Private::Static::Void = $42: T.class_of(Sorbet::Private::Static).keep_for_ide($44: T.class_of(C6)) - $49: T.class_of(Sorbet::Private::Static) = alias - $51: T.class_of(C7) = alias - $47: Sorbet::Private::Static::Void = $49: T.class_of(Sorbet::Private::Static).keep_for_ide($51: T.class_of(C7)) - $56: T.class_of(Sorbet::Private::Static) = alias - $58: T.class_of(C8) = alias - $54: Sorbet::Private::Static::Void = $56: T.class_of(Sorbet::Private::Static).keep_for_ide($58: T.class_of(C8)) - $63: T.class_of(Sorbet::Private::Static) = alias - $65: T.class_of(C9) = alias - $61: Sorbet::Private::Static::Void = $63: T.class_of(Sorbet::Private::Static).keep_for_ide($65: T.class_of(C9)) - $70: T.class_of(Sorbet::Private::Static) = alias - $72: T.class_of(C10) = alias - $68: Sorbet::Private::Static::Void = $70: T.class_of(Sorbet::Private::Static).keep_for_ide($72: T.class_of(C10)) - $77: T.class_of(Sorbet::Private::Static) = alias - $79: T.class_of(C11) = alias - $75: Sorbet::Private::Static::Void = $77: T.class_of(Sorbet::Private::Static).keep_for_ide($79: T.class_of(C11)) - $84: T.class_of(Sorbet::Private::Static) = alias - $86: T.class_of(C12) = alias - $82: Sorbet::Private::Static::Void = $84: T.class_of(Sorbet::Private::Static).keep_for_ide($86: T.class_of(C12)) - $91: T.class_of(Sorbet::Private::Static) = alias - $93: T.class_of(C13) = alias - $89: Sorbet::Private::Static::Void = $91: T.class_of(Sorbet::Private::Static).keep_for_ide($93: T.class_of(C13)) - $98: T.class_of(Sorbet::Private::Static) = alias - $100: T.class_of(C13) = alias - $96: Sorbet::Private::Static::Void = $98: T.class_of(Sorbet::Private::Static).keep_for_ide($100: T.class_of(C13)) - $105: T.class_of(Sorbet::Private::Static) = alias - $107: T.class_of(C13) = alias - $103: Sorbet::Private::Static::Void = $105: T.class_of(Sorbet::Private::Static).keep_for_ide($107: T.class_of(C13)) - $112: T.class_of(Sorbet::Private::Static) = alias - $114: T.class_of(C13) = alias - $110: Sorbet::Private::Static::Void = $112: T.class_of(Sorbet::Private::Static).keep_for_ide($114: T.class_of(C13)) - $119: T.class_of(Sorbet::Private::Static) = alias - $121: T.class_of(C13) = alias - $117: Sorbet::Private::Static::Void = $119: T.class_of(Sorbet::Private::Static).keep_for_ide($121: T.class_of(C13)) - $126: T.class_of(Sorbet::Private::Static) = alias - $128: T.class_of(C13) = alias - $124: Sorbet::Private::Static::Void = $126: T.class_of(Sorbet::Private::Static).keep_for_ide($128: T.class_of(C13)) - $133: T.class_of(Sorbet::Private::Static) = alias - $135: T.class_of(C13) = alias - $131: Sorbet::Private::Static::Void = $133: T.class_of(Sorbet::Private::Static).keep_for_ide($135: T.class_of(C13)) - $140: T.class_of(Sorbet::Private::Static) = alias - $142: T.class_of(C14) = alias - $138: Sorbet::Private::Static::Void = $140: T.class_of(Sorbet::Private::Static).keep_for_ide($142: T.class_of(C14)) - $147: T.class_of(Sorbet::Private::Static) = alias - $149: T.class_of(C15) = alias - $145: Sorbet::Private::Static::Void = $147: T.class_of(Sorbet::Private::Static).keep_for_ide($149: T.class_of(C15)) - $154: T.class_of(Sorbet::Private::Static) = alias - $156: T.class_of(C16) = alias - $152: Sorbet::Private::Static::Void = $154: T.class_of(Sorbet::Private::Static).keep_for_ide($156: T.class_of(C16)) - $161: T.class_of(Sorbet::Private::Static) = alias - $163: T.class_of(C17) = alias - $159: Sorbet::Private::Static::Void = $161: T.class_of(Sorbet::Private::Static).keep_for_ide($163: T.class_of(C17)) - $168: T.class_of(Sorbet::Private::Static) = alias - $170: T.class_of(C18) = alias - $166: Sorbet::Private::Static::Void = $168: T.class_of(Sorbet::Private::Static).keep_for_ide($170: T.class_of(C18)) - $175: T.class_of(Sorbet::Private::Static) = alias - $177: T.class_of(C19) = alias - $173: Sorbet::Private::Static::Void = $175: T.class_of(Sorbet::Private::Static).keep_for_ide($177: T.class_of(C19)) - $182: T.class_of(Sorbet::Private::Static) = alias - $184: T.class_of(C20) = alias - $180: Sorbet::Private::Static::Void = $182: T.class_of(Sorbet::Private::Static).keep_for_ide($184: T.class_of(C20)) - $189: T.class_of(Sorbet::Private::Static) = alias - $191: T.class_of(A) = alias - $187: Sorbet::Private::Static::Void = $189: T.class_of(Sorbet::Private::Static).keep_for_ide($191: T.class_of(A)) : T.noreturn = return $2: NilClass -> bb1 diff --git a/test/testdata/infer/infer1.rb.flatten-tree.exp b/test/testdata/infer/infer1.rb.flatten-tree.exp index 80e3aeb0d8..95c5fe1d8a 100644 --- a/test/testdata/infer/infer1.rb.flatten-tree.exp +++ b/test/testdata/infer/infer1.rb.flatten-tree.exp @@ -1,80 +1,76 @@ -begin - - class <>> < (::) - def baz1() - begin - a = "foo" - b = a.getbyte(a) - end +class <>> < (::) + def baz1() + begin + a = "foo" + b = a.getbyte(a) end + end - def baz2() - begin - a = "foo" - b = a.getbyte("foo") - end + def baz2() + begin + a = "foo" + b = a.getbyte("foo") end + end - def baz3() - b = "foo".getbyte("foo") - end + def baz3() + b = "foo".getbyte("foo") + end - def baz4() - b = .a().getbyte("foo") - end + def baz4() + b = .a().getbyte("foo") + end - def baz5(cond, ) - begin - if cond - b = 1 - else - b = "foo" - end - b = b.getbyte(1) + def baz5(cond, ) + begin + if cond + b = 1 + else + b = "foo" end + b = b.getbyte(1) end + end - def baz6(cond, ) - begin - if cond - b = 1 - else - b = "foo" - end - b = "foo".getbyte(b) + def baz6(cond, ) + begin + if cond + b = 1 + else + b = "foo" end + b = "foo".getbyte(b) end + end - def baz7(cond, ) - begin - if cond - b = 1 - else - - end - b = "foo".getbyte(b) + def baz7(cond, ) + begin + if cond + b = 1 + else + end + b = "foo".getbyte(b) end + end - def baz8() - while true - b = 1 - end + def baz8() + while true + b = 1 end + end - def self.<$CENSORED>() - begin - - - - - - - - - - end + def self.<$CENSORED>() + begin + + + + + + + + + end end - end diff --git a/test/testdata/infer/infer1.rb.symbol-table-raw.exp b/test/testdata/infer/infer1.rb.symbol-table-raw.exp index 89dcb32579..45c540cb04 100644 --- a/test/testdata/infer/infer1.rb.symbol-table-raw.exp +++ b/test/testdata/infer/infer1.rb.symbol-table-raw.exp @@ -2,7 +2,7 @@ class >> < > () class >> $1>[>>] < > $1> () method >> $1>#> $CENSORED> () @ Loc {file=test/testdata/infer/infer1.rb start=2:1 end=50:4} argument @ Loc {file=test/testdata/infer/infer1.rb start=??? end=???} - class > < > (>) @ Loc {file=https://github.com/sorbet/sorbet/tree/master/rbi/core/object.rbi start=removed end=removed} + class > < > (>, >) @ (Loc {file=https://github.com/sorbet/sorbet/tree/master/rbi/stdlib/json.rbi start=removed end=removed}, Loc {file=https://github.com/sorbet/sorbet/tree/master/rbi/core/object.rbi start=removed end=removed}) method ># : private () @ Loc {file=test/testdata/infer/infer1.rb start=2:1 end=2:9} argument @ Loc {file=test/testdata/infer/infer1.rb start=??? end=???} method ># : private () @ Loc {file=test/testdata/infer/infer1.rb start=7:1 end=7:9} diff --git a/test/testdata/infer/isa_generic.rb.cfg-text.exp b/test/testdata/infer/isa_generic.rb.cfg-text.exp index 166b64f540..400010207b 100644 --- a/test/testdata/infer/isa_generic.rb.cfg-text.exp +++ b/test/testdata/infer/isa_generic.rb.cfg-text.exp @@ -170,24 +170,12 @@ bb6[rubyRegionId=2, firstDead=-1](: T.class_of(), $25: Sorbet::Private::Static::Void, $26: T.class_of()): +bb7[rubyRegionId=0, firstDead=6]($25: Sorbet::Private::Static::Void, $26: T.class_of()): $21: Sorbet::Private::Static::Void = Solve<$25, sig> : T.class_of() = $26 $42: T.class_of(T::Sig) = alias $44: T.class_of(T) = alias $39: T.class_of() = : T.class_of().extend($42: T.class_of(T::Sig)) - $49: T.class_of(Sorbet::Private::Static) = alias - $51: T.class_of(Base)[Base, T.untyped] = alias - $47: Sorbet::Private::Static::Void = $49: T.class_of(Sorbet::Private::Static).keep_for_ide($51: T.class_of(Base)[Base, T.untyped]) - $56: T.class_of(Sorbet::Private::Static) = alias - $58: T.class_of(Concrete) = alias - $54: Sorbet::Private::Static::Void = $56: T.class_of(Sorbet::Private::Static).keep_for_ide($58: T.class_of(Concrete)) - $61: T.class_of(Sorbet::Private::Static) = alias - $63: T.class_of(Base)[Base, T.untyped] = alias - $59: Sorbet::Private::Static::Void = $61: T.class_of(Sorbet::Private::Static).keep_for_ide($63: T.class_of(Base)[Base, T.untyped]) - $68: T.class_of(Sorbet::Private::Static) = alias - $70: T.class_of(Other) = alias - $66: Sorbet::Private::Static::Void = $68: T.class_of(Sorbet::Private::Static).keep_for_ide($70: T.class_of(Other)) : T.noreturn = return $2: NilClass -> bb1 diff --git a/test/testdata/infer/loops.rb.cfg-text.exp b/test/testdata/infer/loops.rb.cfg-text.exp index 56d025b233..75ae55f7bf 100644 --- a/test/testdata/infer/loops.rb.cfg-text.exp +++ b/test/testdata/infer/loops.rb.cfg-text.exp @@ -1,10 +1,7 @@ method ::># { -bb0[rubyRegionId=0, firstDead=5](): +bb0[rubyRegionId=0, firstDead=2](): : T.class_of() = cast(: NilClass, T.class_of()); - $6: T.class_of(Sorbet::Private::Static) = alias - $8: T.class_of(HasLoops) = alias - $4: Sorbet::Private::Static::Void = $6: T.class_of(Sorbet::Private::Static).keep_for_ide($8: T.class_of(HasLoops)) : T.noreturn = return $2: NilClass -> bb1 diff --git a/test/testdata/infer/lub_and_glb_corner_cases.rb b/test/testdata/infer/lub_and_glb_corner_cases.rb index 43fd7787b2..e8535bd890 100644 --- a/test/testdata/infer/lub_and_glb_corner_cases.rb +++ b/test/testdata/infer/lub_and_glb_corner_cases.rb @@ -34,7 +34,7 @@ def any_of_all T.assert_type!(ret, T.any(A, T.all(B, C), D)) end - T.assert_type!(1, T.any(Integer, NilClass, String, Integer)) # exists to tigger a sanity check + T.assert_type!(1, T.any(Integer, NilClass, String, Integer)) # exists to trigger a sanity check def generic_class_glb_bottom ret = T.cast(nil, T.all(String, T::Hash[String, String])) diff --git a/test/testdata/infer/lub_between_self_params.rb b/test/testdata/infer/lub_between_self_params.rb index c0946069ea..5b8c3bcf73 100644 --- a/test/testdata/infer/lub_between_self_params.rb +++ b/test/testdata/infer/lub_between_self_params.rb @@ -25,6 +25,7 @@ class Child < Parent def foo(which) self["foo"] || self["bar"] # ^^^^^ error: Expected `Child::A` but found `String("foo")` for argument `k` +# ^^^^ error: Branching on `void` value # ^^^^^^^^^^^ error: This code is unreachable end end diff --git a/test/testdata/infer/meta_types.rb b/test/testdata/infer/meta_types.rb index 8175672dda..bf51dd6671 100644 --- a/test/testdata/infer/meta_types.rb +++ b/test/testdata/infer/meta_types.rb @@ -1,6 +1,6 @@ # typed: true -# Test that putting a MataType into various erroneous places doesn't +# Test that putting a MetaType into various erroneous places doesn't # crash us (and raises appropriate user-level errors) class TestMetaType diff --git a/test/testdata/infer/meta_types.rb.cfg-text.exp b/test/testdata/infer/meta_types.rb.cfg-text.exp index 236c0cffe8..3b75f2ee6f 100644 --- a/test/testdata/infer/meta_types.rb.cfg-text.exp +++ b/test/testdata/infer/meta_types.rb.cfg-text.exp @@ -1,10 +1,7 @@ method ::># { -bb0[rubyRegionId=0, firstDead=5](): +bb0[rubyRegionId=0, firstDead=2](): : T.class_of() = cast(: NilClass, T.class_of()); - $6: T.class_of(Sorbet::Private::Static) = alias - $8: T.class_of(TestMetaType) = alias - $4: Sorbet::Private::Static::Void = $6: T.class_of(Sorbet::Private::Static).keep_for_ide($8: T.class_of(TestMetaType)) : T.noreturn = return $2: NilClass -> bb1 diff --git a/test/testdata/infer/no_eager_expand_sealed.rb b/test/testdata/infer/no_eager_expand_sealed.rb new file mode 100644 index 0000000000..f807718931 --- /dev/null +++ b/test/testdata/infer/no_eager_expand_sealed.rb @@ -0,0 +1,25 @@ +# typed: true +extend T::Sig + +class MyEnum < T::Enum + enums do + X = new + Y = new + end +end + +sig { params(enums: T::Array[MyEnum], orig: T.nilable(MyEnum)).void } +def example(enums, orig) + current_enum = orig + if current_enum.nil? + return + end + T.reveal_type(current_enum) # error: `MyEnum` + + enums.each do |enum| + # If we don't short circuit in dropSubtypesOf, we might eagerly expand + # sealed subclasses, causing `current_enum` expand to `T.any(...)` instead + # of remaining as `MyEnum`, which would cause a pinning error here. + current_enum = enum + end +end diff --git a/test/testdata/infer/no_short_circuit_type_constraint.rb b/test/testdata/infer/no_short_circuit_type_constraint.rb index 3b5e1b10fd..b2d50a8f7f 100644 --- a/test/testdata/infer/no_short_circuit_type_constraint.rb +++ b/test/testdata/infer/no_short_circuit_type_constraint.rb @@ -26,7 +26,7 @@ # Integer <: T.type_parameter(:T)) This should ideally remember `Integer` as lower bound # (2) -# T.any(Integer, NilClass) <: T.any(T.type_paramater(:T), NilClass) +# T.any(Integer, NilClass) <: T.any(T.type_parameter(:T), NilClass) # # ==> Integer <: T.any(T.type_parameter(:T), NilClass) && # NilClass <: T.any(T.type_parameter(:T), NilClass) @@ -54,7 +54,7 @@ # ==> (Integer <: NilClass || # Integer <: T.type_parameter(:T)) && # (NilClass <: NilClass || -# NilClass <: T.type_paramater(:T)) +# NilClass <: T.type_parameter(:T)) # (4) # T.untyped <: T.nilable(T.type_parameter(:T)) diff --git a/test/testdata/infer/no_when_t_forwarder.rb b/test/testdata/infer/no_when_t_forwarder.rb new file mode 100644 index 0000000000..af759b6452 --- /dev/null +++ b/test/testdata/infer/no_when_t_forwarder.rb @@ -0,0 +1,13 @@ +# typed: true + +case T.unsafe(nil) +when T::Array + # ^^^^^^^^ error: Use `Array` without any `T::` prefix to match on a stdlib generic type +when T::Hash + # ^^^^^^^ error: Use `Hash` without any `T::` prefix to match on a stdlib generic type +when T::Set + # ^^^^^^ error: Use `Set` without any `T::` prefix to match on a stdlib generic type +end + +T::Array.===([]) +# ^^^ error: Use `Array` without any `T::` prefix to match on a stdlib generic type diff --git a/test/testdata/infer/no_when_t_forwarder.rb.autocorrects.exp b/test/testdata/infer/no_when_t_forwarder.rb.autocorrects.exp new file mode 100644 index 0000000000..989e795e6a --- /dev/null +++ b/test/testdata/infer/no_when_t_forwarder.rb.autocorrects.exp @@ -0,0 +1,15 @@ +# -- test/testdata/infer/no_when_t_forwarder.rb -- +# typed: true + +case T.unsafe(nil) +when Array + # ^^^^^^^^ error: Use `Array` without any `T::` prefix to match on a stdlib generic type +when Hash + # ^^^^^^^ error: Use `Hash` without any `T::` prefix to match on a stdlib generic type +when Set + # ^^^^^^ error: Use `Set` without any `T::` prefix to match on a stdlib generic type +end + +Array.===([]) +# ^^^ error: Use `Array` without any `T::` prefix to match on a stdlib generic type +# ------------------------------ diff --git a/test/testdata/infer/non_forcing_is_a.rb b/test/testdata/infer/non_forcing_is_a.rb index b24ab5d17f..f3bd086f3c 100644 --- a/test/testdata/infer/non_forcing_is_a.rb +++ b/test/testdata/infer/non_forcing_is_a.rb @@ -56,15 +56,6 @@ class Inner; end end oni = T.unsafe(Outer::Nested::Inner.new) -# in non-package mode, the following are all identical and should all resolve -T::NonForcingConstants.non_forcing_is_a?(oni, "::Outer::Nested::Inner") -T::NonForcingConstants.non_forcing_is_a?(oni, "Inner", package: "Outer::Nested") -T::NonForcingConstants.non_forcing_is_a?(oni, "Nested::Inner", package: "Outer") # these, that reference non-existing constants, should all yield the same error T::NonForcingConstants.non_forcing_is_a?(oni, "::Outer::Nested::Other") # error: Unable to resolve constant `::Outer::Nested::Other` -T::NonForcingConstants.non_forcing_is_a?(oni, "Other", package: "Outer::Nested") # error: Unable to resolve constant `::Outer::Nested::Other` -T::NonForcingConstants.non_forcing_is_a?(oni, "Nested::Other", package: "Outer") # error: Unable to resolve constant `::Outer::Nested::Other` - -pkg = "Outer" -T::NonForcingConstants.non_forcing_is_a?(oni, "Nested::Other", package: pkg) # error: only accepts string literals diff --git a/test/testdata/infer/numbered_parameters.rb.flatten-tree.exp b/test/testdata/infer/numbered_parameters.rb.flatten-tree.exp index a8d18f699d..e7920741ed 100644 --- a/test/testdata/infer/numbered_parameters.rb.flatten-tree.exp +++ b/test/testdata/infer/numbered_parameters.rb.flatten-tree.exp @@ -1,56 +1,52 @@ -begin - - class <>> < (::) - def foo(blk) - blk.call(42, "foo") - end +class <>> < (::) + def foo(blk) + blk.call(42, "foo") + end - def self.<$CENSORED>() - begin - ::Sorbet::Private::Static.sig() do || - .params(:blk, ::T.proc().params(:a, ::Integer, :b, ::String).void()).void() - end - .extend(::T::Sig) - - .foo() do |_1$2| - ::T.reveal_type(_1$2) - end - .foo() do |_1$3, _2$3| - ::T.reveal_type(_2$3) - end - .foo() do |_1$4, _2$4, _3$4| - ::T.reveal_type(_3$4) - end - .foo() do |_1$5, _2$5| - ::T.reveal_type(_2$5.*(_1$5)) - end - .foo() do |_1$6, _2$6, _3$6| - begin - ::T.reveal_type(_1$6) - ::T.reveal_type(_2$6) - ::T.reveal_type(_3$6) - ::T.reveal_type(_2$6.*(_1$6)) - end - end - ::Kernel.lambda() do |_1$7| - ::T.reveal_type(_1$7) - end - ::Kernel.lambda() do |_1$8, _2$8, _3$8, _4$8, _5$8, _6$8, _7$8, _8$8, _9$8| - ::T.reveal_type(_9$8) - end - [1, 2, 3].map() do |_1$9| - ::T.reveal_type(_1$9) + def self.<$CENSORED>() + begin + ::Sorbet::Private::Static.sig() do || + .params(:blk, ::T.proc().params(:a, ::Integer, :b, ::String).void()).void() + end + .extend(::T::Sig) + + .foo() do |_1$2| + ::T.reveal_type(_1$2) + end + .foo() do |_1$3, _2$3| + ::T.reveal_type(_2$3) + end + .foo() do |_1$4, _2$4, _3$4| + ::T.reveal_type(_3$4) + end + .foo() do |_1$5, _2$5| + ::T.reveal_type(_2$5.*(_1$5)) + end + .foo() do |_1$6, _2$6, _3$6| + begin + ::T.reveal_type(_1$6) + ::T.reveal_type(_2$6) + ::T.reveal_type(_3$6) + ::T.reveal_type(_2$6.*(_1$6)) end - ["albatross", "dog", "horse"].max(2) do |_1$10, _2$10| - begin - ::T.reveal_type(_1$10) - ::T.reveal_type(_2$10) - _1$10.length().<=>(_2$10.length()) - end + end + ::Kernel.lambda() do |_1$7| + ::T.reveal_type(_1$7) + end + ::Kernel.lambda() do |_1$8, _2$8, _3$8, _4$8, _5$8, _6$8, _7$8, _8$8, _9$8| + ::T.reveal_type(_9$8) + end + [1, 2, 3].map() do |_1$9| + ::T.reveal_type(_1$9) + end + ["albatross", "dog", "horse"].max(2) do |_1$10, _2$10| + begin + ::T.reveal_type(_1$10) + ::T.reveal_type(_2$10) + _1$10.length().<=>(_2$10.length()) end - end + end end - end diff --git a/test/testdata/infer/or_and_conditional_branch.rb b/test/testdata/infer/or_and_conditional_branch.rb new file mode 100644 index 0000000000..dfafa5da35 --- /dev/null +++ b/test/testdata/infer/or_and_conditional_branch.rb @@ -0,0 +1,45 @@ +# typed: strong +extend T::Sig + +sig {params(lines: T::Array[String]).void} +def foo1(lines) + min_space = lines + .map do |line| + if line.strip.empty? + nil + else + T.unsafe(line) + end + end + # ^^^ error: Value returned from block is `T.untyped` + .compact + .min || 0 + # ^^^^ error: Conditional branch on `T.untyped` +end + +sig {params(lines: T::Array[String]).void} +def foo2(lines) + min_space = lines + .map do |line| + if line.strip.empty? + nil + else + T.unsafe(line) + end + end + # ^^^ error: Value returned from block is `T.untyped` + .compact + .min && 0 + # ^^^^ error: Conditional branch on `T.untyped` +end + +def dont_crash # error: does not have a `sig` + x = begin; end || begin; end + # ^^^^^^^^^^ error: Left side of `||` condition was always `falsy` + x = begin; end && begin; end + # ^^^^^^^^^^ error: This code is unreachable + x = () || () + # ^^ error: Left side of `||` condition was always `falsy` + x = () && () + # ^^ error: This code is unreachable +end diff --git a/test/testdata/infer/overload_stdlib_internal.rb b/test/testdata/infer/overload_stdlib_internal.rb new file mode 100644 index 0000000000..76c7ef752d --- /dev/null +++ b/test/testdata/infer/overload_stdlib_internal.rb @@ -0,0 +1,14 @@ +# typed: __STDLIB_INTERNAL +extend T::Sig + +sig { params(x: String).returns(String) } +sig { params(x: Integer).returns(Integer) } +def foo(x) # error: Refusing to typecheck `Object#foo` against an overloaded signature + raise +end + +res = foo('') +T.reveal_type(res) # error: `String` + +res = foo(0) +T.reveal_type(res) # error: `Integer` diff --git a/test/testdata/infer/overloads_test.rb b/test/testdata/infer/overloads_test.rb index 2e64325ad9..db37163008 100644 --- a/test/testdata/infer/overloads_test.rb +++ b/test/testdata/infer/overloads_test.rb @@ -33,7 +33,7 @@ def make_untyped ) .returns(Symbol) end - def overloaded(arg0, arg1=arg0, arg2=arg0); + def overloaded(arg0, arg1=arg0, arg2=arg0); # error: against an overloaded signature make_untyped end @@ -51,7 +51,7 @@ def overloaded(arg0, arg1=arg0, arg2=arg0); ) .returns(Symbol) end - def invalid_overloaded(a:, b:); + def invalid_overloaded(a:, b:); # error: against an overloaded signature # ^^ error: Bad parameter ordering for `a`, expected `b` instead # ^^ error: Bad parameter ordering for `b`, expected `a` instead make_untyped @@ -67,7 +67,7 @@ def arg0; end sig {params(x: Elem).returns(Elem)} sig {params(x: String).returns(String)} - def overloaded(x); arg0; end + def overloaded(x); arg0; end # error: against an overloaded signature end class Foo @@ -93,7 +93,7 @@ class PrivateOverloads sig {returns(NilClass)} sig {params(x: Integer).returns(Integer)} - private def foo(x=nil); end + private def foo(x=nil); end # error: against an overloaded signature end po1 = PrivateOverloads.new.foo # error: Non-private call to private method @@ -101,3 +101,96 @@ class PrivateOverloads po2 = PrivateOverloads.new.foo(0) # error: Non-private call to private method T.reveal_type(po2) # error: Revealed type: `Integer` + + +class A; end +class B; end + +class BlockOverloads + extend T::Sig + + sig { returns(A) } + sig { params(blk: T.proc.void).returns(B) } + def simple(&blk); end # error: against an overloaded signature + + sig { params(blk: NilClass).returns(A) } + sig { params(blk: T.proc.void).returns(B) } + def explicit_nilclass(&blk); end # error: against an overloaded signature + + sig { returns(A) } + sig { params(blk: T.nilable(T.proc.void)).returns(B) } + def ambiguous_nilable_a(&blk); end # error: against an overloaded signature + sig { params(blk: T.nilable(T.proc.void)).returns(B) } + sig { returns(A) } + def ambiguous_nilable_b(&blk); end # error: against an overloaded signature + + sig { returns(A) } + sig { params(blk: T.untyped).returns(B) } + def ambiguous_untyped_a(&blk); end # error: against an overloaded signature + sig { params(blk: T.untyped).returns(B) } + sig { returns(A) } + def ambiguous_untyped_b(&blk); end # error: against an overloaded signature + + sig { returns(A) } + sig do + type_parameters(:U) + .params(blk: T.proc.returns(T.type_parameter(:U))) + .returns(T.type_parameter(:U)) + end + def not_fully_defined(&blk); end # error: against an overloaded signature + sig do + type_parameters(:U) + .params(blk: T.proc.returns(T.type_parameter(:U))) + .returns(T.type_parameter(:U)) + end + sig { returns(A) } + def not_fully_defined_flipped(&blk); end # error: against an overloaded signature + + def test + x = simple + T.reveal_type(x) # error: `A` + x = simple {} + T.reveal_type(x) # error: `B` + x = simple(&:to_s) + T.reveal_type(x) # error: `B` + + + x = explicit_nilclass + T.reveal_type(x) # error: `A` + x = explicit_nilclass {} + T.reveal_type(x) # error: `B` + + + x = ambiguous_nilable_a + T.reveal_type(x) # error: `A` + x = ambiguous_nilable_a {} + T.reveal_type(x) # error: `B` + + x = ambiguous_nilable_b + T.reveal_type(x) # error: `B` + x = ambiguous_nilable_b {} + T.reveal_type(x) # error: `B` + + + x = ambiguous_untyped_a + T.reveal_type(x) # error: `A` + x = ambiguous_untyped_a {} + T.reveal_type(x) # error: `B` + + x = ambiguous_untyped_b + T.reveal_type(x) # error: `B` + x = ambiguous_untyped_b {} + T.reveal_type(x) # error: `B` + + + x = not_fully_defined + T.reveal_type(x) # error: `A` + x = not_fully_defined {B.new} + T.reveal_type(x) # error: `B` + + x = not_fully_defined_flipped + T.reveal_type(x) # error: `A` + x = not_fully_defined_flipped {B.new} + T.reveal_type(x) # error: `B` + end +end diff --git a/test/testdata/infer/private_class_methods.rb.symbol-table.exp b/test/testdata/infer/private_class_methods.rb.symbol-table.exp index 7ef08d01c2..29b83aa33b 100644 --- a/test/testdata/infer/private_class_methods.rb.symbol-table.exp +++ b/test/testdata/infer/private_class_methods.rb.symbol-table.exp @@ -2,7 +2,7 @@ class :: < ::Object () class ::>[] < :: () method ::># () @ test/testdata/infer/private_class_methods.rb:4 argument @ Loc {file=test/testdata/infer/private_class_methods.rb start=??? end=???} - class ::[] < :: () @ https://github.com/sorbet/sorbet/tree/master/rbi/core/object.rbi#LCENSORED + class ::[] < :: () @ (https://github.com/sorbet/sorbet/tree/master/rbi/stdlib/json.rbi#LCENSORED, https://github.com/sorbet/sorbet/tree/master/rbi/core/object.rbi#LCENSORED) method ::#bar : private () @ test/testdata/infer/private_class_methods.rb:4 argument @ Loc {file=test/testdata/infer/private_class_methods.rb start=??? end=???} class ::Test < ::Object () @ test/testdata/infer/private_class_methods.rb:6 diff --git a/test/testdata/infer/private_constant.rb.symbol-table.exp b/test/testdata/infer/private_constant.rb.symbol-table.exp index 5896fadee1..354f683ffb 100644 --- a/test/testdata/infer/private_constant.rb.symbol-table.exp +++ b/test/testdata/infer/private_constant.rb.symbol-table.exp @@ -52,7 +52,7 @@ class :: < ::Object () method ::#using_private_module (x, ) -> Sorbet::Private::Static::Void @ test/testdata/infer/private_constant.rb:64 argument x<> -> T.class_of(Foo::PrivateModule) @ Loc {file=test/testdata/infer/private_constant.rb start=63:16 end=63:17} argument -> T.untyped @ Loc {file=test/testdata/infer/private_constant.rb start=??? end=???} - class ::Object < ::BasicObject (Kernel) @ https://github.com/sorbet/sorbet/tree/master/rbi/core/object.rbi#LCENSORED + class ::Object < ::BasicObject (Object, Kernel) @ (https://github.com/sorbet/sorbet/tree/master/rbi/stdlib/json.rbi#LCENSORED, https://github.com/sorbet/sorbet/tree/master/rbi/core/object.rbi#LCENSORED) method ::Object#foo : private (x, ) -> Sorbet::Private::Static::Void @ test/testdata/infer/private_constant.rb:100 argument x<> -> T.untyped @ Loc {file=test/testdata/infer/private_constant.rb start=98:13 end=98:14} argument -> T.untyped @ Loc {file=test/testdata/infer/private_constant.rb start=??? end=???} diff --git a/test/testdata/infer/private_initialize.rb b/test/testdata/infer/private_initialize.rb index 8bf5bd73fd..b9e4a17ea5 100644 --- a/test/testdata/infer/private_initialize.rb +++ b/test/testdata/infer/private_initialize.rb @@ -26,7 +26,7 @@ class C end c = C.new -c.initialize +c.initialize # error: Non-private call to private method `initialize` on `C` class D def self.initialize @@ -42,3 +42,10 @@ class E def example(xs) xs.map(&:new) end + +class F + public def initialize + end +end +f = F.new +f.initialize diff --git a/test/testdata/infer/private_methods.rb b/test/testdata/infer/private_methods.rb index e0c4940959..c9634ec3a2 100644 --- a/test/testdata/infer/private_methods.rb +++ b/test/testdata/infer/private_methods.rb @@ -60,7 +60,7 @@ def calling_private_in_parent T.unsafe(Test.new).using_symbol -# TODO: The following call should also error out. It currently does not do that due to issues with the instrinsics. +# TODO: The following call should also error out. It currently does not do that due to issues with the intrinsics. Test.new.splat_call(*T.unsafe(nil)) # Currently no Error, since T.unsafe makes Magic_callWithSplat#apply return prematurely due to the untyped argument. Test.new.splat_and_block_call(*[1, 'a'], &nil) # error: Non-private call to private method `splat_and_block_call` on `Test` diff --git a/test/testdata/infer/private_methods.rb.symbol-table.exp b/test/testdata/infer/private_methods.rb.symbol-table.exp index 741dba3d98..7acbd9785d 100644 --- a/test/testdata/infer/private_methods.rb.symbol-table.exp +++ b/test/testdata/infer/private_methods.rb.symbol-table.exp @@ -2,7 +2,7 @@ class :: < ::Object () class ::>[] < :: () method ::># () @ test/testdata/infer/private_methods.rb:3 argument @ Loc {file=test/testdata/infer/private_methods.rb start=??? end=???} - class ::Object < ::BasicObject (Kernel) @ https://github.com/sorbet/sorbet/tree/master/rbi/core/object.rbi#LCENSORED + class ::Object < ::BasicObject (Object, Kernel) @ (https://github.com/sorbet/sorbet/tree/master/rbi/stdlib/json.rbi#LCENSORED, https://github.com/sorbet/sorbet/tree/master/rbi/core/object.rbi#LCENSORED) method ::Object#foo : private () @ test/testdata/infer/private_methods.rb:3 argument @ Loc {file=test/testdata/infer/private_methods.rb start=??? end=???} class ::Test < ::Object () @ test/testdata/infer/private_methods.rb:5 diff --git a/test/testdata/infer/rbi_overloads__1.rb b/test/testdata/infer/rbi_overloads__1.rb new file mode 100644 index 0000000000..40f9fa9b27 --- /dev/null +++ b/test/testdata/infer/rbi_overloads__1.rb @@ -0,0 +1,19 @@ +# typed: true + +def bad_method(x) # error: Refusing to typecheck `Object#bad_method` against an overloaded signature + T.reveal_type(x) +end + +res = good_method(0) +T.reveal_type(res) # error: `Integer` +res = good_method('') +T.reveal_type(res) # error: `String` +res = good_method(:foo) +# ^^^^ error: Expected `Integer` but found `Symbol(:foo)` for argument `x` + +res = bad_method(0) +T.reveal_type(res) # error: `Integer` +res = bad_method('') +T.reveal_type(res) # error: `String` +res = bad_method(:foo) +# ^^^^ error: Expected `Integer` but found `Symbol(:foo)` for argument `x` diff --git a/test/testdata/infer/rbi_overloads__2.rbi b/test/testdata/infer/rbi_overloads__2.rbi new file mode 100644 index 0000000000..8ab8d87352 --- /dev/null +++ b/test/testdata/infer/rbi_overloads__2.rbi @@ -0,0 +1,11 @@ +# typed: true + +sig { params(x: Integer).returns(Integer) } +sig { params(x: String).returns(String) } +def good_method(x) +end + +sig { params(x: Integer).returns(Integer) } +sig { params(x: String).returns(String) } +def bad_method(x) +end diff --git a/test/testdata/infer/rebind.rb.cfg-text.exp b/test/testdata/infer/rebind.rb.cfg-text.exp index be07ea4710..e4c4eae649 100644 --- a/test/testdata/infer/rebind.rb.cfg-text.exp +++ b/test/testdata/infer/rebind.rb.cfg-text.exp @@ -1,19 +1,7 @@ method ::># { -bb0[rubyRegionId=0, firstDead=14](): +bb0[rubyRegionId=0, firstDead=2](): : T.class_of() = cast(: NilClass, T.class_of()); - $7: T.class_of(Sorbet::Private::Static) = alias - $9: T.class_of(C) = alias - $5: Sorbet::Private::Static::Void = $7: T.class_of(Sorbet::Private::Static).keep_for_ide($9: T.class_of(C)) - $14: T.class_of(Sorbet::Private::Static) = alias - $16: T.class_of(B) = alias - $12: Sorbet::Private::Static::Void = $14: T.class_of(Sorbet::Private::Static).keep_for_ide($16: T.class_of(B)) - $21: T.class_of(Sorbet::Private::Static) = alias - $23: T.class_of(A) = alias - $19: Sorbet::Private::Static::Void = $21: T.class_of(Sorbet::Private::Static).keep_for_ide($23: T.class_of(A)) - $28: T.class_of(Sorbet::Private::Static) = alias - $30: T.class_of(Use) = alias - $26: Sorbet::Private::Static::Void = $28: T.class_of(Sorbet::Private::Static).keep_for_ide($30: T.class_of(Use)) : T.noreturn = return $2: NilClass -> bb1 diff --git a/test/testdata/infer/rescue_raise.rb b/test/testdata/infer/rescue_raise.rb new file mode 100644 index 0000000000..3de936e814 --- /dev/null +++ b/test/testdata/infer/rescue_raise.rb @@ -0,0 +1,105 @@ +# typed: true + +# Sorbet doesn't have the best support when it comes to unconditional +# raise/return inside exception handling blocks. This file aims to show some of +# those problems. +# +# Some relevant issues: +# - https://github.com/sorbet/sorbet/issues/3501 +# - https://github.com/sorbet/sorbet/issues/4108 +# - https://github.com/sorbet/sorbet/issues/7194 + +def example1 + begin + x = 1 + raise + rescue => e + T.reveal_type(e) # error: `StandardError` + T.reveal_type(x) # error: `NilClass` + end + + T.reveal_type(e) # error: `StandardError` + T.reveal_type(x) # error: `NilClass` +end + +def example2 + begin + x = 1 + while true + raise + end + rescue => e + T.reveal_type(e) # error: `StandardError` + T.reveal_type(x) # error: `NilClass` + end + + T.reveal_type(e) # error: `StandardError` + T.reveal_type(x) # error: `NilClass` +end + +def example3 + res = + begin + x = 1.to_i + x + rescue => e + raise + ensure + T.reveal_type(x) # error: `T.nilable(Integer)` + T.reveal_type(e) # error: `T.untyped` + 'not used' + end + T.reveal_type(x) # error: `Integer` + T.reveal_type(e) # error: `T.untyped` + T.reveal_type(res) # error: `Integer` +end + +def example4 + res = + begin + x = 1.to_i + x + rescue => _e + raise + ensure + end + T.reveal_type(x) # error: `Integer` + T.reveal_type(res) # error: `Integer` +end + +def example5 + res = + begin + x = 1.to_i + x + rescue => e + raise + ensure + T.reveal_type(e) # error: `T.untyped` + T.reveal_type(x) # error: `T.nilable(Integer)` + end + T.reveal_type(x) # error: `Integer` + T.reveal_type(res) # error: `Integer` +end + +def example6 + if T.unsafe(nil) + begin + puts + rescue TypeError => e + T.reveal_type(e) # error: `TypeError` + end + else + begin + 1.times do + begin + puts + rescue TypeError => e + # ^ error: Changing the type of a variable in a loop is not permitted + T.reveal_type(e) # error: `T.untyped` + end + end + end + end + T.reveal_type(e) # error: `T.nilable(TypeError)` +end diff --git a/test/testdata/infer/ruby3_keyword_args.rb b/test/testdata/infer/ruby3_keyword_args.rb index c9387d3b01..47db75454c 100644 --- a/test/testdata/infer/ruby3_keyword_args.rb +++ b/test/testdata/infer/ruby3_keyword_args.rb @@ -25,5 +25,5 @@ def foo(name, tags: {}, x: 42, &blk) yield end -blk = Proc.new +blk = Proc.new {} foo("", tags: {}, x: 1, &blk) diff --git a/test/testdata/infer/sealed_list_module.rb b/test/testdata/infer/sealed_list_module.rb index 24fb58cc9f..283fdf5773 100644 --- a/test/testdata/infer/sealed_list_module.rb +++ b/test/testdata/infer/sealed_list_module.rb @@ -85,7 +85,7 @@ def list_integer_to_list_string(xs) T.reveal_type(xs) # error: Revealed type: `List::Nil[Integer]` # The above error (result type) is because List::Nil does not fix the # type_member to T.noreturn, so each empty list can only be used at its - # own type, insead of being compatible with any list. This is mostly for + # own type, instead of being compatible with any list. This is mostly for # test coverage; we also have tests that test the fixed noreturn case. _unused = T.let(xs, List[String]) # error: Argument does not have asserted type `List[String]` List::Nil[String].new diff --git a/test/testdata/infer/self_new_with_splat.rb b/test/testdata/infer/self_new_with_splat.rb index 9b813d385a..4f7d3424f2 100644 --- a/test/testdata/infer/self_new_with_splat.rb +++ b/test/testdata/infer/self_new_with_splat.rb @@ -14,7 +14,7 @@ def self.also_works sig {returns(T.attached_class)} def self.also_works_with_block - new(*[]) do + new(*[]) do # error: `BasicObject#initialize` does not take a block 1 end end diff --git a/test/testdata/infer/self_type.rb.cfg-text.exp b/test/testdata/infer/self_type.rb.cfg-text.exp index d43b401fd9..8f175e47b1 100644 --- a/test/testdata/infer/self_type.rb.cfg-text.exp +++ b/test/testdata/infer/self_type.rb.cfg-text.exp @@ -16,48 +16,30 @@ method ::># { bb0[rubyRegionId=0, firstDead=-1](): : T.class_of() = cast(: NilClass, T.class_of()); - $7: T.class_of(Sorbet::Private::Static) = alias - $9: T.class_of(Parent) = alias - $5: Sorbet::Private::Static::Void = $7: T.class_of(Sorbet::Private::Static).keep_for_ide($9: T.class_of(Parent)) - $14: T.class_of(Sorbet::Private::Static) = alias - $16: T.class_of(Normal) = alias - $12: Sorbet::Private::Static::Void = $14: T.class_of(Sorbet::Private::Static).keep_for_ide($16: T.class_of(Normal)) - $19: T.class_of(Sorbet::Private::Static) = alias - $21: T.class_of(Parent) = alias - $17: Sorbet::Private::Static::Void = $19: T.class_of(Sorbet::Private::Static).keep_for_ide($21: T.class_of(Parent)) - $26: T.class_of(Sorbet::Private::Static) = alias - $28: T.class_of(Generic) = alias - $24: Sorbet::Private::Static::Void = $26: T.class_of(Sorbet::Private::Static).keep_for_ide($28: T.class_of(Generic)) - $31: T.class_of(Sorbet::Private::Static) = alias - $33: T.class_of(Parent) = alias - $29: Sorbet::Private::Static::Void = $31: T.class_of(Sorbet::Private::Static).keep_for_ide($33: T.class_of(Parent)) - $36: T.class_of(Normal) = alias - keep_for_ide$35: T.class_of(Normal) = $36 - keep_for_ide$35: T.untyped = keep_for_ide$35 - $40: T.class_of(Normal) = alias - $38: Normal = $40: T.class_of(Normal).new() - $37: Normal = $38: Normal.returns_self() - $34: Normal = cast($37: Normal, Normal); - $44: T.class_of(Generic) = alias - $46: T.class_of(String) = alias - keep_for_ide$42: Runtime object representing type: Generic[String] = $44: T.class_of(Generic).[]($46: T.class_of(String)) - keep_for_ide$42: T.untyped = keep_for_ide$42 - $51: T.class_of(Generic) = alias - $53: T.class_of(String) = alias - $49: Runtime object representing type: Generic[String] = $51: T.class_of(Generic).[]($53: T.class_of(String)) - $48: Generic[String] = $49: Runtime object representing type: Generic[String].new() - $47: Generic[String] = $48: Generic[String].returns_self() - $41: Generic[String] = cast($47: Generic[String], Generic[String]); - $58: T.class_of(Sorbet::Private::Static) = alias - $60: T.class_of(B) = alias - $56: Sorbet::Private::Static::Void = $58: T.class_of(Sorbet::Private::Static).keep_for_ide($60: T.class_of(B)) - $64: T.class_of(Generic) = alias - $66: T.class_of(String) = alias - $62: Runtime object representing type: Generic[String] = $64: T.class_of(Generic).[]($66: T.class_of(String)) - a: Generic[String] = $62: Runtime object representing type: Generic[String].new() - $71: T.class_of(B) = alias - $68: T::Boolean = a: Generic[String].is_a?($71: T.class_of(B)) - $68 -> (T::Boolean ? bb2 : bb4) + $5: T.class_of(Normal) = alias + keep_for_ide$4: T.class_of(Normal) = $5 + keep_for_ide$4: T.untyped = keep_for_ide$4 + $9: T.class_of(Normal) = alias + $7: Normal = $9: T.class_of(Normal).new() + $6: Normal = $7: Normal.returns_self() + $3: Normal = cast($6: Normal, Normal); + $13: T.class_of(Generic) = alias + $15: T.class_of(String) = alias + keep_for_ide$11: Runtime object representing type: Generic[String] = $13: T.class_of(Generic).[]($15: T.class_of(String)) + keep_for_ide$11: T.untyped = keep_for_ide$11 + $20: T.class_of(Generic) = alias + $22: T.class_of(String) = alias + $18: Runtime object representing type: Generic[String] = $20: T.class_of(Generic).[]($22: T.class_of(String)) + $17: Generic[String] = $18: Runtime object representing type: Generic[String].new() + $16: Generic[String] = $17: Generic[String].returns_self() + $10: Generic[String] = cast($16: Generic[String], Generic[String]); + $26: T.class_of(Generic) = alias + $28: T.class_of(String) = alias + $24: Runtime object representing type: Generic[String] = $26: T.class_of(Generic).[]($28: T.class_of(String)) + a: Generic[String] = $24: Runtime object representing type: Generic[String].new() + $33: T.class_of(B) = alias + $30: T::Boolean = a: Generic[String].is_a?($33: T.class_of(B)) + $30 -> (T::Boolean ? bb2 : bb4) # backedges # - bb7(rubyRegionId=0) @@ -67,43 +49,34 @@ bb1[rubyRegionId=0, firstDead=-1](): # backedges # - bb0(rubyRegionId=0) bb2[rubyRegionId=0, firstDead=-1](: T.class_of(), a: T.all(Generic[String], B)): - $74: T.class_of(T) = alias - $77: T.class_of(Generic) = alias - $79: T.class_of(String) = alias - $75: Runtime object representing type: Generic[String] = $77: T.class_of(Generic).[]($79: T.class_of(String)) - $81: T.class_of(B) = alias - keep_for_ide$72: Runtime object representing type: T.all(Generic[String], B) = $74: T.class_of(T).all($75: Runtime object representing type: Generic[String], $81: T.class_of(B)) - keep_for_ide$72: T.untyped = keep_for_ide$72 - $82: T.all(Generic[String], B) = a: T.all(Generic[String], B).returns_self() - $67: T.all(Generic[String], B) = cast($82: T.all(Generic[String], B), T.all(Generic[String], B)); + $36: T.class_of(T) = alias + $39: T.class_of(Generic) = alias + $41: T.class_of(String) = alias + $37: Runtime object representing type: Generic[String] = $39: T.class_of(Generic).[]($41: T.class_of(String)) + $43: T.class_of(B) = alias + keep_for_ide$34: Runtime object representing type: T.all(Generic[String], B) = $36: T.class_of(T).all($37: Runtime object representing type: Generic[String], $43: T.class_of(B)) + keep_for_ide$34: T.untyped = keep_for_ide$34 + $44: T.all(Generic[String], B) = a: T.all(Generic[String], B).returns_self() + $29: T.all(Generic[String], B) = cast($44: T.all(Generic[String], B), T.all(Generic[String], B)); -> bb4 # backedges # - bb0(rubyRegionId=0) # - bb2(rubyRegionId=0) bb4[rubyRegionId=0, firstDead=-1](: T.class_of()): - $88: T.class_of(Sorbet::Private::Static) = alias - $90: T.class_of(Array)[T::Array[T.untyped]] = alias - $86: Sorbet::Private::Static::Void = $88: T.class_of(Sorbet::Private::Static).keep_for_ide($90: T.class_of(Array)[T::Array[T.untyped]]) - $94: T.class_of(Integer) = alias - $96: T.class_of(Integer) = alias - $97: T.class_of() = alias > - keep_for_ide$92: [T.class_of(Integer), T.class_of(Integer)] = $97: T.class_of().($94: T.class_of(Integer), $96: T.class_of(Integer)) - keep_for_ide$92: T.untyped = keep_for_ide$92 - $100: Integer(1) = 1 - $101: Integer(2) = 2 - $102: T.class_of() = alias > - $99: [Integer(1), Integer(2)] = $102: T.class_of().($100: Integer(1), $101: Integer(2)) - $98: [Integer(1), Integer(2)] = $99: [Integer(1), Integer(2)].returns_self() - $91: [Integer, Integer] = cast($98: [Integer(1), Integer(2)], [Integer, Integer]); - $107: T.class_of(Sorbet::Private::Static) = alias - $109: T.class_of(A) = alias - $105: Sorbet::Private::Static::Void = $107: T.class_of(Sorbet::Private::Static).keep_for_ide($109: T.class_of(A)) - $114: T.class_of(Sorbet::Private::Static) = alias - $116: T.class_of(B) = alias - $112: Sorbet::Private::Static::Void = $114: T.class_of(Sorbet::Private::Static).keep_for_ide($116: T.class_of(B)) - $119: T.class_of(A) = alias - s: A = $119: T.class_of(A).new() + $49: T.class_of(Integer) = alias + $51: T.class_of(Integer) = alias + $52: T.class_of() = alias > + keep_for_ide$47: [T.class_of(Integer), T.class_of(Integer)] = $52: T.class_of().($49: T.class_of(Integer), $51: T.class_of(Integer)) + keep_for_ide$47: T.untyped = keep_for_ide$47 + $55: Integer(1) = 1 + $56: Integer(2) = 2 + $57: T.class_of() = alias > + $54: [Integer(1), Integer(2)] = $57: T.class_of().($55: Integer(1), $56: Integer(2)) + $53: [Integer(1), Integer(2)] = $54: [Integer(1), Integer(2)].returns_self() + $46: [Integer, Integer] = cast($53: [Integer(1), Integer(2)], [Integer, Integer]); + $60: T.class_of(A) = alias + s: A = $60: T.class_of(A).new() -> bb5 # backedges @@ -112,13 +85,13 @@ bb4[rubyRegionId=0, firstDead=-1](: T.class_of()): # - bb9(rubyRegionId=0) bb5[rubyRegionId=0, firstDead=-1](: T.class_of(), s: A): # outerLoops: 1 - $122: T.untyped = : T.class_of().rnd() - $122 -> (T.untyped ? bb8 : bb7) + $63: T.untyped = : T.class_of().rnd() + $63 -> (T.untyped ? bb8 : bb7) # backedges # - bb5(rubyRegionId=0) bb7[rubyRegionId=0, firstDead=2](: T.class_of(), s: A): - $130: NilClass = : T.class_of().puts(s: A) + $71: NilClass = : T.class_of().puts(s: A) : T.noreturn = return $2: NilClass -> bb1 @@ -126,16 +99,16 @@ bb7[rubyRegionId=0, firstDead=2](: T.class_of(), s: A): # - bb5(rubyRegionId=0) bb8[rubyRegionId=0, firstDead=-1](: T.class_of(), s: A): # outerLoops: 1 - $128: T.class_of(B) = alias - $125: T::Boolean = s: A.is_a?($128: T.class_of(B)) - $125 -> (T::Boolean ? bb9 : bb5) + $69: T.class_of(B) = alias + $66: T::Boolean = s: A.is_a?($69: T.class_of(B)) + $66 -> (T::Boolean ? bb9 : bb5) # backedges # - bb8(rubyRegionId=0) bb9[rubyRegionId=0, firstDead=-1](: T.class_of(), s: T.all(A, B)): # outerLoops: 1 - $129: T.all(A, B) = s - s: T.all(A, B) = $129: T.all(A, B).returns_self() + $70: T.all(A, B) = s + s: T.all(A, B) = $70: T.all(A, B).returns_self() -> bb5 } diff --git a/test/testdata/infer/self_type_param.rb b/test/testdata/infer/self_type_param.rb index 616a1f5dd3..d127198857 100644 --- a/test/testdata/infer/self_type_param.rb +++ b/test/testdata/infer/self_type_param.rb @@ -1,6 +1,6 @@ # typed: true -class GenricClass +class GenericClass extend(T::Sig) extend(T::Generic) diff --git a/test/testdata/infer/singleton_methods.rb.cfg-text.exp b/test/testdata/infer/singleton_methods.rb.cfg-text.exp index f33640dfed..dc5f28f415 100644 --- a/test/testdata/infer/singleton_methods.rb.cfg-text.exp +++ b/test/testdata/infer/singleton_methods.rb.cfg-text.exp @@ -1,13 +1,7 @@ method ::># { -bb0[rubyRegionId=0, firstDead=8](): +bb0[rubyRegionId=0, firstDead=2](): : T.class_of() = cast(: NilClass, T.class_of()); - $7: T.class_of(Sorbet::Private::Static) = alias - $9: T.class_of(Foo) = alias - $5: Sorbet::Private::Static::Void = $7: T.class_of(Sorbet::Private::Static).keep_for_ide($9: T.class_of(Foo)) - $14: T.class_of(Sorbet::Private::Static) = alias - $16: T.class_of(Bar) = alias - $12: Sorbet::Private::Static::Void = $14: T.class_of(Sorbet::Private::Static).keep_for_ide($16: T.class_of(Bar)) : T.noreturn = return $2: NilClass -> bb1 diff --git a/test/testdata/infer/singleton_of_singleton.rb.cfg-text.exp b/test/testdata/infer/singleton_of_singleton.rb.cfg-text.exp index f92efd6223..864183682a 100644 --- a/test/testdata/infer/singleton_of_singleton.rb.cfg-text.exp +++ b/test/testdata/infer/singleton_of_singleton.rb.cfg-text.exp @@ -1,10 +1,7 @@ method ::># { -bb0[rubyRegionId=0, firstDead=5](): +bb0[rubyRegionId=0, firstDead=2](): : T.class_of() = cast(: NilClass, T.class_of()); - $6: T.class_of(Sorbet::Private::Static) = alias - $8: T.class_of(A) = alias - $4: Sorbet::Private::Static::Void = $6: T.class_of(Sorbet::Private::Static).keep_for_ide($8: T.class_of(A)) : T.noreturn = return $2: NilClass -> bb1 diff --git a/test/testdata/infer/square_bracket_err_loc.rb b/test/testdata/infer/square_bracket_err_loc.rb new file mode 100644 index 0000000000..2a91137316 --- /dev/null +++ b/test/testdata/infer/square_bracket_err_loc.rb @@ -0,0 +1,18 @@ +# typed: strong +extend T::Sig + +sig {params(x: T::Hash[T.untyped, T::Hash[T.untyped, T.untyped]]).void} +def example1(x) + x["foo"]["bar"] + # ^^^^^^^ error: Method `[]` does not exist on `NilClass` component +end + +sig {params(x: T.nilable(T::Hash[T.untyped, T::Hash[T.untyped, T.untyped]])).void} +def example2(x) + x["foo"]["bar"] + #^^^^^^^ error: Method `[]` does not exist on `NilClass` component + # ^^^^^^^ error: Call to method `[]` on `T.untyped` + +end + + diff --git a/test/testdata/infer/strong_rescue.rb b/test/testdata/infer/strong_rescue.rb index 3943f5c5ac..75b042697a 100644 --- a/test/testdata/infer/strong_rescue.rb +++ b/test/testdata/infer/strong_rescue.rb @@ -1,30 +1,22 @@ # typed: strong extend T::Sig -# Sorbet's implementation of exceptions currently heavily relies on T.untyped -# Fixing that involves more work than I have time for, so let's at least just -# not spam an entire method with untyped squiggles. - sig {void} def example1 begin rescue TypeError => e -# ^^^^^^ error: Conditional branch on `T.untyped` -# ^^^^^^ error: Conditional branch on `T.untyped` - # ^^^^^^^^^ error: Argument passed to parameter `other` is `T.untyped` T.reveal_type(e) # error: `TypeError` else ensure end + + T.reveal_type(e) # error: `T.nilable(TypeError)` end sig {void} def example2 begin rescue; puts("") -# ^^^^^^ error: Conditional branch on `T.untyped` -# ^^^^^^ error: Conditional branch on `T.untyped` -# ^^^^^^ error: Argument passed to parameter `other` is `T.untyped` ensure end end @@ -33,12 +25,11 @@ def example2 def example3 begin rescue => e; T.reveal_type(e) -# ^^^^^^ error: Conditional branch on `T.untyped` -# ^^^^^^ error: Conditional branch on `T.untyped` - # ^ error: Argument passed to parameter `other` is `T.untyped` # ^^^^^^^^^^^^^^^^ error: `StandardError` ensure end + + T.reveal_type(e) # error: `T.nilable(StandardError)` end sig {void} @@ -46,9 +37,6 @@ def example4 begin puts("here we are") rescue; puts("") -# ^^^^^^ error: Conditional branch on `T.untyped` -# ^^^^^^ error: Conditional branch on `T.untyped` -# ^^^^^^ error: Argument passed to parameter `other` is `T.untyped` ensure end end @@ -57,11 +45,38 @@ def example4 def example5 begin rescue Exception => e -# ^^^^^^ error: Conditional branch on `T.untyped` -# ^^^^^^ error: Conditional branch on `T.untyped` - # ^^^^^^^^^ error: Argument passed to parameter `other` is `T.untyped` T.reveal_type(e) # error: `Exception` else ensure end + + T.reveal_type(e) # error: `T.nilable(Exception)` +end + +sig {void} +def example6 + begin + rescue Exception => e + T.reveal_type(e) # error: `Exception` + else + ensure + e.foo + # ^^^ error: Call to method `foo` on `T.untyped` + end + + e.foo + # ^^^ error: Call to method `foo` on `T.untyped` end + +class NotAnException + # usually indicates logic bug or problem with RBI generation +end + +sig {void} +def example7 + begin + rescue NotAnException + puts('hello') # error: This code is unreachable + end +end + diff --git a/test/testdata/infer/strong_top.rb b/test/testdata/infer/strong_top.rb new file mode 100644 index 0000000000..880a230068 --- /dev/null +++ b/test/testdata/infer/strong_top.rb @@ -0,0 +1,4 @@ +# typed: strong + +x = T.unsafe(nil) +Integer.===(x) diff --git a/test/testdata/infer/suggest_dot_class.rb b/test/testdata/infer/suggest_dot_class.rb new file mode 100644 index 0000000000..71613c0aba --- /dev/null +++ b/test/testdata/infer/suggest_dot_class.rb @@ -0,0 +1,12 @@ +# typed: true + +class A + def self.my_singleton_class_method; end + + def example + my_singleton_class_method # error: does not exist + self.my_singleton_class_method # error: does not exist + a = A.new + a.my_singleton_class_method # error: does not exist + end +end diff --git a/test/testdata/infer/suggest_dot_class.rb.autocorrects.exp b/test/testdata/infer/suggest_dot_class.rb.autocorrects.exp new file mode 100644 index 0000000000..cef1b52294 --- /dev/null +++ b/test/testdata/infer/suggest_dot_class.rb.autocorrects.exp @@ -0,0 +1,14 @@ +# -- test/testdata/infer/suggest_dot_class.rb -- +# typed: true + +class A + def self.my_singleton_class_method; end + + def example + self.class.my_singleton_class_method # error: does not exist + self.class.my_singleton_class_method # error: does not exist + a = A.new + a.class.my_singleton_class_method # error: does not exist + end +end +# ------------------------------ diff --git a/test/testdata/infer/suggest_field.rb.autocorrects.exp b/test/testdata/infer/suggest_field.rb.autocorrects.exp index 1b66082043..dce925dde6 100644 --- a/test/testdata/infer/suggest_field.rb.autocorrects.exp +++ b/test/testdata/infer/suggest_field.rb.autocorrects.exp @@ -11,7 +11,7 @@ class A def initialize @x = T.let(0, Integer) # ^^ error: The instance variable `@x` must be declared using `T.let` when specifying `# typed: strict` - @y = begin; end + @y = T.let(begin; end, NilClass) # ^^ error: The instance variable `@y` must be declared using `T.let` when specifying `# typed: strict` @z = T.let(returns_integer, Integer) # ^^ error: The instance variable `@z` must be declared using `T.let` when specifying `# typed: strict` diff --git a/test/testdata/infer/super_class_exec.rb b/test/testdata/infer/super_class_exec.rb new file mode 100644 index 0000000000..63d9489f73 --- /dev/null +++ b/test/testdata/infer/super_class_exec.rb @@ -0,0 +1,21 @@ +# typed: strict +class Module; include T::Sig; end + +class Parent + sig {void} + def frob + puts 'Parent#frob' + end +end + +class Child < Parent +end + +Child.class_exec do + sig {void} + def frob + super + end +end + +Child.new.frob diff --git a/test/testdata/infer/super_generic_method.rb b/test/testdata/infer/super_generic_method.rb new file mode 100644 index 0000000000..dae1c06719 --- /dev/null +++ b/test/testdata/infer/super_generic_method.rb @@ -0,0 +1,25 @@ +# typed: strict +class Module; include T::Sig; end + +class Parent + sig do + type_parameters(:U) + .params( + x: T.type_parameter(:U), + blk: T.proc.params(x: T.type_parameter(:U)).returns(T.type_parameter(:U)) + ) + .void + end + def initialize(x, &blk) + end +end + +class Child < Parent + sig {void} + def initialize + super(0) do |x| + T.reveal_type(x) # error: `Integer` + puts 'hello' + end + end +end diff --git a/test/testdata/infer/super_module.rb b/test/testdata/infer/super_module.rb new file mode 100644 index 0000000000..6ba94036e7 --- /dev/null +++ b/test/testdata/infer/super_module.rb @@ -0,0 +1,41 @@ +# typed: strict +class Module; include T::Sig; end + +module ThisOneExists + sig {returns(Integer)} + def this_one_exists; 0; end +end + +module A + extend ThisOneExists + + class << self + sig {void} + def bar + x = super # error: Method `bar` does not exist on ancestors + T.reveal_type(x) # error: `T.untyped` + end + + sig {void} + def this_one_exists + x = super + T.reveal_type(x) # error: `Integer` + end + end +end + +module ModuleSelfDotMethods + extend ThisOneExists + + sig {void} + def self.bar + x = super # error: Method `bar` does not exist on ancestors + T.reveal_type(x) # error: `T.untyped` + end + + sig {void} + def self.this_one_exists + x = super + T.reveal_type(x) # error: `Integer` + end +end diff --git a/test/testdata/infer/super_strict.rb b/test/testdata/infer/super_strict.rb new file mode 100644 index 0000000000..1995932e57 --- /dev/null +++ b/test/testdata/infer/super_strict.rb @@ -0,0 +1,87 @@ +# typed: strict + +module M + extend T::Sig + + sig { returns(Integer) } + def baz + T.reveal_type(super) # error: Revealed type: `T.untyped` + end +end + +class A + extend T::Sig + + sig { returns(Integer) } + def foo + super + # ^^^^^ error: Method `foo` does not exist on ancestors of `A` + 1 + end + + sig { params(x: Integer).returns(Integer) } + def bar(x) + x + end + + sig { params(x: Integer).returns(Integer) } + def quz(x) + x + end + + sig { params(blk: T.proc.void).void } + def takes_block(&blk) + yield + end + + sig { params(x: Integer).void } + def required_kwargs(x:) + end +end + +class B < A + extend T::Sig + + include M + + sig { params(x: Integer).returns(Integer) } + def foo(x) + super + # ^ error: Too many arguments provided for method `A#foo`. Expected: `0`, got: `1 + super(1) + # ^ error: Too many arguments provided for method `A#foo`. Expected: `0`, got: `1` + T.reveal_type(super()) # error: Revealed type: `Integer` + end + + sig { params(x: String).returns(Integer) } + def bar(x) + super() # error: Not enough arguments provided for method `A#bar`. Expected: `1`, got: `0` + super("a") + # ^^^ error: Expected `Integer` but found `String("a")` for argument `x` + super + # ^ error: Expected `Integer` but found `String` for argument `x` + super(1) + end + + sig { returns(Integer) } + def baz + T.reveal_type(super) # error: Revealed type: `Integer` + end + + sig { params(x: Integer).returns(Integer) } + def quz(x) + super + end + + sig { void } + def takes_block + super + # ^ error: `takes_block` requires a block parameter, but no block was passed + end + + sig { params(opts: T.untyped).void} + def required_kwargs(**opts) + super + # ^ error: the method has required keyword parameters + end +end diff --git a/test/testdata/infer/super_strict.rb.autocorrects.exp b/test/testdata/infer/super_strict.rb.autocorrects.exp new file mode 100644 index 0000000000..3ae840f996 --- /dev/null +++ b/test/testdata/infer/super_strict.rb.autocorrects.exp @@ -0,0 +1,89 @@ +# -- test/testdata/infer/super_strict.rb -- +# typed: strict + +module M + extend T::Sig + + sig { returns(Integer) } + def baz + T.reveal_type(super) # error: Revealed type: `T.untyped` + end +end + +class A + extend T::Sig + + sig { returns(Integer) } + def foo + super + # ^^^^^ error: Method `foo` does not exist on ancestors of `A` + 1 + end + + sig { params(x: Integer).returns(Integer) } + def bar(x) + x + end + + sig { params(x: Integer).returns(Integer) } + def quz(x) + x + end + + sig { params(blk: T.proc.void).void } + def takes_block(&blk) + yield + end + + sig { params(x: Integer).void } + def required_kwargs(x:) + end +end + +class B < A + extend T::Sig + + include M + + sig { params(x: Integer).returns(Integer) } + def foo(x) + super + # ^ error: Too many arguments provided for method `A#foo`. Expected: `0`, got: `1 + super() + # ^ error: Too many arguments provided for method `A#foo`. Expected: `0`, got: `1` + T.reveal_type(super()) # error: Revealed type: `Integer` + end + + sig { params(x: String).returns(Integer) } + def bar(x) + super() # error: Not enough arguments provided for method `A#bar`. Expected: `1`, got: `0` + super("a") + # ^^^ error: Expected `Integer` but found `String("a")` for argument `x` + super + # ^ error: Expected `Integer` but found `String` for argument `x` + super(1) + end + + sig { returns(Integer) } + def baz + T.reveal_type(super) # error: Revealed type: `Integer` + end + + sig { params(x: Integer).returns(Integer) } + def quz(x) + super + end + + sig { void } + def takes_block + super + # ^ error: `takes_block` requires a block parameter, but no block was passed + end + + sig { params(opts: T.untyped).void} + def required_kwargs(**opts) + super + # ^ error: the method has required keyword parameters + end +end +# ------------------------------ diff --git a/test/testdata/infer/t_class_new_attached.rb b/test/testdata/infer/t_class_new_attached.rb new file mode 100644 index 0000000000..b40d68c8c2 --- /dev/null +++ b/test/testdata/infer/t_class_new_attached.rb @@ -0,0 +1,94 @@ +# typed: true +class Module; include T::Sig; end + +class Class + def my_new + # There should be an error here, but Sorbet doesn't currently model that. + # See the comment inside the Class_new intrinsic for why. + new(1) + end +end + +class A +end + +A.my_new + +class MyClass + sig {params(x: String).void} + def initialize(x) + end +end + +sig {params(klass: T::Class[MyClass]).void} +def example1(klass) + klass.new + # ^ error: Not enough arguments provided for method `MyClass#initialize`. Expected: `1`, got: `0` + klass.new(0) + # ^ error: Expected `String` but found `Integer(0)` for argument `x` + klass.new('', 0) + # ^ error: Too many arguments provided for method `MyClass#initialize`. Expected: `1`, got: `2` + instance = klass.new('') + T.reveal_type(instance) # error: `MyClass` +end + +sig do + type_parameters(:Instance) + .params(klass: T::Class[T.type_parameter(:Instance)]) + .void +end +def example2(klass) + klass.new +end + +sig do + type_parameters(:Instance) + .params(klass: T::Class[T.all(MyClass, T.type_parameter(:Instance))]) + .void +end +def example3(klass) + klass.new + # ^ error: Not enough arguments provided for method `MyClass#initialize` on `MyClass` component of `T.all(MyClass, T.type_parameter(:Instance) (of Object#example3))`. Expected: `1`, got: `0` + klass.new(0) + # ^ error: Expected `String` but found `Integer(0)` for argument `x` + klass.new('', 0) + # ^ error: Too many arguments provided for method `MyClass#initialize`. Expected: `1`, got: `2` + instance = klass.new('') + T.reveal_type(instance) # error: `T.all(MyClass, T.type_parameter(:Instance) (of Object#example3))` +end + +sig do + type_parameters(:Instance) + .params(klass: T::Class[T.anything]) + .void +end +def example4(klass) + klass.new +end + +# That there are no errors for the MyModule cases below is a weird side effect +# of the fact that we treat all modules as having an ImplicitModuleSuperclass, +# which has BasicObject as its superclass. +# +# Arguably, we should make modules descend from instead of BasicObject, +# but that would be an invasive change, and it looks like it's been this way +# ~forever, so let's not worry about fixing that now. +# +# https://github.com/sorbet/sorbet/issues/7309 + +module MyModule + def example + # missing error! + initialize + end +end + +sig {params(klass: T::Class[MyModule], m: MyModule).void} +def example5(klass, m) + # missing error! + klass.new + # error, but the wrong one (should be: `initialize` doesn't exist, etc.) + klass.new(0) + # ^ error: Wrong number of arguments for constructor. Expected: `0`, got: `1` + T.let(m, BasicObject) +end diff --git a/test/testdata/infer/t_class_triple_eq.rb b/test/testdata/infer/t_class_triple_eq.rb new file mode 100644 index 0000000000..f5281e17a7 --- /dev/null +++ b/test/testdata/infer/t_class_triple_eq.rb @@ -0,0 +1,115 @@ +# typed: true +extend T::Sig + +class Parent +end + +class Child < Parent +end + +sig do + params( + obj: Parent, + class_of: T.class_of(Parent), + t_class: T::Class[Parent] + ) + .void +end +def example1(obj, class_of, t_class) + if obj.is_a?(class_of) + T.reveal_type(obj) # error: `Parent` + else + # Will be reached if `class_of` is `Child` + T.reveal_type(obj) # error: This code is unreachable + end + + if obj.is_a?(t_class) + T.reveal_type(obj) # error: `Parent` + else + T.reveal_type(obj) # error: `Parent` + end + + if class_of.===(obj) + T.reveal_type(obj) # error: `Parent` + else + # Will be reached if `class_of` is `Child` + T.reveal_type(obj) # error: This code is unreachable + end + + if t_class.===(obj) + T.reveal_type(obj) # error: `Parent` + else + T.reveal_type(obj) # error: `Parent` + end +end + +example1(Parent.new, Child, Child) + + +sig do + params( + obj: T.any(Integer, String), + class_of: T.any(T.class_of(Integer), T.class_of(String)), + t_class: T::Class[T.any(Integer, String)] + ) + .void +end +def example2(obj, class_of, t_class) + if obj.is_a?(class_of) + T.reveal_type(obj) # error: `T.any(Integer, String)` + else + T.reveal_type(obj) # error: `T.any(Integer, String)` + end + + if obj.is_a?(t_class) + T.reveal_type(obj) # error: `T.any(Integer, String)` + else + T.reveal_type(obj) # error: `T.any(Integer, String)` + end + + if class_of.===(obj) + T.reveal_type(obj) # error: `T.any(Integer, String)` + else + T.reveal_type(obj) # error: `T.any(Integer, String)` + end + + if t_class.===(obj) + T.reveal_type(obj) # error: `T.any(Integer, String)` + else + T.reveal_type(obj) # error: `T.any(Integer, String)` + end +end + +sig do + params( + obj: Object, + class_of: T.any(T.class_of(Integer), T.class_of(String)), + t_class: T::Class[T.any(Integer, String)] + ) + .void +end +def example3(obj, class_of, t_class) + if obj.is_a?(class_of) + T.reveal_type(obj) # error: `Object` + else + T.reveal_type(obj) # error: `Object` + end + + if obj.is_a?(t_class) + T.reveal_type(obj) # error: `T.any(Integer, String)` + else + T.reveal_type(obj) # error: `Object` + end + + if class_of.===(obj) + T.reveal_type(obj) # error: `Object` + else + T.reveal_type(obj) # error: `Object` + end + + if t_class.===(obj) + T.reveal_type(obj) # error: `T.any(Integer, String)` + else + T.reveal_type(obj) # error: `Object` + end +end diff --git a/test/testdata/infer/t_let_lambda.rb b/test/testdata/infer/t_let_lambda.rb new file mode 100644 index 0000000000..264fc5e5c6 --- /dev/null +++ b/test/testdata/infer/t_let_lambda.rb @@ -0,0 +1,53 @@ +# typed: true + +f = T.let( + -> (x) do + T.reveal_type(x) # error: `Integer` + end, + T.proc.params(x: Integer).returns(Integer) +) +T.reveal_type(f) # error: `T.proc.params(arg0: Integer).returns(Integer)` + +f = T.let( + -> (x) do + T.reveal_type(x) # error: `Integer` + x.to_s # error: Expected `Integer` but found `String` for block result type + end, + T.proc.params(x: Integer).returns(Integer) +) +T.reveal_type(f) # error: `T.proc.params(arg0: Integer).returns(Integer)` + +f = T.let( + -> (x) do + T.reveal_type(x) # error: `T.untyped` + end, + Integer +# ^^^^^^^ error: Lambda type annotation must be either `Proc` or a `T.proc` type (and possibly nilable) +) +T.reveal_type(f) # error: `Integer` + +f = T.cast( + -> (x) do + T.reveal_type(x) # error: `T.untyped` + end, + Integer +# ^^^^^^^ error: Lambda type annotation must be either `Proc` or a `T.proc` type (and possibly nilable) +) +T.reveal_type(f) # error: `Integer` + +f = T.let( + Kernel.lambda do |x| + T.reveal_type(x) # error: `Integer` + end, + T.proc.params(x: Integer).returns(Integer) +) +T.reveal_type(f) # error: `T.proc.params(arg0: Integer).returns(Integer)` + +f = T.let( + Kernel.proc do |x| + T.reveal_type(x) # error: `Integer` + end, + T.proc.params(x: Integer).returns(Integer) +) +T.reveal_type(f) # error: `T.proc.params(arg0: Integer).returns(Integer)` + diff --git a/test/testdata/infer/takes_no_block__1.rb b/test/testdata/infer/takes_no_block__1.rb index 5f52c33440..8e3bf4a4ed 100644 --- a/test/testdata/infer/takes_no_block__1.rb +++ b/test/testdata/infer/takes_no_block__1.rb @@ -22,7 +22,7 @@ def self.never_yields TypedTrue.never_yields {} TypedStrict.never_yields {} # error: does not take a block -# Techically this causes problems too, but this should not happen in practice, +# Technically this causes problems too, but this should not happen in practice, # because there will have been an error reported for # `TypedStrict.implicit_yield` TypedTrue.implicit_yield {} diff --git a/test/testdata/infer/takes_no_block__2.rb b/test/testdata/infer/takes_no_block__2.rb index 88f258002c..47063c984b 100644 --- a/test/testdata/infer/takes_no_block__2.rb +++ b/test/testdata/infer/takes_no_block__2.rb @@ -27,7 +27,7 @@ def self.never_yields TypedTrue.never_yields {} TypedStrict.never_yields {} # error: does not take a block -# Techically this causes problems too, but this should not happen in practice, +# Technically this causes problems too, but this should not happen in practice, # because there will have been an error reported for # `TypedStrict.implicit_yield` TypedTrue.implicit_yield {} diff --git a/test/testdata/infer/toplevel.rb.flatten-tree.exp b/test/testdata/infer/toplevel.rb.flatten-tree.exp index 447a565ece..9d89efd9c5 100644 --- a/test/testdata/infer/toplevel.rb.flatten-tree.exp +++ b/test/testdata/infer/toplevel.rb.flatten-tree.exp @@ -1,9 +1,5 @@ -begin - - class <>> < (::) - def self.<$CENSORED>() - 1.+("hi") - end +class <>> < (::) + def self.<$CENSORED>() + 1.+("hi") end - end diff --git a/test/testdata/infer/transitive.rb.cfg-text.exp b/test/testdata/infer/transitive.rb.cfg-text.exp index 1018ef9340..c4ec570152 100644 --- a/test/testdata/infer/transitive.rb.cfg-text.exp +++ b/test/testdata/infer/transitive.rb.cfg-text.exp @@ -1,16 +1,7 @@ method ::># { -bb0[rubyRegionId=0, firstDead=11](): +bb0[rubyRegionId=0, firstDead=2](): : T.class_of() = cast(: NilClass, T.class_of()); - $7: T.class_of(Sorbet::Private::Static) = alias - $9: T.class_of(A) = alias - $5: Sorbet::Private::Static::Void = $7: T.class_of(Sorbet::Private::Static).keep_for_ide($9: T.class_of(A)) - $14: T.class_of(Sorbet::Private::Static) = alias - $16: T.class_of(Bar) = alias - $12: Sorbet::Private::Static::Void = $14: T.class_of(Sorbet::Private::Static).keep_for_ide($16: T.class_of(Bar)) - $19: T.class_of(Sorbet::Private::Static) = alias - $21: T.class_of(A) = alias - $17: Sorbet::Private::Static::Void = $19: T.class_of(Sorbet::Private::Static).keep_for_ide($21: T.class_of(A)) : T.noreturn = return $2: NilClass -> bb1 diff --git a/test/testdata/infer/tricky_pinning.rb b/test/testdata/infer/tricky_pinning.rb new file mode 100644 index 0000000000..180886cf1e --- /dev/null +++ b/test/testdata/infer/tricky_pinning.rb @@ -0,0 +1,82 @@ +# typed: strict +extend T::Sig + +sig {returns(T.nilable(Integer))} +def nilable_integer() + nil +end + +sig {params(item: T.untyped).void} +def example1(item) + to_date = T.let(item["to_date"], T.nilable(String)) + to_date = to_date.nil? ? nil : T.let(Date.parse(to_date).to_time.utc, Time) + nil +end + +sig {params(item: T.untyped).void} +def example2(item) + to_date = T.let(item["to_date"], T.nilable(String)) + to_date = T.let(to_date.nil? ? nil : Date.parse(to_date).to_time.utc, T.nilable(Time)) + nil +end + +sig { params(params: Integer).void } +def example3(params) + x = T.unsafe(nil) || T.let(0, Integer) + x = T.let(0, Integer) + p(x) + nil +end + +sig { params(params: Integer).void } +def example4(params) + x = T.unsafe(nil) + x ||= 0 # error: Incompatible assignment to variable declared via `let`: `Integer(0)` is not a subtype of `T.nilable(FalseClass)` + x = T.let(0, Integer) + nil +end + +sig { params(params: Integer).void } +def example5(params) + x = T.unsafe(nil) + x ||= T.let(0, Integer) + x = T.let(0, Integer) + nil +end + +sig { params(x: T.nilable(Integer)).void } +def example6(x) + 1.times do + y = x + x = y || T.let( + (T.reveal_type(x); 0), # error: `NilClass` + T.nilable(Integer) + ) + end + nil +end + +sig { params(x: T.nilable(Integer)).void } +def example7(x) + 1.times do + y = x + x = y + x ||= T.let( + (T.reveal_type(x); 0), # error: `NilClass` + T.nilable(Integer) + ) + end + nil +end + +sig { params(x: T.nilable(Integer)).void } +def example8(x) + 1.times do + y = nilable_integer() + x = y || T.let( + (T.reveal_type(x); 0), # error: `T.nilable(Integer)` + T.nilable(Integer) + ) + end + nil +end diff --git a/test/testdata/infer/typed_true_super__1.rb b/test/testdata/infer/typed_true_super__1.rb new file mode 100644 index 0000000000..3936a8292a --- /dev/null +++ b/test/testdata/infer/typed_true_super__1.rb @@ -0,0 +1,32 @@ +# typed: true +class Module; include T::Sig; end + +class Child1 < Parent + # No sig is fine--the Ruby VM will forward the + # block arg, if passed a block + def self.foo + x = super + T.reveal_type(x) # error: `Integer` + end +end + +Child1.foo do + # this *does* get called + puts 'hello' +end + +class Child2 < Parent + # This one is a little weird. Sorbet allows this method to be called with a + # block, even though the sig doesn't take a block, because this method is + # defined in a `# typed: true` file. + sig {returns(Integer)} + def self.foo + x = super + T.reveal_type(x) # error: `Integer` + end +end + +Child2.foo do + # this *does* get called + puts 'hello' +end diff --git a/test/testdata/infer/typed_true_super__2.rb b/test/testdata/infer/typed_true_super__2.rb new file mode 100644 index 0000000000..4014c96037 --- /dev/null +++ b/test/testdata/infer/typed_true_super__2.rb @@ -0,0 +1,15 @@ +# typed: strict + +class Parent + sig do + type_parameters(:U) + .params( + blk: T.proc.void + ) + .returns(Integer) + end + def self.foo(&blk) + yield + 0 + end +end diff --git a/test/testdata/infer/untyped_return_multiline.rb b/test/testdata/infer/untyped_return_multiline.rb new file mode 100644 index 0000000000..df7d8aad4e --- /dev/null +++ b/test/testdata/infer/untyped_return_multiline.rb @@ -0,0 +1,39 @@ +# typed: strong +extend T::Sig + +sig do + type_parameters(:Return) + .params(blk: T.proc.returns(T.type_parameter(:Return))) + .returns(T.type_parameter(:Return)) +end +def with_block(&blk) + yield +end + +sig { params(blk: T.proc.returns(T.untyped)).returns(T.untyped) } +def with_block_untyped(&blk) + yield +end + +sig { params(x: Integer, y: String).returns(T.untyped) } +def returns_untyped(x, y) +end + +# --- + +sig { returns(Integer) } +def example1 + with_block_untyped do +# ^^^^^^^^^^^^^^^^^^^^^ error: Value returned from method is `T.untyped` + 0 + end +end + +sig { returns(Integer) } +def example2 + returns_untyped( +# ^^^^^^^^^^^^^^^^ error: Value returned from method is `T.untyped` + 0, + '' + ) +end diff --git a/test/testdata/infer/void_anything.rb b/test/testdata/infer/void_anything.rb new file mode 100644 index 0000000000..9c0cb11207 --- /dev/null +++ b/test/testdata/infer/void_anything.rb @@ -0,0 +1,30 @@ +# typed: strict +extend T::Sig + +# We used to implement the logic to allow methods typed as `.void` to return +# anything. The logic was buggy in such a way that methods returning `.void` or +# *any superclass of void* would be treated as `.returns(T.anything)`. + +module M; end + +sig { params(x: M).void } +def foo1(x) + x +end + +sig { params(x: M).returns(Object) } +def foo2(x) + x # error: Expected `Object` but found `M` for method result type +end + +sig { params(x: M).returns(Kernel) } +def foo3(x) + x # error: Expected `Kernel` but found `M` for method result type +end + +sig { params(x: M).returns(BasicObject) } +def foo4(x) + # Sorbet treats modules as descending from BasicObject + # We should fix that at some point, but that's unrelated to the `.void` bug + x +end diff --git a/test/testdata/infer/void_final.rb b/test/testdata/infer/void_final.rb index 5f4e4eb675..e30aa420bd 100644 --- a/test/testdata/infer/void_final.rb +++ b/test/testdata/infer/void_final.rb @@ -11,6 +11,7 @@ def returns_void def main result = returns_void if result + #^^^^^^ error: Branching on `void` value T.reveal_type(result) # error: Revealed type: `Sorbet::Private::Static::Void` else T.reveal_type(result) # error: This code is unreachable diff --git a/test/testdata/infer/zsuper.rb.cfg-text.exp b/test/testdata/infer/zsuper.rb.cfg-text.exp index 50b59e9127..b547dd33a6 100644 --- a/test/testdata/infer/zsuper.rb.cfg-text.exp +++ b/test/testdata/infer/zsuper.rb.cfg-text.exp @@ -1,16 +1,7 @@ method ::># { -bb0[rubyRegionId=0, firstDead=11](): +bb0[rubyRegionId=0, firstDead=2](): : T.class_of() = cast(: NilClass, T.class_of()); - $7: T.class_of(Sorbet::Private::Static) = alias - $9: T.class_of(Foo) = alias - $5: Sorbet::Private::Static::Void = $7: T.class_of(Sorbet::Private::Static).keep_for_ide($9: T.class_of(Foo)) - $14: T.class_of(Sorbet::Private::Static) = alias - $16: T.class_of(Bar) = alias - $12: Sorbet::Private::Static::Void = $14: T.class_of(Sorbet::Private::Static).keep_for_ide($16: T.class_of(Bar)) - $19: T.class_of(Sorbet::Private::Static) = alias - $21: T.class_of(Foo) = alias - $17: Sorbet::Private::Static::Void = $19: T.class_of(Sorbet::Private::Static).keep_for_ide($21: T.class_of(Foo)) : T.noreturn = return $2: NilClass -> bb1 diff --git a/test/testdata/local_vars/z_super_args.rb b/test/testdata/local_vars/z_super_args.rb index 06ec8f0289..ed406f9346 100644 --- a/test/testdata/local_vars/z_super_args.rb +++ b/test/testdata/local_vars/z_super_args.rb @@ -70,3 +70,9 @@ def zsuper_inside_block end end end + +module M + def super_inside_module + super + end +end diff --git a/test/testdata/local_vars/z_super_args.rb.index-tree-raw.exp b/test/testdata/local_vars/z_super_args.rb.index-tree-raw.exp index 53a0462cce..043bb04b3b 100644 --- a/test/testdata/local_vars/z_super_args.rb.index-tree-raw.exp +++ b/test/testdata/local_vars/z_super_args.rb.index-tree-raw.exp @@ -563,7 +563,7 @@ ClassDef{ Local{ localVariable = > } - Literal{ value = : } + Literal{ value = : } Local{ localVariable = > } @@ -593,5 +593,45 @@ ClassDef{ ] } + + ClassDef{ + kind = module + name = UnresolvedConstantLit{ + cnst = > + scope = EmptyTree + } + symbol = >> + ancestors = [] + rhs = [ + MethodDef{ + flags = {} + name = <>> + args = [BlockArg{ expr = Local{ + localVariable = > + } }] + rhs = Send{ + flags = {privateOk} + recv = ConstantLit{ + symbol = (class ::) + orig = nullptr + } + fun = > + block = nullptr + pos_args = 3 + args = [ + Local{ + localVariable = > + } + Literal{ value = : } + Local{ + localVariable = > + } + ] + } + } + + + ] + } ] } diff --git a/test/testdata/local_vars/z_super_args_splats_blocks.rb b/test/testdata/local_vars/z_super_args_splats_blocks.rb index 7675f5c34e..11918aac74 100644 --- a/test/testdata/local_vars/z_super_args_splats_blocks.rb +++ b/test/testdata/local_vars/z_super_args_splats_blocks.rb @@ -2,6 +2,9 @@ # Note that this test exists just for the purpose of validating lowering of zero-arg "super", via exp files. It's not # expected to do anything useful if actually executed. +# +# For this reason, this test does double duty in checking that `--typed-super=false` works. +# typed-super: false def pos(x, y, z) super diff --git a/test/testdata/local_vars/z_super_args_splats_blocks.rb.index-tree-raw.exp b/test/testdata/local_vars/z_super_args_splats_blocks.rb.index-tree-raw.exp index a9d9e39158..b52847c617 100644 --- a/test/testdata/local_vars/z_super_args_splats_blocks.rb.index-tree-raw.exp +++ b/test/testdata/local_vars/z_super_args_splats_blocks.rb.index-tree-raw.exp @@ -34,7 +34,7 @@ ClassDef{ Local{ localVariable = > } - Literal{ value = : } + Literal{ value = : } Local{ localVariable = > } @@ -55,7 +55,7 @@ ClassDef{ recv = Local{ localVariable = > } - fun = > + fun = > block = Block { args = [ ] @@ -123,7 +123,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = ConstantLit{ @@ -186,7 +186,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = ConstantLit{ @@ -247,7 +247,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = Send{ @@ -328,7 +328,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = Send{ @@ -409,7 +409,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = Array{ @@ -493,7 +493,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = Array{ @@ -581,7 +581,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = Send{ @@ -683,7 +683,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = Send{ @@ -771,7 +771,7 @@ ClassDef{ Local{ localVariable = > } - Literal{ value = : } + Literal{ value = : } Local{ localVariable = } @@ -792,7 +792,7 @@ ClassDef{ recv = Local{ localVariable = > } - fun = > + fun = > block = Block { args = [ ] @@ -860,7 +860,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = ConstantLit{ @@ -923,7 +923,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = ConstantLit{ @@ -984,7 +984,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = Send{ @@ -1065,7 +1065,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = Send{ @@ -1146,7 +1146,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = Array{ @@ -1230,7 +1230,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = Array{ @@ -1318,7 +1318,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = Send{ @@ -1420,7 +1420,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = Send{ @@ -1512,7 +1512,7 @@ ClassDef{ Local{ localVariable = > } - Literal{ value = : } + Literal{ value = : } Local{ localVariable = > } @@ -1541,7 +1541,7 @@ ClassDef{ recv = Local{ localVariable = > } - fun = > + fun = > block = Block { args = [ ] @@ -1621,7 +1621,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = ConstantLit{ @@ -1695,7 +1695,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = ConstantLit{ @@ -1771,7 +1771,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = Send{ @@ -1863,7 +1863,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = Send{ @@ -1959,7 +1959,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = Array{ @@ -2054,7 +2054,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = Array{ @@ -2157,7 +2157,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = Send{ @@ -2270,7 +2270,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = Send{ @@ -2371,7 +2371,7 @@ ClassDef{ Local{ localVariable = > } - Literal{ value = : } + Literal{ value = : } Local{ localVariable = > } @@ -2407,7 +2407,7 @@ ClassDef{ recv = Local{ localVariable = > } - fun = > + fun = > block = Block { args = [ ] @@ -2492,7 +2492,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = ConstantLit{ @@ -2573,7 +2573,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = ConstantLit{ @@ -2654,7 +2654,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = Send{ @@ -2753,7 +2753,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = Send{ @@ -2854,7 +2854,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = Array{ @@ -2956,7 +2956,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = Array{ @@ -3064,7 +3064,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = Send{ @@ -3184,7 +3184,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = Send{ @@ -3296,7 +3296,7 @@ ClassDef{ Local{ localVariable = > } - Literal{ value = : } + Literal{ value = : } Local{ localVariable = > } @@ -3356,7 +3356,7 @@ ClassDef{ recv = Local{ localVariable = > } - fun = > + fun = > block = Block { args = [ ] @@ -3469,7 +3469,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = ConstantLit{ @@ -3574,7 +3574,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = ConstantLit{ @@ -3683,7 +3683,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = Send{ @@ -3806,7 +3806,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = Send{ @@ -3935,7 +3935,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = Array{ @@ -4061,7 +4061,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = Array{ @@ -4197,7 +4197,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = Send{ @@ -4341,7 +4341,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = Send{ @@ -4475,7 +4475,7 @@ ClassDef{ Local{ localVariable = > } - Literal{ value = : } + Literal{ value = : } Local{ localVariable = } @@ -4504,7 +4504,7 @@ ClassDef{ recv = Local{ localVariable = > } - fun = > + fun = > block = Block { args = [ ] @@ -4584,7 +4584,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = ConstantLit{ @@ -4658,7 +4658,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = ConstantLit{ @@ -4734,7 +4734,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = Send{ @@ -4826,7 +4826,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = Send{ @@ -4922,7 +4922,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = Array{ @@ -5017,7 +5017,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = Array{ @@ -5120,7 +5120,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = Send{ @@ -5233,7 +5233,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = Send{ @@ -5334,7 +5334,7 @@ ClassDef{ Local{ localVariable = > } - Literal{ value = : } + Literal{ value = : } Local{ localVariable = } @@ -5370,7 +5370,7 @@ ClassDef{ recv = Local{ localVariable = > } - fun = > + fun = > block = Block { args = [ ] @@ -5455,7 +5455,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = ConstantLit{ @@ -5536,7 +5536,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = ConstantLit{ @@ -5617,7 +5617,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = Send{ @@ -5716,7 +5716,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = Send{ @@ -5817,7 +5817,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = Array{ @@ -5919,7 +5919,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = Array{ @@ -6027,7 +6027,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = Send{ @@ -6147,7 +6147,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = Send{ @@ -6259,7 +6259,7 @@ ClassDef{ Local{ localVariable = > } - Literal{ value = : } + Literal{ value = : } Local{ localVariable = } @@ -6319,7 +6319,7 @@ ClassDef{ recv = Local{ localVariable = > } - fun = > + fun = > block = Block { args = [ ] @@ -6432,7 +6432,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = ConstantLit{ @@ -6537,7 +6537,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = ConstantLit{ @@ -6646,7 +6646,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = Send{ @@ -6769,7 +6769,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = Send{ @@ -6898,7 +6898,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = Array{ @@ -7024,7 +7024,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = Array{ @@ -7160,7 +7160,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = Send{ @@ -7304,7 +7304,7 @@ ClassDef{ } ] } - Literal{ value = : } + Literal{ value = : } Send{ flags = {} recv = Send{ diff --git a/test/testdata/local_vars/z_super_args_strict.rb b/test/testdata/local_vars/z_super_args_strict.rb new file mode 100644 index 0000000000..4d6af228a8 --- /dev/null +++ b/test/testdata/local_vars/z_super_args_strict.rb @@ -0,0 +1,99 @@ +# typed: strict + +class Parent + extend T::Sig + + sig { params(arg0: Integer).void } + def takes_positional(arg0) + p arg0 + end + + sig { params(arg0: Integer).void } + def takes_keyword(arg0:) + p arg0 + end + + sig { params(arg0: Integer, arg1: Integer).void } + def takes_two_keyword(arg0:, arg1:) + p arg0 + p arg1 + end + + sig { params(arg0: Integer, arg1: Integer).void } + def takes_positional_then_keyword(arg0, arg1:) + p arg0 + p arg1 + end + + sig { params(args: T.untyped).void } + def takes_rest(*args) + p args + end + + sig { params(args: T.untyped).void } + def takes_keyword_rest(**args) + p args + end + + sig { params(blk: T.proc.void).void } + def takes_block(&blk) + p blk + end + + sig { void } + def zsuper_inside_block + end +end + +class Child < Parent + sig { params(arg0: Integer).void } + def takes_positional(arg0) + super + end + + sig { params(arg0: Integer).void } + def takes_keyword(arg0:) + super + end + + sig { params(arg0: Integer, arg1: Integer).void } + def takes_two_keyword(arg0:, arg1:) + super + end + + sig { params(arg0: Integer, arg1: Integer).void } + def takes_positional_then_keyword(arg0, arg1:) + super + end + + sig { params(args: T.untyped).void } + def takes_rest(*args) + super + end + + sig { params(args: T.untyped).void } + def takes_keyword_rest(**args) + super + end + + sig { params(blk: T.proc.void).void } + def takes_block(&blk) + super + end + + sig { void } + def zsuper_inside_block + 1.times do |i| + super + end + end +end + +module M + extend T::Sig + + sig { void } + def super_inside_module + super + end +end diff --git a/test/testdata/local_vars/z_super_args_strict.rb.index-tree-raw.exp b/test/testdata/local_vars/z_super_args_strict.rb.index-tree-raw.exp new file mode 100644 index 0000000000..45cef812ad --- /dev/null +++ b/test/testdata/local_vars/z_super_args_strict.rb.index-tree-raw.exp @@ -0,0 +1,1392 @@ +ClassDef{ + kind = class + name = EmptyTree + symbol = >> + ancestors = [ConstantLit{ + symbol = (class ::) + orig = nullptr + }] + rhs = [ + ClassDef{ + kind = class + name = UnresolvedConstantLit{ + cnst = > + scope = EmptyTree + } + symbol = >> + ancestors = [ConstantLit{ + symbol = (class ::) + orig = nullptr + }] + rhs = [ + Send{ + flags = {privateOk} + recv = ConstantLit{ + symbol = (module ::Sorbet::Private::Static) + orig = nullptr + } + fun = + block = Block { + args = [ + ] + body = Send{ + flags = {} + recv = Send{ + flags = {privateOk} + recv = Local{ + localVariable = > + } + fun = + block = nullptr + pos_args = 0 + args = [ + Literal{ value = :arg0 } + UnresolvedConstantLit{ + cnst = > + scope = EmptyTree + } + ] + } + fun = + block = nullptr + pos_args = 0 + args = [ + ] + } + } + pos_args = 1 + args = [ + Local{ + localVariable = > + } + ] + } + + MethodDef{ + flags = {} + name = <>> + args = [Local{ + localVariable = + }, BlockArg{ expr = Local{ + localVariable = > + } }] + rhs = Send{ + flags = {privateOk} + recv = Local{ + localVariable = > + } + fun = + block = nullptr + pos_args = 1 + args = [ + Local{ + localVariable = + } + ] + } + } + + Send{ + flags = {privateOk} + recv = ConstantLit{ + symbol = (module ::Sorbet::Private::Static) + orig = nullptr + } + fun = + block = Block { + args = [ + ] + body = Send{ + flags = {} + recv = Send{ + flags = {privateOk} + recv = Local{ + localVariable = > + } + fun = + block = nullptr + pos_args = 0 + args = [ + Literal{ value = :arg0 } + UnresolvedConstantLit{ + cnst = > + scope = EmptyTree + } + ] + } + fun = + block = nullptr + pos_args = 0 + args = [ + ] + } + } + pos_args = 1 + args = [ + Local{ + localVariable = > + } + ] + } + + MethodDef{ + flags = {} + name = <>> + args = [KeywordArg{ expr = Local{ + localVariable = + } }, BlockArg{ expr = Local{ + localVariable = > + } }] + rhs = Send{ + flags = {privateOk} + recv = Local{ + localVariable = > + } + fun = + block = nullptr + pos_args = 1 + args = [ + Local{ + localVariable = + } + ] + } + } + + Send{ + flags = {privateOk} + recv = ConstantLit{ + symbol = (module ::Sorbet::Private::Static) + orig = nullptr + } + fun = + block = Block { + args = [ + ] + body = Send{ + flags = {} + recv = Send{ + flags = {privateOk} + recv = Local{ + localVariable = > + } + fun = + block = nullptr + pos_args = 0 + args = [ + Literal{ value = :arg0 } + UnresolvedConstantLit{ + cnst = > + scope = EmptyTree + } + Literal{ value = :arg1 } + UnresolvedConstantLit{ + cnst = > + scope = EmptyTree + } + ] + } + fun = + block = nullptr + pos_args = 0 + args = [ + ] + } + } + pos_args = 1 + args = [ + Local{ + localVariable = > + } + ] + } + + MethodDef{ + flags = {} + name = <>> + args = [KeywordArg{ expr = Local{ + localVariable = + } }, KeywordArg{ expr = Local{ + localVariable = + } }, BlockArg{ expr = Local{ + localVariable = > + } }] + rhs = InsSeq{ + stats = [ + Send{ + flags = {privateOk} + recv = Local{ + localVariable = > + } + fun = + block = nullptr + pos_args = 1 + args = [ + Local{ + localVariable = + } + ] + } + ], + expr = Send{ + flags = {privateOk} + recv = Local{ + localVariable = > + } + fun = + block = nullptr + pos_args = 1 + args = [ + Local{ + localVariable = + } + ] + } + } + } + + Send{ + flags = {privateOk} + recv = ConstantLit{ + symbol = (module ::Sorbet::Private::Static) + orig = nullptr + } + fun = + block = Block { + args = [ + ] + body = Send{ + flags = {} + recv = Send{ + flags = {privateOk} + recv = Local{ + localVariable = > + } + fun = + block = nullptr + pos_args = 0 + args = [ + Literal{ value = :arg0 } + UnresolvedConstantLit{ + cnst = > + scope = EmptyTree + } + Literal{ value = :arg1 } + UnresolvedConstantLit{ + cnst = > + scope = EmptyTree + } + ] + } + fun = + block = nullptr + pos_args = 0 + args = [ + ] + } + } + pos_args = 1 + args = [ + Local{ + localVariable = > + } + ] + } + + MethodDef{ + flags = {} + name = <>> + args = [Local{ + localVariable = + }, KeywordArg{ expr = Local{ + localVariable = + } }, BlockArg{ expr = Local{ + localVariable = > + } }] + rhs = InsSeq{ + stats = [ + Send{ + flags = {privateOk} + recv = Local{ + localVariable = > + } + fun = + block = nullptr + pos_args = 1 + args = [ + Local{ + localVariable = + } + ] + } + ], + expr = Send{ + flags = {privateOk} + recv = Local{ + localVariable = > + } + fun = + block = nullptr + pos_args = 1 + args = [ + Local{ + localVariable = + } + ] + } + } + } + + Send{ + flags = {privateOk} + recv = ConstantLit{ + symbol = (module ::Sorbet::Private::Static) + orig = nullptr + } + fun = + block = Block { + args = [ + ] + body = Send{ + flags = {} + recv = Send{ + flags = {privateOk} + recv = Local{ + localVariable = > + } + fun = + block = nullptr + pos_args = 0 + args = [ + Literal{ value = :args } + Send{ + flags = {} + recv = UnresolvedConstantLit{ + cnst = > + scope = EmptyTree + } + fun = + block = nullptr + pos_args = 0 + args = [ + ] + } + ] + } + fun = + block = nullptr + pos_args = 0 + args = [ + ] + } + } + pos_args = 1 + args = [ + Local{ + localVariable = > + } + ] + } + + MethodDef{ + flags = {} + name = <>> + args = [RestArg{ expr = Local{ + localVariable = + } }, BlockArg{ expr = Local{ + localVariable = > + } }] + rhs = Send{ + flags = {privateOk} + recv = Local{ + localVariable = > + } + fun = + block = nullptr + pos_args = 1 + args = [ + Local{ + localVariable = + } + ] + } + } + + Send{ + flags = {privateOk} + recv = ConstantLit{ + symbol = (module ::Sorbet::Private::Static) + orig = nullptr + } + fun = + block = Block { + args = [ + ] + body = Send{ + flags = {} + recv = Send{ + flags = {privateOk} + recv = Local{ + localVariable = > + } + fun = + block = nullptr + pos_args = 0 + args = [ + Literal{ value = :args } + Send{ + flags = {} + recv = UnresolvedConstantLit{ + cnst = > + scope = EmptyTree + } + fun = + block = nullptr + pos_args = 0 + args = [ + ] + } + ] + } + fun = + block = nullptr + pos_args = 0 + args = [ + ] + } + } + pos_args = 1 + args = [ + Local{ + localVariable = > + } + ] + } + + MethodDef{ + flags = {} + name = <>> + args = [RestArg{ expr = KeywordArg{ expr = Local{ + localVariable = + } } }, BlockArg{ expr = Local{ + localVariable = > + } }] + rhs = Send{ + flags = {privateOk} + recv = Local{ + localVariable = > + } + fun = + block = nullptr + pos_args = 1 + args = [ + Local{ + localVariable = + } + ] + } + } + + Send{ + flags = {privateOk} + recv = ConstantLit{ + symbol = (module ::Sorbet::Private::Static) + orig = nullptr + } + fun = + block = Block { + args = [ + ] + body = Send{ + flags = {} + recv = Send{ + flags = {privateOk} + recv = Local{ + localVariable = > + } + fun = + block = nullptr + pos_args = 0 + args = [ + Literal{ value = :blk } + Send{ + flags = {} + recv = Send{ + flags = {} + recv = UnresolvedConstantLit{ + cnst = > + scope = EmptyTree + } + fun = + block = nullptr + pos_args = 0 + args = [ + ] + } + fun = + block = nullptr + pos_args = 0 + args = [ + ] + } + ] + } + fun = + block = nullptr + pos_args = 0 + args = [ + ] + } + } + pos_args = 1 + args = [ + Local{ + localVariable = > + } + ] + } + + MethodDef{ + flags = {} + name = <>> + args = [BlockArg{ expr = Local{ + localVariable = + } }] + rhs = Send{ + flags = {privateOk} + recv = Local{ + localVariable = > + } + fun = + block = nullptr + pos_args = 1 + args = [ + Local{ + localVariable = + } + ] + } + } + + Send{ + flags = {privateOk} + recv = ConstantLit{ + symbol = (module ::Sorbet::Private::Static) + orig = nullptr + } + fun = + block = Block { + args = [ + ] + body = Send{ + flags = {privateOk} + recv = Local{ + localVariable = > + } + fun = + block = nullptr + pos_args = 0 + args = [ + ] + } + } + pos_args = 1 + args = [ + Local{ + localVariable = > + } + ] + } + + MethodDef{ + flags = {} + name = <>> + args = [BlockArg{ expr = Local{ + localVariable = > + } }] + rhs = EmptyTree + } + + Send{ + flags = {privateOk} + recv = Local{ + localVariable = > + } + fun = + block = nullptr + pos_args = 1 + args = [ + UnresolvedConstantLit{ + cnst = > + scope = UnresolvedConstantLit{ + cnst = > + scope = EmptyTree + } + } + ] + } + + + + + + + + + + + + + + + + + ] + } + + ClassDef{ + kind = class + name = UnresolvedConstantLit{ + cnst = > + scope = EmptyTree + } + symbol = >> + ancestors = [UnresolvedConstantLit{ + cnst = > + scope = EmptyTree + }] + rhs = [ + Send{ + flags = {privateOk} + recv = ConstantLit{ + symbol = (module ::Sorbet::Private::Static) + orig = nullptr + } + fun = + block = Block { + args = [ + ] + body = Send{ + flags = {} + recv = Send{ + flags = {privateOk} + recv = Local{ + localVariable = > + } + fun = + block = nullptr + pos_args = 0 + args = [ + Literal{ value = :arg0 } + UnresolvedConstantLit{ + cnst = > + scope = EmptyTree + } + ] + } + fun = + block = nullptr + pos_args = 0 + args = [ + ] + } + } + pos_args = 1 + args = [ + Local{ + localVariable = > + } + ] + } + + MethodDef{ + flags = {} + name = <>> + args = [Local{ + localVariable = + }, BlockArg{ expr = Local{ + localVariable = > + } }] + rhs = Send{ + flags = {privateOk} + recv = Local{ + localVariable = > + } + fun = > + block = nullptr + pos_args = 1 + args = [ + Local{ + localVariable = + } + ] + } + } + + Send{ + flags = {privateOk} + recv = ConstantLit{ + symbol = (module ::Sorbet::Private::Static) + orig = nullptr + } + fun = + block = Block { + args = [ + ] + body = Send{ + flags = {} + recv = Send{ + flags = {privateOk} + recv = Local{ + localVariable = > + } + fun = + block = nullptr + pos_args = 0 + args = [ + Literal{ value = :arg0 } + UnresolvedConstantLit{ + cnst = > + scope = EmptyTree + } + ] + } + fun = + block = nullptr + pos_args = 0 + args = [ + ] + } + } + pos_args = 1 + args = [ + Local{ + localVariable = > + } + ] + } + + MethodDef{ + flags = {} + name = <>> + args = [KeywordArg{ expr = Local{ + localVariable = + } }, BlockArg{ expr = Local{ + localVariable = > + } }] + rhs = Send{ + flags = {privateOk} + recv = Local{ + localVariable = > + } + fun = > + block = nullptr + pos_args = 0 + args = [ + Literal{ value = :arg0 } + Local{ + localVariable = + } + ] + } + } + + Send{ + flags = {privateOk} + recv = ConstantLit{ + symbol = (module ::Sorbet::Private::Static) + orig = nullptr + } + fun = + block = Block { + args = [ + ] + body = Send{ + flags = {} + recv = Send{ + flags = {privateOk} + recv = Local{ + localVariable = > + } + fun = + block = nullptr + pos_args = 0 + args = [ + Literal{ value = :arg0 } + UnresolvedConstantLit{ + cnst = > + scope = EmptyTree + } + Literal{ value = :arg1 } + UnresolvedConstantLit{ + cnst = > + scope = EmptyTree + } + ] + } + fun = + block = nullptr + pos_args = 0 + args = [ + ] + } + } + pos_args = 1 + args = [ + Local{ + localVariable = > + } + ] + } + + MethodDef{ + flags = {} + name = <>> + args = [KeywordArg{ expr = Local{ + localVariable = + } }, KeywordArg{ expr = Local{ + localVariable = + } }, BlockArg{ expr = Local{ + localVariable = > + } }] + rhs = Send{ + flags = {privateOk} + recv = Local{ + localVariable = > + } + fun = > + block = nullptr + pos_args = 0 + args = [ + Literal{ value = :arg0 } + Local{ + localVariable = + } + Literal{ value = :arg1 } + Local{ + localVariable = + } + ] + } + } + + Send{ + flags = {privateOk} + recv = ConstantLit{ + symbol = (module ::Sorbet::Private::Static) + orig = nullptr + } + fun = + block = Block { + args = [ + ] + body = Send{ + flags = {} + recv = Send{ + flags = {privateOk} + recv = Local{ + localVariable = > + } + fun = + block = nullptr + pos_args = 0 + args = [ + Literal{ value = :arg0 } + UnresolvedConstantLit{ + cnst = > + scope = EmptyTree + } + Literal{ value = :arg1 } + UnresolvedConstantLit{ + cnst = > + scope = EmptyTree + } + ] + } + fun = + block = nullptr + pos_args = 0 + args = [ + ] + } + } + pos_args = 1 + args = [ + Local{ + localVariable = > + } + ] + } + + MethodDef{ + flags = {} + name = <>> + args = [Local{ + localVariable = + }, KeywordArg{ expr = Local{ + localVariable = + } }, BlockArg{ expr = Local{ + localVariable = > + } }] + rhs = Send{ + flags = {privateOk} + recv = Local{ + localVariable = > + } + fun = > + block = nullptr + pos_args = 1 + args = [ + Local{ + localVariable = + } + Literal{ value = :arg1 } + Local{ + localVariable = + } + ] + } + } + + Send{ + flags = {privateOk} + recv = ConstantLit{ + symbol = (module ::Sorbet::Private::Static) + orig = nullptr + } + fun = + block = Block { + args = [ + ] + body = Send{ + flags = {} + recv = Send{ + flags = {privateOk} + recv = Local{ + localVariable = > + } + fun = + block = nullptr + pos_args = 0 + args = [ + Literal{ value = :args } + Send{ + flags = {} + recv = UnresolvedConstantLit{ + cnst = > + scope = EmptyTree + } + fun = + block = nullptr + pos_args = 0 + args = [ + ] + } + ] + } + fun = + block = nullptr + pos_args = 0 + args = [ + ] + } + } + pos_args = 1 + args = [ + Local{ + localVariable = > + } + ] + } + + MethodDef{ + flags = {} + name = <>> + args = [RestArg{ expr = Local{ + localVariable = + } }, BlockArg{ expr = Local{ + localVariable = > + } }] + rhs = Send{ + flags = {privateOk} + recv = ConstantLit{ + symbol = (class ::) + orig = nullptr + } + fun = > + block = nullptr + pos_args = 4 + args = [ + Send{ + flags = {} + recv = ConstantLit{ + symbol = (module ::T) + orig = nullptr + } + fun = + block = nullptr + pos_args = 1 + args = [ + Local{ + localVariable = > + } + ] + } + Literal{ value = : } + Send{ + flags = {} + recv = ConstantLit{ + symbol = (class ::) + orig = nullptr + } + fun = > + block = nullptr + pos_args = 1 + args = [ + Local{ + localVariable = + } + ] + } + Literal{ value = nil } + ] + } + } + + Send{ + flags = {privateOk} + recv = ConstantLit{ + symbol = (module ::Sorbet::Private::Static) + orig = nullptr + } + fun = + block = Block { + args = [ + ] + body = Send{ + flags = {} + recv = Send{ + flags = {privateOk} + recv = Local{ + localVariable = > + } + fun = + block = nullptr + pos_args = 0 + args = [ + Literal{ value = :args } + Send{ + flags = {} + recv = UnresolvedConstantLit{ + cnst = > + scope = EmptyTree + } + fun = + block = nullptr + pos_args = 0 + args = [ + ] + } + ] + } + fun = + block = nullptr + pos_args = 0 + args = [ + ] + } + } + pos_args = 1 + args = [ + Local{ + localVariable = > + } + ] + } + + MethodDef{ + flags = {} + name = <>> + args = [RestArg{ expr = KeywordArg{ expr = Local{ + localVariable = + } } }, BlockArg{ expr = Local{ + localVariable = > + } }] + rhs = Send{ + flags = {privateOk} + recv = Local{ + localVariable = > + } + fun = > + block = nullptr + pos_args = 0 + args = [ + Send{ + flags = {} + recv = ConstantLit{ + symbol = (class ::) + orig = nullptr + } + fun = > + block = nullptr + pos_args = 1 + args = [ + Local{ + localVariable = + } + ] + } + ] + } + } + + Send{ + flags = {privateOk} + recv = ConstantLit{ + symbol = (module ::Sorbet::Private::Static) + orig = nullptr + } + fun = + block = Block { + args = [ + ] + body = Send{ + flags = {} + recv = Send{ + flags = {privateOk} + recv = Local{ + localVariable = > + } + fun = + block = nullptr + pos_args = 0 + args = [ + Literal{ value = :blk } + Send{ + flags = {} + recv = Send{ + flags = {} + recv = UnresolvedConstantLit{ + cnst = > + scope = EmptyTree + } + fun = + block = nullptr + pos_args = 0 + args = [ + ] + } + fun = + block = nullptr + pos_args = 0 + args = [ + ] + } + ] + } + fun = + block = nullptr + pos_args = 0 + args = [ + ] + } + } + pos_args = 1 + args = [ + Local{ + localVariable = > + } + ] + } + + MethodDef{ + flags = {} + name = <>> + args = [BlockArg{ expr = Local{ + localVariable = + } }] + rhs = Send{ + flags = {privateOk} + recv = ConstantLit{ + symbol = (class ::) + orig = nullptr + } + fun = > + block = nullptr + pos_args = 3 + args = [ + Local{ + localVariable = > + } + Literal{ value = : } + Local{ + localVariable = + } + ] + } + } + + Send{ + flags = {privateOk} + recv = ConstantLit{ + symbol = (module ::Sorbet::Private::Static) + orig = nullptr + } + fun = + block = Block { + args = [ + ] + body = Send{ + flags = {privateOk} + recv = Local{ + localVariable = > + } + fun = + block = nullptr + pos_args = 0 + args = [ + ] + } + } + pos_args = 1 + args = [ + Local{ + localVariable = > + } + ] + } + + MethodDef{ + flags = {} + name = <>> + args = [BlockArg{ expr = Local{ + localVariable = > + } }] + rhs = Send{ + flags = {} + recv = Literal{ value = 1 } + fun = + block = Block { + args = [ + Local{ + localVariable = $1 + } + ] + body = Send{ + flags = {privateOk} + recv = Local{ + localVariable = > + } + fun = > + block = nullptr + pos_args = 0 + args = [ + ] + } + } + pos_args = 0 + args = [ + ] + } + } + + + + + + + + + + + + + + + + + ] + } + + ClassDef{ + kind = module + name = UnresolvedConstantLit{ + cnst = > + scope = EmptyTree + } + symbol = >> + ancestors = [] + rhs = [ + Send{ + flags = {privateOk} + recv = ConstantLit{ + symbol = (module ::Sorbet::Private::Static) + orig = nullptr + } + fun = + block = Block { + args = [ + ] + body = Send{ + flags = {privateOk} + recv = Local{ + localVariable = > + } + fun = + block = nullptr + pos_args = 0 + args = [ + ] + } + } + pos_args = 1 + args = [ + Local{ + localVariable = > + } + ] + } + + MethodDef{ + flags = {} + name = <>> + args = [BlockArg{ expr = Local{ + localVariable = > + } }] + rhs = Send{ + flags = {privateOk} + recv = Local{ + localVariable = > + } + fun = > + block = nullptr + pos_args = 0 + args = [ + ] + } + } + + Send{ + flags = {privateOk} + recv = Local{ + localVariable = > + } + fun = + block = nullptr + pos_args = 1 + args = [ + UnresolvedConstantLit{ + cnst = > + scope = UnresolvedConstantLit{ + cnst = > + scope = EmptyTree + } + } + ] + } + + + ] + } + ] +} diff --git a/test/testdata/lsp/code_actions/convert_to_singleton_method/block_pass.A.rbedited b/test/testdata/lsp/code_actions/convert_to_singleton_method/block_pass.A.rbedited index 64cd9fc654..db01400306 100644 --- a/test/testdata/lsp/code_actions/convert_to_singleton_method/block_pass.A.rbedited +++ b/test/testdata/lsp/code_actions/convert_to_singleton_method/block_pass.A.rbedited @@ -6,7 +6,7 @@ class A extend T::Sig def self.takes_block(this, x, &blk) - # ^ apply-code-action: [A] Convert to singleton class method (best effort) + # | apply-code-action: [A] Convert to singleton class method (best effort) puts "Hello, peter." end end diff --git a/test/testdata/lsp/code_actions/convert_to_singleton_method/block_pass.rb b/test/testdata/lsp/code_actions/convert_to_singleton_method/block_pass.rb index abd22d9d4a..7d1062a36e 100644 --- a/test/testdata/lsp/code_actions/convert_to_singleton_method/block_pass.rb +++ b/test/testdata/lsp/code_actions/convert_to_singleton_method/block_pass.rb @@ -6,7 +6,7 @@ class A extend T::Sig def takes_block(x, &blk) - # ^ apply-code-action: [A] Convert to singleton class method (best effort) + # | apply-code-action: [A] Convert to singleton class method (best effort) puts "Hello, peter." end end diff --git a/test/testdata/lsp/code_actions/convert_to_singleton_method/kwarg_paren.A.rbedited b/test/testdata/lsp/code_actions/convert_to_singleton_method/kwarg_paren.A.rbedited new file mode 100644 index 0000000000..1dd8489331 --- /dev/null +++ b/test/testdata/lsp/code_actions/convert_to_singleton_method/kwarg_paren.A.rbedited @@ -0,0 +1,14 @@ +# typed: true +# selective-apply-code-action: refactor.rewrite + +class A + def self.example(this, x:) + # | apply-code-action: [A] Convert to singleton class method (best effort) + end +end + +A.example(A.new, x: (0)) + +A.example(A.new, x: begin + 0 + end) diff --git a/test/testdata/lsp/code_actions/convert_to_singleton_method/kwarg_paren.rb b/test/testdata/lsp/code_actions/convert_to_singleton_method/kwarg_paren.rb new file mode 100644 index 0000000000..048f7cf6e2 --- /dev/null +++ b/test/testdata/lsp/code_actions/convert_to_singleton_method/kwarg_paren.rb @@ -0,0 +1,18 @@ +# typed: true +# selective-apply-code-action: refactor.rewrite + +class A + def example(x:) + # | apply-code-action: [A] Convert to singleton class method (best effort) + end +end + +A.new.example( + x: (0) +) + +A.new.example( + x: begin + 0 + end +) diff --git a/test/testdata/lsp/code_actions/convert_to_singleton_method/no_sig.A.rbedited b/test/testdata/lsp/code_actions/convert_to_singleton_method/no_sig.A.rbedited index ac16bf21f9..d0f4892fbb 100644 --- a/test/testdata/lsp/code_actions/convert_to_singleton_method/no_sig.A.rbedited +++ b/test/testdata/lsp/code_actions/convert_to_singleton_method/no_sig.A.rbedited @@ -6,7 +6,7 @@ class A extend T::Sig def self.no_sig(this, x) - # ^ apply-code-action: [A] Convert to singleton class method (best effort) + # | apply-code-action: [A] Convert to singleton class method (best effort) puts "Hello, peter." end end diff --git a/test/testdata/lsp/code_actions/convert_to_singleton_method/no_sig.rb b/test/testdata/lsp/code_actions/convert_to_singleton_method/no_sig.rb index 4706372ac5..8c5efd8053 100644 --- a/test/testdata/lsp/code_actions/convert_to_singleton_method/no_sig.rb +++ b/test/testdata/lsp/code_actions/convert_to_singleton_method/no_sig.rb @@ -6,7 +6,7 @@ class A extend T::Sig def no_sig(x) - # ^ apply-code-action: [A] Convert to singleton class method (best effort) + # | apply-code-action: [A] Convert to singleton class method (best effort) puts "Hello, peter." end end diff --git a/test/testdata/lsp/code_actions/convert_to_singleton_method/nullary.A.rbedited b/test/testdata/lsp/code_actions/convert_to_singleton_method/nullary.A.rbedited index 25cc24d646..2e1f401621 100644 --- a/test/testdata/lsp/code_actions/convert_to_singleton_method/nullary.A.rbedited +++ b/test/testdata/lsp/code_actions/convert_to_singleton_method/nullary.A.rbedited @@ -7,7 +7,7 @@ class A sig {params(this: A).void} def self.nullary(this) - # ^ apply-code-action: [A] Convert to singleton class method (best effort) + # | apply-code-action: [A] Convert to singleton class method (best effort) puts "Hello, peter." end @@ -34,4 +34,4 @@ end (T.unsafe(A.new)).nullary -A.nullary(A.new + A.new) +A.nullary((A.new + A.new)) diff --git a/test/testdata/lsp/code_actions/convert_to_singleton_method/nullary.rb b/test/testdata/lsp/code_actions/convert_to_singleton_method/nullary.rb index 2ce0753b30..b8c73af17e 100644 --- a/test/testdata/lsp/code_actions/convert_to_singleton_method/nullary.rb +++ b/test/testdata/lsp/code_actions/convert_to_singleton_method/nullary.rb @@ -7,7 +7,7 @@ class A sig {void} def nullary - # ^ apply-code-action: [A] Convert to singleton class method (best effort) + # | apply-code-action: [A] Convert to singleton class method (best effort) puts "Hello, peter." end diff --git a/test/testdata/lsp/code_actions/convert_to_singleton_method/unary.A.rbedited b/test/testdata/lsp/code_actions/convert_to_singleton_method/unary.A.rbedited index b47f38647e..535d732fc5 100644 --- a/test/testdata/lsp/code_actions/convert_to_singleton_method/unary.A.rbedited +++ b/test/testdata/lsp/code_actions/convert_to_singleton_method/unary.A.rbedited @@ -7,7 +7,7 @@ class A sig {params(this: A, x: Integer).void} def self.unary(this, x) - # ^ apply-code-action: [A] Convert to singleton class method (best effort) + # | apply-code-action: [A] Convert to singleton class method (best effort) puts "Hello, peter." end @@ -31,4 +31,4 @@ end (T.unsafe(A.new)).unary(0) -A.unary(A.new + A.new, 0) +A.unary((A.new + A.new), 0) diff --git a/test/testdata/lsp/code_actions/convert_to_singleton_method/unary.rb b/test/testdata/lsp/code_actions/convert_to_singleton_method/unary.rb index c8426ec636..ed987fa55a 100644 --- a/test/testdata/lsp/code_actions/convert_to_singleton_method/unary.rb +++ b/test/testdata/lsp/code_actions/convert_to_singleton_method/unary.rb @@ -7,7 +7,7 @@ class A sig {params(x: Integer).void} def unary(x) - # ^ apply-code-action: [A] Convert to singleton class method (best effort) + # | apply-code-action: [A] Convert to singleton class method (best effort) puts "Hello, peter." end diff --git a/test/testdata/lsp/code_actions/delete_must.A.rbedited b/test/testdata/lsp/code_actions/delete_must.A.rbedited new file mode 100644 index 0000000000..bda88e17c4 --- /dev/null +++ b/test/testdata/lsp/code_actions/delete_must.A.rbedited @@ -0,0 +1,6 @@ +# typed: true +# selective-apply-code-action: refactor.rewrite + +xs = T::Array[Integer].new.first +xs.even? +# | apply-code-action: [A] Delete T.must diff --git a/test/testdata/lsp/code_actions/delete_must.rb b/test/testdata/lsp/code_actions/delete_must.rb new file mode 100644 index 0000000000..4e161f86ea --- /dev/null +++ b/test/testdata/lsp/code_actions/delete_must.rb @@ -0,0 +1,6 @@ +# typed: true +# selective-apply-code-action: refactor.rewrite + +xs = T::Array[Integer].new.first +T.must(xs).even? +# | apply-code-action: [A] Delete T.must diff --git a/test/testdata/lsp/code_actions/delete_unsafe.A.rbedited b/test/testdata/lsp/code_actions/delete_unsafe.A.rbedited index 285829f5ec..69b36cab61 100644 --- a/test/testdata/lsp/code_actions/delete_unsafe.A.rbedited +++ b/test/testdata/lsp/code_actions/delete_unsafe.A.rbedited @@ -2,14 +2,14 @@ # selective-apply-code-action: refactor.rewrite 0.even? -# ^ apply-code-action: [A] Delete T.unsafe +# | apply-code-action: [A] Delete T.unsafe T.unsafe( -# ^ apply-code-action: [B] Delete T.unsafe +# | apply-code-action: [B] Delete T.unsafe 0 ).even? _foo = T .unsafe(0) - # ^ apply-code-action: [C] Delete T.unsafe + # | apply-code-action: [C] Delete T.unsafe .even? diff --git a/test/testdata/lsp/code_actions/delete_unsafe.B.rbedited b/test/testdata/lsp/code_actions/delete_unsafe.B.rbedited index 329ab4f767..8cac444ecf 100644 --- a/test/testdata/lsp/code_actions/delete_unsafe.B.rbedited +++ b/test/testdata/lsp/code_actions/delete_unsafe.B.rbedited @@ -2,11 +2,11 @@ # selective-apply-code-action: refactor.rewrite T.unsafe(0).even? -# ^ apply-code-action: [A] Delete T.unsafe +# | apply-code-action: [A] Delete T.unsafe 0.even? _foo = T .unsafe(0) - # ^ apply-code-action: [C] Delete T.unsafe + # | apply-code-action: [C] Delete T.unsafe .even? diff --git a/test/testdata/lsp/code_actions/delete_unsafe.C.rbedited b/test/testdata/lsp/code_actions/delete_unsafe.C.rbedited index 08035c9a37..79d2e7e100 100644 --- a/test/testdata/lsp/code_actions/delete_unsafe.C.rbedited +++ b/test/testdata/lsp/code_actions/delete_unsafe.C.rbedited @@ -2,13 +2,13 @@ # selective-apply-code-action: refactor.rewrite T.unsafe(0).even? -# ^ apply-code-action: [A] Delete T.unsafe +# | apply-code-action: [A] Delete T.unsafe T.unsafe( -# ^ apply-code-action: [B] Delete T.unsafe +# | apply-code-action: [B] Delete T.unsafe 0 ).even? _foo = 0 - # ^ apply-code-action: [C] Delete T.unsafe + # | apply-code-action: [C] Delete T.unsafe .even? diff --git a/test/testdata/lsp/code_actions/delete_unsafe.rb b/test/testdata/lsp/code_actions/delete_unsafe.rb index 17ee628d73..36dd72a64d 100644 --- a/test/testdata/lsp/code_actions/delete_unsafe.rb +++ b/test/testdata/lsp/code_actions/delete_unsafe.rb @@ -2,14 +2,14 @@ # selective-apply-code-action: refactor.rewrite T.unsafe(0).even? -# ^ apply-code-action: [A] Delete T.unsafe +# | apply-code-action: [A] Delete T.unsafe T.unsafe( -# ^ apply-code-action: [B] Delete T.unsafe +# | apply-code-action: [B] Delete T.unsafe 0 ).even? _foo = T .unsafe(0) - # ^ apply-code-action: [C] Delete T.unsafe + # | apply-code-action: [C] Delete T.unsafe .even? diff --git a/test/testdata/lsp/code_actions/extract_variable_single/block.A.rbedited b/test/testdata/lsp/code_actions/extract_variable_single/block.A.rbedited new file mode 100644 index 0000000000..03187a8922 --- /dev/null +++ b/test/testdata/lsp/code_actions/extract_variable_single/block.A.rbedited @@ -0,0 +1,29 @@ +# typed: true +# selective-apply-code-action: refactor.extract +# enable-experimental-lsp-extract-to-variable: true + +[].each { |x| newVariable = x.foo; return 0 if newVariable} +# ^^^^^ apply-code-action: [A] Extract Variable + +1.times do |x| 1 + 123 end +# ^^^ apply-code-action: [B] Extract Variable + +1.times do |x| 1 + 1; 1 + 1234 end +# ^^^^ apply-code-action: [C] Extract Variable + +1.times do |x| + 1 + 12345 +# ^^^^^ apply-code-action: [D] Extract Variable +end + +1.times do |x| + 1 + 1 + 1 + 123456 +# ^^^^^^ apply-code-action: [E] Extract Variable +end + +1.times do |x| + 1 + 1 + 1 + 2; 1 + 1234567 +# ^^^^^^^ apply-code-action: [F] Extract Variable +end diff --git a/test/testdata/lsp/code_actions/extract_variable_single/block.B.rbedited b/test/testdata/lsp/code_actions/extract_variable_single/block.B.rbedited new file mode 100644 index 0000000000..0f41900177 --- /dev/null +++ b/test/testdata/lsp/code_actions/extract_variable_single/block.B.rbedited @@ -0,0 +1,29 @@ +# typed: true +# selective-apply-code-action: refactor.extract +# enable-experimental-lsp-extract-to-variable: true + +[].each { |x| return 0 if x.foo} +# ^^^^^ apply-code-action: [A] Extract Variable + +1.times do |x| newVariable = 123; 1 + newVariable end +# ^^^ apply-code-action: [B] Extract Variable + +1.times do |x| 1 + 1; 1 + 1234 end +# ^^^^ apply-code-action: [C] Extract Variable + +1.times do |x| + 1 + 12345 +# ^^^^^ apply-code-action: [D] Extract Variable +end + +1.times do |x| + 1 + 1 + 1 + 123456 +# ^^^^^^ apply-code-action: [E] Extract Variable +end + +1.times do |x| + 1 + 1 + 1 + 2; 1 + 1234567 +# ^^^^^^^ apply-code-action: [F] Extract Variable +end diff --git a/test/testdata/lsp/code_actions/extract_variable_single/block.C.rbedited b/test/testdata/lsp/code_actions/extract_variable_single/block.C.rbedited new file mode 100644 index 0000000000..ec09e60ceb --- /dev/null +++ b/test/testdata/lsp/code_actions/extract_variable_single/block.C.rbedited @@ -0,0 +1,29 @@ +# typed: true +# selective-apply-code-action: refactor.extract +# enable-experimental-lsp-extract-to-variable: true + +[].each { |x| return 0 if x.foo} +# ^^^^^ apply-code-action: [A] Extract Variable + +1.times do |x| 1 + 123 end +# ^^^ apply-code-action: [B] Extract Variable + +1.times do |x| 1 + 1; newVariable = 1234; 1 + newVariable end +# ^^^^ apply-code-action: [C] Extract Variable + +1.times do |x| + 1 + 12345 +# ^^^^^ apply-code-action: [D] Extract Variable +end + +1.times do |x| + 1 + 1 + 1 + 123456 +# ^^^^^^ apply-code-action: [E] Extract Variable +end + +1.times do |x| + 1 + 1 + 1 + 2; 1 + 1234567 +# ^^^^^^^ apply-code-action: [F] Extract Variable +end diff --git a/test/testdata/lsp/code_actions/extract_variable_single/block.D.rbedited b/test/testdata/lsp/code_actions/extract_variable_single/block.D.rbedited new file mode 100644 index 0000000000..4d751c05e4 --- /dev/null +++ b/test/testdata/lsp/code_actions/extract_variable_single/block.D.rbedited @@ -0,0 +1,30 @@ +# typed: true +# selective-apply-code-action: refactor.extract +# enable-experimental-lsp-extract-to-variable: true + +[].each { |x| return 0 if x.foo} +# ^^^^^ apply-code-action: [A] Extract Variable + +1.times do |x| 1 + 123 end +# ^^^ apply-code-action: [B] Extract Variable + +1.times do |x| 1 + 1; 1 + 1234 end +# ^^^^ apply-code-action: [C] Extract Variable + +1.times do |x| + newVariable = 12345 + 1 + newVariable +# ^^^^^ apply-code-action: [D] Extract Variable +end + +1.times do |x| + 1 + 1 + 1 + 123456 +# ^^^^^^ apply-code-action: [E] Extract Variable +end + +1.times do |x| + 1 + 1 + 1 + 2; 1 + 1234567 +# ^^^^^^^ apply-code-action: [F] Extract Variable +end diff --git a/test/testdata/lsp/code_actions/extract_variable_single/block.E.rbedited b/test/testdata/lsp/code_actions/extract_variable_single/block.E.rbedited new file mode 100644 index 0000000000..3bd7c9a868 --- /dev/null +++ b/test/testdata/lsp/code_actions/extract_variable_single/block.E.rbedited @@ -0,0 +1,30 @@ +# typed: true +# selective-apply-code-action: refactor.extract +# enable-experimental-lsp-extract-to-variable: true + +[].each { |x| return 0 if x.foo} +# ^^^^^ apply-code-action: [A] Extract Variable + +1.times do |x| 1 + 123 end +# ^^^ apply-code-action: [B] Extract Variable + +1.times do |x| 1 + 1; 1 + 1234 end +# ^^^^ apply-code-action: [C] Extract Variable + +1.times do |x| + 1 + 12345 +# ^^^^^ apply-code-action: [D] Extract Variable +end + +1.times do |x| + 1 + 1 + newVariable = 123456 + 1 + newVariable +# ^^^^^^ apply-code-action: [E] Extract Variable +end + +1.times do |x| + 1 + 1 + 1 + 2; 1 + 1234567 +# ^^^^^^^ apply-code-action: [F] Extract Variable +end diff --git a/test/testdata/lsp/code_actions/extract_variable_single/block.F.rbedited b/test/testdata/lsp/code_actions/extract_variable_single/block.F.rbedited new file mode 100644 index 0000000000..207d904cff --- /dev/null +++ b/test/testdata/lsp/code_actions/extract_variable_single/block.F.rbedited @@ -0,0 +1,29 @@ +# typed: true +# selective-apply-code-action: refactor.extract +# enable-experimental-lsp-extract-to-variable: true + +[].each { |x| return 0 if x.foo} +# ^^^^^ apply-code-action: [A] Extract Variable + +1.times do |x| 1 + 123 end +# ^^^ apply-code-action: [B] Extract Variable + +1.times do |x| 1 + 1; 1 + 1234 end +# ^^^^ apply-code-action: [C] Extract Variable + +1.times do |x| + 1 + 12345 +# ^^^^^ apply-code-action: [D] Extract Variable +end + +1.times do |x| + 1 + 1 + 1 + 123456 +# ^^^^^^ apply-code-action: [E] Extract Variable +end + +1.times do |x| + 1 + 1 + 1 + 2; newVariable = 1234567; 1 + newVariable +# ^^^^^^^ apply-code-action: [F] Extract Variable +end diff --git a/test/testdata/lsp/code_actions/extract_variable_single/block.rb b/test/testdata/lsp/code_actions/extract_variable_single/block.rb new file mode 100644 index 0000000000..6cd2496b26 --- /dev/null +++ b/test/testdata/lsp/code_actions/extract_variable_single/block.rb @@ -0,0 +1,29 @@ +# typed: true +# selective-apply-code-action: refactor.extract +# enable-experimental-lsp-extract-to-variable: true + +[].each { |x| return 0 if x.foo} +# ^^^^^ apply-code-action: [A] Extract Variable + +1.times do |x| 1 + 123 end +# ^^^ apply-code-action: [B] Extract Variable + +1.times do |x| 1 + 1; 1 + 1234 end +# ^^^^ apply-code-action: [C] Extract Variable + +1.times do |x| + 1 + 12345 +# ^^^^^ apply-code-action: [D] Extract Variable +end + +1.times do |x| + 1 + 1 + 1 + 123456 +# ^^^^^^ apply-code-action: [E] Extract Variable +end + +1.times do |x| + 1 + 1 + 1 + 2; 1 + 1234567 +# ^^^^^^^ apply-code-action: [F] Extract Variable +end diff --git a/test/testdata/lsp/code_actions/extract_variable_single/bugs_false_negatives.rb b/test/testdata/lsp/code_actions/extract_variable_single/bugs_false_negatives.rb new file mode 100644 index 0000000000..81ba8d95b9 --- /dev/null +++ b/test/testdata/lsp/code_actions/extract_variable_single/bugs_false_negatives.rb @@ -0,0 +1,14 @@ +# typed: true +# selective-apply-code-action: refactor.extract +# enable-experimental-lsp-extract-to-variable: true +# assert-no-code-action: refactor.extract + +# This file contains cases where we should allow the user to extract to a variable, +# but incorrectly disallow it. + +a = 1 + +case a +# ^ apply-code-action: [A] Extract Variable +when Integer +end diff --git a/test/testdata/lsp/code_actions/extract_variable_single/bugs_false_positives.A.rbedited b/test/testdata/lsp/code_actions/extract_variable_single/bugs_false_positives.A.rbedited new file mode 100644 index 0000000000..d416843ad9 --- /dev/null +++ b/test/testdata/lsp/code_actions/extract_variable_single/bugs_false_positives.A.rbedited @@ -0,0 +1,20 @@ +# typed: true +# selective-apply-code-action: refactor.extract +# enable-experimental-lsp-extract-to-variable: true +# +# This file contain cases where we should disallow the user from extracting to variable, +# but incorrectly allow it. + +newVariable = i +/W[aeiou]rd/newVariable +# ^ apply-code-action: [A] Extract Variable + + /A[aeiou]rd/m +# ^^^^^^^^^^ apply-code-action: [B] Extract Variable + +class A < T::Struct + prop :x, Integer +# ^^ apply-code-action: [C] Extract Variable + prop :y, String +# ^^^^^^ apply-code-action: [D] Extract Variable +end diff --git a/test/testdata/lsp/code_actions/extract_variable_single/bugs_false_positives.B.rbedited b/test/testdata/lsp/code_actions/extract_variable_single/bugs_false_positives.B.rbedited new file mode 100644 index 0000000000..52f89ab38e --- /dev/null +++ b/test/testdata/lsp/code_actions/extract_variable_single/bugs_false_positives.B.rbedited @@ -0,0 +1,20 @@ +# typed: true +# selective-apply-code-action: refactor.extract +# enable-experimental-lsp-extract-to-variable: true +# +# This file contain cases where we should disallow the user from extracting to variable, +# but incorrectly allow it. + +/W[aeiou]rd/i +# ^ apply-code-action: [A] Extract Variable + + newVariable = A[aeiou]rd + /newVariable/m +# ^^^^^^^^^^ apply-code-action: [B] Extract Variable + +class A < T::Struct + prop :x, Integer +# ^^ apply-code-action: [C] Extract Variable + prop :y, String +# ^^^^^^ apply-code-action: [D] Extract Variable +end diff --git a/test/testdata/lsp/code_actions/extract_variable_single/bugs_false_positives.C.rbedited b/test/testdata/lsp/code_actions/extract_variable_single/bugs_false_positives.C.rbedited new file mode 100644 index 0000000000..9bf4683d3c --- /dev/null +++ b/test/testdata/lsp/code_actions/extract_variable_single/bugs_false_positives.C.rbedited @@ -0,0 +1,20 @@ +# typed: true +# selective-apply-code-action: refactor.extract +# enable-experimental-lsp-extract-to-variable: true +# +# This file contain cases where we should disallow the user from extracting to variable, +# but incorrectly allow it. + +/W[aeiou]rd/i +# ^ apply-code-action: [A] Extract Variable + + /A[aeiou]rd/m +# ^^^^^^^^^^ apply-code-action: [B] Extract Variable + +class A < T::Struct + newVariable = :x + prop newVariable, Integer +# ^^ apply-code-action: [C] Extract Variable + prop :y, String +# ^^^^^^ apply-code-action: [D] Extract Variable +end diff --git a/test/testdata/lsp/code_actions/extract_variable_single/bugs_false_positives.D.rbedited b/test/testdata/lsp/code_actions/extract_variable_single/bugs_false_positives.D.rbedited new file mode 100644 index 0000000000..501b20899e --- /dev/null +++ b/test/testdata/lsp/code_actions/extract_variable_single/bugs_false_positives.D.rbedited @@ -0,0 +1,20 @@ +# typed: true +# selective-apply-code-action: refactor.extract +# enable-experimental-lsp-extract-to-variable: true +# +# This file contain cases where we should disallow the user from extracting to variable, +# but incorrectly allow it. + +/W[aeiou]rd/i +# ^ apply-code-action: [A] Extract Variable + + /A[aeiou]rd/m +# ^^^^^^^^^^ apply-code-action: [B] Extract Variable + +class A < T::Struct + prop :x, Integer +# ^^ apply-code-action: [C] Extract Variable + newVariable = String + prop :y, newVariable +# ^^^^^^ apply-code-action: [D] Extract Variable +end diff --git a/test/testdata/lsp/code_actions/extract_variable_single/bugs_false_positives.rb b/test/testdata/lsp/code_actions/extract_variable_single/bugs_false_positives.rb new file mode 100644 index 0000000000..d050ddf996 --- /dev/null +++ b/test/testdata/lsp/code_actions/extract_variable_single/bugs_false_positives.rb @@ -0,0 +1,19 @@ +# typed: true +# selective-apply-code-action: refactor.extract +# enable-experimental-lsp-extract-to-variable: true +# +# This file contain cases where we should disallow the user from extracting to variable, +# but incorrectly allow it. + +/W[aeiou]rd/i +# ^ apply-code-action: [A] Extract Variable + + /A[aeiou]rd/m +# ^^^^^^^^^^ apply-code-action: [B] Extract Variable + +class A < T::Struct + prop :x, Integer +# ^^ apply-code-action: [C] Extract Variable + prop :y, String +# ^^^^^^ apply-code-action: [D] Extract Variable +end diff --git a/test/testdata/lsp/code_actions/extract_variable_single/class.A.rbedited b/test/testdata/lsp/code_actions/extract_variable_single/class.A.rbedited new file mode 100644 index 0000000000..d8dd65687f --- /dev/null +++ b/test/testdata/lsp/code_actions/extract_variable_single/class.A.rbedited @@ -0,0 +1,26 @@ +# typed: true +# selective-apply-code-action: refactor.extract +# enable-experimental-lsp-extract-to-variable: true + +class A; newVariable = 123; 1 + newVariable; end +# ^^^ apply-code-action: [A] Extract Variable + +class B; 1 + 1; 1 + 123; end +# ^^^ apply-code-action: [B] Extract Variable + +class C + 1 + 123 +# ^^^ apply-code-action: [C] Extract Variable +end + +class D + 1 + 1 + 1 + 123 +# ^^^ apply-code-action: [D] Extract Variable +end + +class E + 1 + 1 + 1+ 2; 1 + 123 +# ^^^ apply-code-action: [E] Extract Variable +end diff --git a/test/testdata/lsp/code_actions/extract_variable_single/class.B.rbedited b/test/testdata/lsp/code_actions/extract_variable_single/class.B.rbedited new file mode 100644 index 0000000000..d908248fdc --- /dev/null +++ b/test/testdata/lsp/code_actions/extract_variable_single/class.B.rbedited @@ -0,0 +1,26 @@ +# typed: true +# selective-apply-code-action: refactor.extract +# enable-experimental-lsp-extract-to-variable: true + +class A; 1 + 123; end +# ^^^ apply-code-action: [A] Extract Variable + +class B; 1 + 1; newVariable = 123; 1 + newVariable; end +# ^^^ apply-code-action: [B] Extract Variable + +class C + 1 + 123 +# ^^^ apply-code-action: [C] Extract Variable +end + +class D + 1 + 1 + 1 + 123 +# ^^^ apply-code-action: [D] Extract Variable +end + +class E + 1 + 1 + 1+ 2; 1 + 123 +# ^^^ apply-code-action: [E] Extract Variable +end diff --git a/test/testdata/lsp/code_actions/extract_variable_single/class.C.rbedited b/test/testdata/lsp/code_actions/extract_variable_single/class.C.rbedited new file mode 100644 index 0000000000..acd005eff9 --- /dev/null +++ b/test/testdata/lsp/code_actions/extract_variable_single/class.C.rbedited @@ -0,0 +1,27 @@ +# typed: true +# selective-apply-code-action: refactor.extract +# enable-experimental-lsp-extract-to-variable: true + +class A; 1 + 123; end +# ^^^ apply-code-action: [A] Extract Variable + +class B; 1 + 1; 1 + 123; end +# ^^^ apply-code-action: [B] Extract Variable + +class C + newVariable = 123 + 1 + newVariable +# ^^^ apply-code-action: [C] Extract Variable +end + +class D + 1 + 1 + 1 + 123 +# ^^^ apply-code-action: [D] Extract Variable +end + +class E + 1 + 1 + 1+ 2; 1 + 123 +# ^^^ apply-code-action: [E] Extract Variable +end diff --git a/test/testdata/lsp/code_actions/extract_variable_single/class.D.rbedited b/test/testdata/lsp/code_actions/extract_variable_single/class.D.rbedited new file mode 100644 index 0000000000..e984cb3eff --- /dev/null +++ b/test/testdata/lsp/code_actions/extract_variable_single/class.D.rbedited @@ -0,0 +1,27 @@ +# typed: true +# selective-apply-code-action: refactor.extract +# enable-experimental-lsp-extract-to-variable: true + +class A; 1 + 123; end +# ^^^ apply-code-action: [A] Extract Variable + +class B; 1 + 1; 1 + 123; end +# ^^^ apply-code-action: [B] Extract Variable + +class C + 1 + 123 +# ^^^ apply-code-action: [C] Extract Variable +end + +class D + 1 + 1 + newVariable = 123 + 1 + newVariable +# ^^^ apply-code-action: [D] Extract Variable +end + +class E + 1 + 1 + 1+ 2; 1 + 123 +# ^^^ apply-code-action: [E] Extract Variable +end diff --git a/test/testdata/lsp/code_actions/extract_variable_single/class.E.rbedited b/test/testdata/lsp/code_actions/extract_variable_single/class.E.rbedited new file mode 100644 index 0000000000..66458c8326 --- /dev/null +++ b/test/testdata/lsp/code_actions/extract_variable_single/class.E.rbedited @@ -0,0 +1,26 @@ +# typed: true +# selective-apply-code-action: refactor.extract +# enable-experimental-lsp-extract-to-variable: true + +class A; 1 + 123; end +# ^^^ apply-code-action: [A] Extract Variable + +class B; 1 + 1; 1 + 123; end +# ^^^ apply-code-action: [B] Extract Variable + +class C + 1 + 123 +# ^^^ apply-code-action: [C] Extract Variable +end + +class D + 1 + 1 + 1 + 123 +# ^^^ apply-code-action: [D] Extract Variable +end + +class E + 1 + 1 + 1+ 2; newVariable = 123; 1 + newVariable +# ^^^ apply-code-action: [E] Extract Variable +end diff --git a/test/testdata/lsp/code_actions/extract_variable_single/class.rb b/test/testdata/lsp/code_actions/extract_variable_single/class.rb new file mode 100644 index 0000000000..ff75ba988f --- /dev/null +++ b/test/testdata/lsp/code_actions/extract_variable_single/class.rb @@ -0,0 +1,26 @@ +# typed: true +# selective-apply-code-action: refactor.extract +# enable-experimental-lsp-extract-to-variable: true + +class A; 1 + 123; end +# ^^^ apply-code-action: [A] Extract Variable + +class B; 1 + 1; 1 + 123; end +# ^^^ apply-code-action: [B] Extract Variable + +class C + 1 + 123 +# ^^^ apply-code-action: [C] Extract Variable +end + +class D + 1 + 1 + 1 + 123 +# ^^^ apply-code-action: [D] Extract Variable +end + +class E + 1 + 1 + 1+ 2; 1 + 123 +# ^^^ apply-code-action: [E] Extract Variable +end diff --git a/test/testdata/lsp/code_actions/extract_variable_single/invalid.rb b/test/testdata/lsp/code_actions/extract_variable_single/invalid.rb new file mode 100644 index 0000000000..b6ff064fa8 --- /dev/null +++ b/test/testdata/lsp/code_actions/extract_variable_single/invalid.rb @@ -0,0 +1,135 @@ +# typed: true +# selective-apply-code-action: refactor.extract +# enable-experimental-lsp-extract-to-variable: true +# assert-no-code-action: refactor.extract +def example(x) +# ^ apply-code-action: [A] Extract Variable + xs = [] +# ^^ apply-code-action: [A] Extract Variable + xs = [] +# ^ apply-code-action: [A] Extract Variable + xs.each { |x| return x.foo if x.foo} +# ^^^^ apply-code-action: [A] Extract Variable + T.must(123) +# ^^^^ apply-code-action: [A] Extract Variable + T.must(123) +# ^^^^^^ apply-code-action: [A] Extract Variable + return +# ^^^^^^ apply-code-action: [A] Extract Variable +end + +def extract_method_parameters(x, y); end +# ^ apply-code-action: [A] Extract Variable + +def extract_method_parameters_2(x, y); end +# ^ apply-code-action: [A] Extract Variable + +# Extract block parameters + +[].each do |x, y| end +# ^ apply-code-action: [A] Extract Variable + +[].each do |x, y| end +# ^ apply-code-action: [A] Extract Variable + +class A + attr_accessor :a + + x = 1 + 1 +# ^^^ apply-code-action: [A] Extract Variable + x = 1 + 1 +# ^ apply-code-action: [A] Extract Variable + x = 1 + 1 +# ^ apply-code-action: [A] Extract Variable + + def multiple_return_break_next + 1.times do + if 1 == 2 + break 1, 2 +# ^^^^^^^^^^ apply-code-action: [A] Extract Variable + else + next 1, 2 +# ^^^^^^^^^^ apply-code-action: [A] Extract Variable + end + end + return 1, 2 +# ^^^^^^^^^^^ apply-code-action: [A] Extract Variable + end +end + + A.new.a &&= 1 +# ^^^^^^^ apply-code-action: [A] Extract Variable + + A.new.a ||= 1 +# ^^^^^^^ apply-code-action: [A] Extract Variable + + A.new.a *= 1 +# ^^^^^^^ apply-code-action: [A] Extract Variable + +a = T.let(1, T.untyped) + + a &&= 1 +# ^ apply-code-action: [A] Extract Variable + + a ||= 1 +# ^ apply-code-action: [A] Extract Variable + + a *= 1 +# ^ apply-code-action: [A] Extract Variable + +a = T.unsafe(nil) || T.unsafe(nil) +# ^^^^ apply-code-action: [A] Extract Variable + +a = T.unsafe(nil) && T.unsafe(nil) +# ^^^^ apply-code-action: [A] Extract Variable + + A.new.a&.foo &&= 1 +# ^^^^^^^^^^^^ apply-code-action: [A] Extract Variable + + A.new.a&.foo ||= 1 +# ^^^^^^^^^^^^ apply-code-action: [A] Extract Variable + + A.new.a&.foo *= 1 +# ^^^^^^^^^^^^ apply-code-action: [A] Extract Variable + +[1, 2, 3].each do |x| +# ^^^^ apply-code-action: [A] Extract Variable +end +[1, 2, 3].each do |x| +# ^ apply-code-action: [A] Extract Variable + break +# ^^^^^ apply-code-action: [A] Extract Variable + next +# ^^^^ apply-code-action: [A] Extract Variable +end + +begin +rescue Errno::ENOENT +# ^^^^^^^^^^^^^ apply-code-action: [A] Extract Variable +rescue ArgumentError => e +# ^^^^^^^^^^^^^^^^^^ apply-code-action: [A] Extract Variable +rescue Exception => e +# ^ apply-code-action: [A] Extract Variable +end + +class B < T::Struct + const :baz, String +# ^^^^^ apply-code-action: [A] Extract Variable +end + +def endless_method = 1 + 123 +# ^^^ apply-code-action: [A] Extract Variable + +# The actual issue we saw causing crashes was the following, the selection is the entire method body: +# def foo +# 1 + 1 +# 2 + 2 +# end +# However, our tests don't support multi-line assertions, so this test case is approximating that +# by joining the lines with a ; (the parse result will be the same for both cases). +# TODO(neil): add a multiline test case here once we have multiline assertions. + +def multi_stat_selection + 1 + 1; 2 + 2 +# ^^^^^^^^^^^^ apply-code-action: [A] Extract Variable +end diff --git a/test/testdata/lsp/code_actions/extract_variable_single/lsp_flag.rb b/test/testdata/lsp/code_actions/extract_variable_single/lsp_flag.rb new file mode 100644 index 0000000000..98dd756c53 --- /dev/null +++ b/test/testdata/lsp/code_actions/extract_variable_single/lsp_flag.rb @@ -0,0 +1,9 @@ +# typed: true +# selective-apply-code-action: refactor.extract +# assert-no-code-action: refactor.extract +# Tests that the code action is not shown when the flag is not enabled. + +def example(x) + xs = [] +# ^^ apply-code-action: [A] Extract Variable +end diff --git a/test/testdata/lsp/code_actions/extract_variable_single/method.A.rbedited b/test/testdata/lsp/code_actions/extract_variable_single/method.A.rbedited new file mode 100644 index 0000000000..6ea9521fb5 --- /dev/null +++ b/test/testdata/lsp/code_actions/extract_variable_single/method.A.rbedited @@ -0,0 +1,26 @@ +# typed: true +# selective-apply-code-action: refactor.extract +# enable-experimental-lsp-extract-to-variable: true + +def b; newVariable = 123; 1 + newVariable; end +# ^^^ apply-code-action: [A] Extract Variable + +def c; 1 + 1; 1 + 123; end +# ^^^ apply-code-action: [B] Extract Variable + +def d + 1 + 123 +# ^^^ apply-code-action: [C] Extract Variable +end + +def e + 1 + 1 + 1 + 123 +# ^^^ apply-code-action: [D] Extract Variable +end + +def f + 1 + 1 + 1 + 2; 1 + 123 +# ^^^ apply-code-action: [E] Extract Variable +end diff --git a/test/testdata/lsp/code_actions/extract_variable_single/method.B.rbedited b/test/testdata/lsp/code_actions/extract_variable_single/method.B.rbedited new file mode 100644 index 0000000000..6d17473600 --- /dev/null +++ b/test/testdata/lsp/code_actions/extract_variable_single/method.B.rbedited @@ -0,0 +1,26 @@ +# typed: true +# selective-apply-code-action: refactor.extract +# enable-experimental-lsp-extract-to-variable: true + +def b; 1 + 123; end +# ^^^ apply-code-action: [A] Extract Variable + +def c; 1 + 1; newVariable = 123; 1 + newVariable; end +# ^^^ apply-code-action: [B] Extract Variable + +def d + 1 + 123 +# ^^^ apply-code-action: [C] Extract Variable +end + +def e + 1 + 1 + 1 + 123 +# ^^^ apply-code-action: [D] Extract Variable +end + +def f + 1 + 1 + 1 + 2; 1 + 123 +# ^^^ apply-code-action: [E] Extract Variable +end diff --git a/test/testdata/lsp/code_actions/extract_variable_single/method.C.rbedited b/test/testdata/lsp/code_actions/extract_variable_single/method.C.rbedited new file mode 100644 index 0000000000..cb7331630f --- /dev/null +++ b/test/testdata/lsp/code_actions/extract_variable_single/method.C.rbedited @@ -0,0 +1,27 @@ +# typed: true +# selective-apply-code-action: refactor.extract +# enable-experimental-lsp-extract-to-variable: true + +def b; 1 + 123; end +# ^^^ apply-code-action: [A] Extract Variable + +def c; 1 + 1; 1 + 123; end +# ^^^ apply-code-action: [B] Extract Variable + +def d + newVariable = 123 + 1 + newVariable +# ^^^ apply-code-action: [C] Extract Variable +end + +def e + 1 + 1 + 1 + 123 +# ^^^ apply-code-action: [D] Extract Variable +end + +def f + 1 + 1 + 1 + 2; 1 + 123 +# ^^^ apply-code-action: [E] Extract Variable +end diff --git a/test/testdata/lsp/code_actions/extract_variable_single/method.D.rbedited b/test/testdata/lsp/code_actions/extract_variable_single/method.D.rbedited new file mode 100644 index 0000000000..334a5a6f20 --- /dev/null +++ b/test/testdata/lsp/code_actions/extract_variable_single/method.D.rbedited @@ -0,0 +1,27 @@ +# typed: true +# selective-apply-code-action: refactor.extract +# enable-experimental-lsp-extract-to-variable: true + +def b; 1 + 123; end +# ^^^ apply-code-action: [A] Extract Variable + +def c; 1 + 1; 1 + 123; end +# ^^^ apply-code-action: [B] Extract Variable + +def d + 1 + 123 +# ^^^ apply-code-action: [C] Extract Variable +end + +def e + 1 + 1 + newVariable = 123 + 1 + newVariable +# ^^^ apply-code-action: [D] Extract Variable +end + +def f + 1 + 1 + 1 + 2; 1 + 123 +# ^^^ apply-code-action: [E] Extract Variable +end diff --git a/test/testdata/lsp/code_actions/extract_variable_single/method.E.rbedited b/test/testdata/lsp/code_actions/extract_variable_single/method.E.rbedited new file mode 100644 index 0000000000..688cbfce31 --- /dev/null +++ b/test/testdata/lsp/code_actions/extract_variable_single/method.E.rbedited @@ -0,0 +1,26 @@ +# typed: true +# selective-apply-code-action: refactor.extract +# enable-experimental-lsp-extract-to-variable: true + +def b; 1 + 123; end +# ^^^ apply-code-action: [A] Extract Variable + +def c; 1 + 1; 1 + 123; end +# ^^^ apply-code-action: [B] Extract Variable + +def d + 1 + 123 +# ^^^ apply-code-action: [C] Extract Variable +end + +def e + 1 + 1 + 1 + 123 +# ^^^ apply-code-action: [D] Extract Variable +end + +def f + 1 + 1 + 1 + 2; newVariable = 123; 1 + newVariable +# ^^^ apply-code-action: [E] Extract Variable +end diff --git a/test/testdata/lsp/code_actions/extract_variable_single/method.rb b/test/testdata/lsp/code_actions/extract_variable_single/method.rb new file mode 100644 index 0000000000..a3f9de1934 --- /dev/null +++ b/test/testdata/lsp/code_actions/extract_variable_single/method.rb @@ -0,0 +1,26 @@ +# typed: true +# selective-apply-code-action: refactor.extract +# enable-experimental-lsp-extract-to-variable: true + +def b; 1 + 123; end +# ^^^ apply-code-action: [A] Extract Variable + +def c; 1 + 1; 1 + 123; end +# ^^^ apply-code-action: [B] Extract Variable + +def d + 1 + 123 +# ^^^ apply-code-action: [C] Extract Variable +end + +def e + 1 + 1 + 1 + 123 +# ^^^ apply-code-action: [D] Extract Variable +end + +def f + 1 + 1 + 1 + 2; 1 + 123 +# ^^^ apply-code-action: [E] Extract Variable +end diff --git a/test/testdata/lsp/code_actions/extract_variable_single/other.A.rbedited b/test/testdata/lsp/code_actions/extract_variable_single/other.A.rbedited new file mode 100644 index 0000000000..2cb3cc8a68 --- /dev/null +++ b/test/testdata/lsp/code_actions/extract_variable_single/other.A.rbedited @@ -0,0 +1,31 @@ +# typed: true +# selective-apply-code-action: refactor.extract +# enable-experimental-lsp-extract-to-variable: true + +if 1 == 2 + newVariable = 5 + 1 + newVariable +# ^ apply-code-action: [A] Extract Variable +else + 6 + 3 +# ^ apply-code-action: [B] Extract Variable +end + +y = 2 +a = (x = 1; x + y) +# ^ apply-code-action: [C] Extract Variable + +while 1 == 2 + x = a + y +# ^^^^^ apply-code-action: [D] Extract Variable +end + +for i in 1..7 + puts i + 1 +# ^^^^^ apply-code-action: [E] Extract Variable +end + +unless 1 == 2 + puts 2 + 3 +# ^^^^^ apply-code-action: [F] Extract Variable +end diff --git a/test/testdata/lsp/code_actions/extract_variable_single/other.B.rbedited b/test/testdata/lsp/code_actions/extract_variable_single/other.B.rbedited new file mode 100644 index 0000000000..d89a3a927b --- /dev/null +++ b/test/testdata/lsp/code_actions/extract_variable_single/other.B.rbedited @@ -0,0 +1,31 @@ +# typed: true +# selective-apply-code-action: refactor.extract +# enable-experimental-lsp-extract-to-variable: true + +if 1 == 2 + 1 + 5 +# ^ apply-code-action: [A] Extract Variable +else + newVariable = 6 + newVariable + 3 +# ^ apply-code-action: [B] Extract Variable +end + +y = 2 +a = (x = 1; x + y) +# ^ apply-code-action: [C] Extract Variable + +while 1 == 2 + x = a + y +# ^^^^^ apply-code-action: [D] Extract Variable +end + +for i in 1..7 + puts i + 1 +# ^^^^^ apply-code-action: [E] Extract Variable +end + +unless 1 == 2 + puts 2 + 3 +# ^^^^^ apply-code-action: [F] Extract Variable +end diff --git a/test/testdata/lsp/code_actions/extract_variable_single/other.C.rbedited b/test/testdata/lsp/code_actions/extract_variable_single/other.C.rbedited new file mode 100644 index 0000000000..c4879e59b1 --- /dev/null +++ b/test/testdata/lsp/code_actions/extract_variable_single/other.C.rbedited @@ -0,0 +1,30 @@ +# typed: true +# selective-apply-code-action: refactor.extract +# enable-experimental-lsp-extract-to-variable: true + +if 1 == 2 + 1 + 5 +# ^ apply-code-action: [A] Extract Variable +else + 6 + 3 +# ^ apply-code-action: [B] Extract Variable +end + +y = 2 +a = (x = 1; newVariable = x; newVariable + y) +# ^ apply-code-action: [C] Extract Variable + +while 1 == 2 + x = a + y +# ^^^^^ apply-code-action: [D] Extract Variable +end + +for i in 1..7 + puts i + 1 +# ^^^^^ apply-code-action: [E] Extract Variable +end + +unless 1 == 2 + puts 2 + 3 +# ^^^^^ apply-code-action: [F] Extract Variable +end diff --git a/test/testdata/lsp/code_actions/extract_variable_single/other.D.rbedited b/test/testdata/lsp/code_actions/extract_variable_single/other.D.rbedited new file mode 100644 index 0000000000..0304907d3b --- /dev/null +++ b/test/testdata/lsp/code_actions/extract_variable_single/other.D.rbedited @@ -0,0 +1,31 @@ +# typed: true +# selective-apply-code-action: refactor.extract +# enable-experimental-lsp-extract-to-variable: true + +if 1 == 2 + 1 + 5 +# ^ apply-code-action: [A] Extract Variable +else + 6 + 3 +# ^ apply-code-action: [B] Extract Variable +end + +y = 2 +a = (x = 1; x + y) +# ^ apply-code-action: [C] Extract Variable + +while 1 == 2 + newVariable = a + y + x = newVariable +# ^^^^^ apply-code-action: [D] Extract Variable +end + +for i in 1..7 + puts i + 1 +# ^^^^^ apply-code-action: [E] Extract Variable +end + +unless 1 == 2 + puts 2 + 3 +# ^^^^^ apply-code-action: [F] Extract Variable +end diff --git a/test/testdata/lsp/code_actions/extract_variable_single/other.E.rbedited b/test/testdata/lsp/code_actions/extract_variable_single/other.E.rbedited new file mode 100644 index 0000000000..4d9c6ef8f2 --- /dev/null +++ b/test/testdata/lsp/code_actions/extract_variable_single/other.E.rbedited @@ -0,0 +1,31 @@ +# typed: true +# selective-apply-code-action: refactor.extract +# enable-experimental-lsp-extract-to-variable: true + +if 1 == 2 + 1 + 5 +# ^ apply-code-action: [A] Extract Variable +else + 6 + 3 +# ^ apply-code-action: [B] Extract Variable +end + +y = 2 +a = (x = 1; x + y) +# ^ apply-code-action: [C] Extract Variable + +while 1 == 2 + x = a + y +# ^^^^^ apply-code-action: [D] Extract Variable +end + +for i in 1..7 + newVariable = i + 1 + puts newVariable +# ^^^^^ apply-code-action: [E] Extract Variable +end + +unless 1 == 2 + puts 2 + 3 +# ^^^^^ apply-code-action: [F] Extract Variable +end diff --git a/test/testdata/lsp/code_actions/extract_variable_single/other.F.rbedited b/test/testdata/lsp/code_actions/extract_variable_single/other.F.rbedited new file mode 100644 index 0000000000..f62b7b9c87 --- /dev/null +++ b/test/testdata/lsp/code_actions/extract_variable_single/other.F.rbedited @@ -0,0 +1,31 @@ +# typed: true +# selective-apply-code-action: refactor.extract +# enable-experimental-lsp-extract-to-variable: true + +if 1 == 2 + 1 + 5 +# ^ apply-code-action: [A] Extract Variable +else + 6 + 3 +# ^ apply-code-action: [B] Extract Variable +end + +y = 2 +a = (x = 1; x + y) +# ^ apply-code-action: [C] Extract Variable + +while 1 == 2 + x = a + y +# ^^^^^ apply-code-action: [D] Extract Variable +end + +for i in 1..7 + puts i + 1 +# ^^^^^ apply-code-action: [E] Extract Variable +end + +unless 1 == 2 + newVariable = 2 + 3 + puts newVariable +# ^^^^^ apply-code-action: [F] Extract Variable +end diff --git a/test/testdata/lsp/code_actions/extract_variable_single/other.rb b/test/testdata/lsp/code_actions/extract_variable_single/other.rb new file mode 100644 index 0000000000..f1567b0066 --- /dev/null +++ b/test/testdata/lsp/code_actions/extract_variable_single/other.rb @@ -0,0 +1,30 @@ +# typed: true +# selective-apply-code-action: refactor.extract +# enable-experimental-lsp-extract-to-variable: true + +if 1 == 2 + 1 + 5 +# ^ apply-code-action: [A] Extract Variable +else + 6 + 3 +# ^ apply-code-action: [B] Extract Variable +end + +y = 2 +a = (x = 1; x + y) +# ^ apply-code-action: [C] Extract Variable + +while 1 == 2 + x = a + y +# ^^^^^ apply-code-action: [D] Extract Variable +end + +for i in 1..7 + puts i + 1 +# ^^^^^ apply-code-action: [E] Extract Variable +end + +unless 1 == 2 + puts 2 + 3 +# ^^^^^ apply-code-action: [F] Extract Variable +end diff --git a/test/testdata/lsp/code_actions/extract_variable_single/rescue.A.rbedited b/test/testdata/lsp/code_actions/extract_variable_single/rescue.A.rbedited new file mode 100644 index 0000000000..d7d9bcfb01 --- /dev/null +++ b/test/testdata/lsp/code_actions/extract_variable_single/rescue.A.rbedited @@ -0,0 +1,27 @@ +# typed: true +# selective-apply-code-action: refactor.extract +# enable-experimental-lsp-extract-to-variable: true + +begin + newVariable = 11 + 1 + newVariable +# ^^ apply-code-action: [A] Extract Variable +rescue Errno::ENOENT + 2 + 22 +# ^^ apply-code-action: [B] Extract Variable +rescue ArgumentError + 3 + 33 +# ^^ apply-code-action: [C] Extract Variable +else + 4 + 44 +# ^^ apply-code-action: [D] Extract Variable +ensure + 5 + 55 +# ^^ apply-code-action: [E] Extract Variable +end + +begin +ensure + 6 + 66 +# ^^ apply-code-action: [F] Extract Variable +end diff --git a/test/testdata/lsp/code_actions/extract_variable_single/rescue.B.rbedited b/test/testdata/lsp/code_actions/extract_variable_single/rescue.B.rbedited new file mode 100644 index 0000000000..a9d0b49a29 --- /dev/null +++ b/test/testdata/lsp/code_actions/extract_variable_single/rescue.B.rbedited @@ -0,0 +1,27 @@ +# typed: true +# selective-apply-code-action: refactor.extract +# enable-experimental-lsp-extract-to-variable: true + +begin + 1 + 11 +# ^^ apply-code-action: [A] Extract Variable +rescue Errno::ENOENT + newVariable = 22 + 2 + newVariable +# ^^ apply-code-action: [B] Extract Variable +rescue ArgumentError + 3 + 33 +# ^^ apply-code-action: [C] Extract Variable +else + 4 + 44 +# ^^ apply-code-action: [D] Extract Variable +ensure + 5 + 55 +# ^^ apply-code-action: [E] Extract Variable +end + +begin +ensure + 6 + 66 +# ^^ apply-code-action: [F] Extract Variable +end diff --git a/test/testdata/lsp/code_actions/extract_variable_single/rescue.C.rbedited b/test/testdata/lsp/code_actions/extract_variable_single/rescue.C.rbedited new file mode 100644 index 0000000000..d484e61210 --- /dev/null +++ b/test/testdata/lsp/code_actions/extract_variable_single/rescue.C.rbedited @@ -0,0 +1,27 @@ +# typed: true +# selective-apply-code-action: refactor.extract +# enable-experimental-lsp-extract-to-variable: true + +begin + 1 + 11 +# ^^ apply-code-action: [A] Extract Variable +rescue Errno::ENOENT + 2 + 22 +# ^^ apply-code-action: [B] Extract Variable +rescue ArgumentError + newVariable = 33 + 3 + newVariable +# ^^ apply-code-action: [C] Extract Variable +else + 4 + 44 +# ^^ apply-code-action: [D] Extract Variable +ensure + 5 + 55 +# ^^ apply-code-action: [E] Extract Variable +end + +begin +ensure + 6 + 66 +# ^^ apply-code-action: [F] Extract Variable +end diff --git a/test/testdata/lsp/code_actions/extract_variable_single/rescue.D.rbedited b/test/testdata/lsp/code_actions/extract_variable_single/rescue.D.rbedited new file mode 100644 index 0000000000..cdf6eb580e --- /dev/null +++ b/test/testdata/lsp/code_actions/extract_variable_single/rescue.D.rbedited @@ -0,0 +1,27 @@ +# typed: true +# selective-apply-code-action: refactor.extract +# enable-experimental-lsp-extract-to-variable: true + +begin + 1 + 11 +# ^^ apply-code-action: [A] Extract Variable +rescue Errno::ENOENT + 2 + 22 +# ^^ apply-code-action: [B] Extract Variable +rescue ArgumentError + 3 + 33 +# ^^ apply-code-action: [C] Extract Variable +else + newVariable = 44 + 4 + newVariable +# ^^ apply-code-action: [D] Extract Variable +ensure + 5 + 55 +# ^^ apply-code-action: [E] Extract Variable +end + +begin +ensure + 6 + 66 +# ^^ apply-code-action: [F] Extract Variable +end diff --git a/test/testdata/lsp/code_actions/extract_variable_single/rescue.E.rbedited b/test/testdata/lsp/code_actions/extract_variable_single/rescue.E.rbedited new file mode 100644 index 0000000000..49168e91c4 --- /dev/null +++ b/test/testdata/lsp/code_actions/extract_variable_single/rescue.E.rbedited @@ -0,0 +1,27 @@ +# typed: true +# selective-apply-code-action: refactor.extract +# enable-experimental-lsp-extract-to-variable: true + +begin + 1 + 11 +# ^^ apply-code-action: [A] Extract Variable +rescue Errno::ENOENT + 2 + 22 +# ^^ apply-code-action: [B] Extract Variable +rescue ArgumentError + 3 + 33 +# ^^ apply-code-action: [C] Extract Variable +else + 4 + 44 +# ^^ apply-code-action: [D] Extract Variable +ensure + newVariable = 55 + 5 + newVariable +# ^^ apply-code-action: [E] Extract Variable +end + +begin +ensure + 6 + 66 +# ^^ apply-code-action: [F] Extract Variable +end diff --git a/test/testdata/lsp/code_actions/extract_variable_single/rescue.F.rbedited b/test/testdata/lsp/code_actions/extract_variable_single/rescue.F.rbedited new file mode 100644 index 0000000000..6a79cb73b9 --- /dev/null +++ b/test/testdata/lsp/code_actions/extract_variable_single/rescue.F.rbedited @@ -0,0 +1,27 @@ +# typed: true +# selective-apply-code-action: refactor.extract +# enable-experimental-lsp-extract-to-variable: true + +begin + 1 + 11 +# ^^ apply-code-action: [A] Extract Variable +rescue Errno::ENOENT + 2 + 22 +# ^^ apply-code-action: [B] Extract Variable +rescue ArgumentError + 3 + 33 +# ^^ apply-code-action: [C] Extract Variable +else + 4 + 44 +# ^^ apply-code-action: [D] Extract Variable +ensure + 5 + 55 +# ^^ apply-code-action: [E] Extract Variable +end + +begin +ensure + newVariable = 66 + 6 + newVariable +# ^^ apply-code-action: [F] Extract Variable +end diff --git a/test/testdata/lsp/code_actions/extract_variable_single/rescue.rb b/test/testdata/lsp/code_actions/extract_variable_single/rescue.rb new file mode 100644 index 0000000000..ee13459400 --- /dev/null +++ b/test/testdata/lsp/code_actions/extract_variable_single/rescue.rb @@ -0,0 +1,26 @@ +# typed: true +# selective-apply-code-action: refactor.extract +# enable-experimental-lsp-extract-to-variable: true + +begin + 1 + 11 +# ^^ apply-code-action: [A] Extract Variable +rescue Errno::ENOENT + 2 + 22 +# ^^ apply-code-action: [B] Extract Variable +rescue ArgumentError + 3 + 33 +# ^^ apply-code-action: [C] Extract Variable +else + 4 + 44 +# ^^ apply-code-action: [D] Extract Variable +ensure + 5 + 55 +# ^^ apply-code-action: [E] Extract Variable +end + +begin +ensure + 6 + 66 +# ^^ apply-code-action: [F] Extract Variable +end diff --git a/test/testdata/lsp/code_actions/move_method/call_location.A.rbedited b/test/testdata/lsp/code_actions/move_method/call_location.A.rbedited index 671471c0f8..f4b08a6a92 100644 --- a/test/testdata/lsp/code_actions/move_method/call_location.A.rbedited +++ b/test/testdata/lsp/code_actions/move_method/call_location.A.rbedited @@ -5,7 +5,7 @@ module AModule extend T::Sig sig {void} def self.a -# ^ apply-code-action: [A] Move method to a new module +# | apply-code-action: [A] Move method to a new module end end @@ -15,16 +15,16 @@ module Foo sig {void} def self.b - # ^ apply-code-action: [B] Move method to a new module + # | apply-code-action: [B] Move method to a new module end sig {void} def self.c - # ^ apply-code-action: [C] Move method to a new module + # | apply-code-action: [C] Move method to a new module end sig {void} def self.d - # ^ apply-code-action: [D] Move method to a new module + # | apply-code-action: [D] Move method to a new module end end diff --git a/test/testdata/lsp/code_actions/move_method/call_location.B.rbedited b/test/testdata/lsp/code_actions/move_method/call_location.B.rbedited index c06383cfbe..8673ef5db6 100644 --- a/test/testdata/lsp/code_actions/move_method/call_location.B.rbedited +++ b/test/testdata/lsp/code_actions/move_method/call_location.B.rbedited @@ -5,7 +5,7 @@ module BModule extend T::Sig sig {void} def self.b - # ^ apply-code-action: [B] Move method to a new module + # | apply-code-action: [B] Move method to a new module end end @@ -14,17 +14,17 @@ module Foo sig {void} def self.a -# ^ apply-code-action: [A] Move method to a new module +# | apply-code-action: [A] Move method to a new module end sig {void} def self.c - # ^ apply-code-action: [C] Move method to a new module + # | apply-code-action: [C] Move method to a new module end sig {void} def self.d - # ^ apply-code-action: [D] Move method to a new module + # | apply-code-action: [D] Move method to a new module end end diff --git a/test/testdata/lsp/code_actions/move_method/call_location.C.rbedited b/test/testdata/lsp/code_actions/move_method/call_location.C.rbedited index 952f292df0..1f23f578bc 100644 --- a/test/testdata/lsp/code_actions/move_method/call_location.C.rbedited +++ b/test/testdata/lsp/code_actions/move_method/call_location.C.rbedited @@ -5,7 +5,7 @@ module CModule extend T::Sig sig {void} def self.c - # ^ apply-code-action: [C] Move method to a new module + # | apply-code-action: [C] Move method to a new module end end @@ -14,17 +14,17 @@ module Foo sig {void} def self.a -# ^ apply-code-action: [A] Move method to a new module +# | apply-code-action: [A] Move method to a new module end sig {void} def self.b - # ^ apply-code-action: [B] Move method to a new module + # | apply-code-action: [B] Move method to a new module end sig {void} def self.d - # ^ apply-code-action: [D] Move method to a new module + # | apply-code-action: [D] Move method to a new module end end diff --git a/test/testdata/lsp/code_actions/move_method/call_location.D.rbedited b/test/testdata/lsp/code_actions/move_method/call_location.D.rbedited index c4e08ea371..2bc6599fe6 100644 --- a/test/testdata/lsp/code_actions/move_method/call_location.D.rbedited +++ b/test/testdata/lsp/code_actions/move_method/call_location.D.rbedited @@ -5,7 +5,7 @@ module DModule extend T::Sig sig {void} def self.d - # ^ apply-code-action: [D] Move method to a new module + # | apply-code-action: [D] Move method to a new module end end @@ -14,17 +14,17 @@ module Foo sig {void} def self.a -# ^ apply-code-action: [A] Move method to a new module +# | apply-code-action: [A] Move method to a new module end sig {void} def self.b - # ^ apply-code-action: [B] Move method to a new module + # | apply-code-action: [B] Move method to a new module end sig {void} def self.c - # ^ apply-code-action: [C] Move method to a new module + # | apply-code-action: [C] Move method to a new module end end diff --git a/test/testdata/lsp/code_actions/move_method/call_location.rb b/test/testdata/lsp/code_actions/move_method/call_location.rb index 9e9431087d..34550f85c0 100644 --- a/test/testdata/lsp/code_actions/move_method/call_location.rb +++ b/test/testdata/lsp/code_actions/move_method/call_location.rb @@ -6,21 +6,21 @@ module Foo sig {void} def self.a -# ^ apply-code-action: [A] Move method to a new module +# | apply-code-action: [A] Move method to a new module end sig {void} def self.b - # ^ apply-code-action: [B] Move method to a new module + # | apply-code-action: [B] Move method to a new module end sig {void} def self.c - # ^ apply-code-action: [C] Move method to a new module + # | apply-code-action: [C] Move method to a new module end sig {void} def self.d - # ^ apply-code-action: [D] Move method to a new module + # | apply-code-action: [D] Move method to a new module end end diff --git a/test/testdata/lsp/code_actions/move_method/class_func.A.rbedited b/test/testdata/lsp/code_actions/move_method/class_func.A.rbedited index 67c50c2f2c..fe283be0da 100644 --- a/test/testdata/lsp/code_actions/move_method/class_func.A.rbedited +++ b/test/testdata/lsp/code_actions/move_method/class_func.A.rbedited @@ -5,7 +5,7 @@ module GreetingModule extend T::Sig sig {returns(String)} def self.greeting - # ^^^^^^^^ apply-code-action: [A] Move method to a new module + # | apply-code-action: [A] Move method to a new module 'Hello' end end @@ -33,7 +33,9 @@ module A GreetingModule.greeting GreetingModule.greeting - print((GreetingModule).greeting) + print(GreetingModule.greeting) + print(GreetingModule.greeting) + print((if T.unsafe(true); Foo; else Integer; end).greeting) # error: does not exist end sig {params(m: T.class_of(Foo)).void} diff --git a/test/testdata/lsp/code_actions/move_method/class_func.rb b/test/testdata/lsp/code_actions/move_method/class_func.rb index 4b4df654d3..8d162792a5 100644 --- a/test/testdata/lsp/code_actions/move_method/class_func.rb +++ b/test/testdata/lsp/code_actions/move_method/class_func.rb @@ -6,7 +6,7 @@ module Foo sig {returns(String)} def self.greeting - # ^^^^^^^^ apply-code-action: [A] Move method to a new module + # | apply-code-action: [A] Move method to a new module 'Hello' end @@ -30,6 +30,8 @@ def bar m.greeting print((Foo if true).greeting) + print((if T.unsafe(true); Foo; else Foo; end).greeting) + print((if T.unsafe(true); Foo; else Integer; end).greeting) # error: does not exist end sig {params(m: T.class_of(Foo)).void} diff --git a/test/testdata/lsp/code_actions/move_method/comments.A.rbedited b/test/testdata/lsp/code_actions/move_method/comments.A.rbedited index 7acc70c56d..2d6e0d7daa 100644 --- a/test/testdata/lsp/code_actions/move_method/comments.A.rbedited +++ b/test/testdata/lsp/code_actions/move_method/comments.A.rbedited @@ -13,13 +13,13 @@ end class Foo extend T::Sig - # ^ apply-code-action: [A] Move method to a new module + # | apply-code-action: [A] Move method to a new module sig do void end # comment after sig should move with the method def self.baz; end -# ^ apply-code-action: [B] Move method to a new module +# | apply-code-action: [B] Move method to a new module sig do void @@ -27,7 +27,7 @@ end def self.qux -# ^ apply-code-action: [C] Move method to a new module +# | apply-code-action: [C] Move method to a new module end end diff --git a/test/testdata/lsp/code_actions/move_method/comments.B.rbedited b/test/testdata/lsp/code_actions/move_method/comments.B.rbedited index cf2baec0b2..4caf253edb 100644 --- a/test/testdata/lsp/code_actions/move_method/comments.B.rbedited +++ b/test/testdata/lsp/code_actions/move_method/comments.B.rbedited @@ -17,9 +17,9 @@ class Foo sig {void} # comment between sig and def should move with the method def self.bar; end - # ^ apply-code-action: [A] Move method to a new module + # | apply-code-action: [A] Move method to a new module -# ^ apply-code-action: [B] Move method to a new module +# | apply-code-action: [B] Move method to a new module sig do void @@ -27,7 +27,7 @@ end def self.qux -# ^ apply-code-action: [C] Move method to a new module +# | apply-code-action: [C] Move method to a new module end end diff --git a/test/testdata/lsp/code_actions/move_method/comments.C.rbedited b/test/testdata/lsp/code_actions/move_method/comments.C.rbedited index 8cc860f902..572780ad07 100644 --- a/test/testdata/lsp/code_actions/move_method/comments.C.rbedited +++ b/test/testdata/lsp/code_actions/move_method/comments.C.rbedited @@ -11,7 +11,7 @@ end def self.qux -# ^ apply-code-action: [C] Move method to a new module +# | apply-code-action: [C] Move method to a new module end end @@ -21,13 +21,13 @@ class Foo sig {void} # comment between sig and def should move with the method def self.bar; end - # ^ apply-code-action: [A] Move method to a new module + # | apply-code-action: [A] Move method to a new module sig do void end # comment after sig should move with the method def self.baz; end -# ^ apply-code-action: [B] Move method to a new module +# | apply-code-action: [B] Move method to a new module end diff --git a/test/testdata/lsp/code_actions/move_method/comments.D.rbedited b/test/testdata/lsp/code_actions/move_method/comments.D.rbedited index 58bf03ae70..6be2ae2934 100644 --- a/test/testdata/lsp/code_actions/move_method/comments.D.rbedited +++ b/test/testdata/lsp/code_actions/move_method/comments.D.rbedited @@ -11,7 +11,7 @@ module QuuxModule end # This comment has leading whitespaces, but should also be moved def self.quux -# ^ apply-code-action: [D] Move method to a new module +# | apply-code-action: [D] Move method to a new module end end @@ -21,13 +21,13 @@ class Foo sig {void} # comment between sig and def should move with the method def self.bar; end - # ^ apply-code-action: [A] Move method to a new module + # | apply-code-action: [A] Move method to a new module sig do void end # comment after sig should move with the method def self.baz; end -# ^ apply-code-action: [B] Move method to a new module +# | apply-code-action: [B] Move method to a new module sig do void diff --git a/test/testdata/lsp/code_actions/move_method/comments.rb b/test/testdata/lsp/code_actions/move_method/comments.rb index c268727b9a..ccb22df8ec 100644 --- a/test/testdata/lsp/code_actions/move_method/comments.rb +++ b/test/testdata/lsp/code_actions/move_method/comments.rb @@ -9,13 +9,13 @@ class Foo sig {void} # comment between sig and def should move with the method def self.bar; end - # ^ apply-code-action: [A] Move method to a new module + # | apply-code-action: [A] Move method to a new module sig do void end # comment after sig should move with the method def self.baz; end -# ^ apply-code-action: [B] Move method to a new module +# | apply-code-action: [B] Move method to a new module sig do void @@ -23,7 +23,7 @@ def self.baz; end def self.qux -# ^ apply-code-action: [C] Move method to a new module +# | apply-code-action: [C] Move method to a new module end end diff --git a/test/testdata/lsp/code_actions/move_method/dsl_required.A.rbedited b/test/testdata/lsp/code_actions/move_method/dsl_required.A.rbedited index 2847201028..6a83214be1 100644 --- a/test/testdata/lsp/code_actions/move_method/dsl_required.A.rbedited +++ b/test/testdata/lsp/code_actions/move_method/dsl_required.A.rbedited @@ -6,5 +6,5 @@ class TestDSLBuilder dsl_required :example1, String - # ^ apply-code-action: [A] Move method to a new module + # | apply-code-action: [A] Move method to a new module end diff --git a/test/testdata/lsp/code_actions/move_method/dsl_required.rb b/test/testdata/lsp/code_actions/move_method/dsl_required.rb index 2847201028..6a83214be1 100644 --- a/test/testdata/lsp/code_actions/move_method/dsl_required.rb +++ b/test/testdata/lsp/code_actions/move_method/dsl_required.rb @@ -6,5 +6,5 @@ class TestDSLBuilder dsl_required :example1, String - # ^ apply-code-action: [A] Move method to a new module + # | apply-code-action: [A] Move method to a new module end diff --git a/test/testdata/lsp/code_actions/move_method/either.A.rbedited b/test/testdata/lsp/code_actions/move_method/either.A.rbedited index 51e6353549..93a6ab4d3d 100644 --- a/test/testdata/lsp/code_actions/move_method/either.A.rbedited +++ b/test/testdata/lsp/code_actions/move_method/either.A.rbedited @@ -12,7 +12,7 @@ module Either class Left extend T::Sig - # ^ apply-code-action: [A] Move method to a new module + # | apply-code-action: [A] Move method to a new module end class Right diff --git a/test/testdata/lsp/code_actions/move_method/either.rb b/test/testdata/lsp/code_actions/move_method/either.rb index cbb9ecc75b..e6e1411324 100644 --- a/test/testdata/lsp/code_actions/move_method/either.rb +++ b/test/testdata/lsp/code_actions/move_method/either.rb @@ -8,7 +8,7 @@ class Left extend T::Sig sig {void} def self.foo; end - # ^ apply-code-action: [A] Move method to a new module + # | apply-code-action: [A] Move method to a new module end class Right diff --git a/test/testdata/lsp/code_actions/move_method/func_naming.A.rbedited b/test/testdata/lsp/code_actions/move_method/func_naming.A.rbedited index 5e85c66684..42dd6c6e4a 100644 --- a/test/testdata/lsp/code_actions/move_method/func_naming.A.rbedited +++ b/test/testdata/lsp/code_actions/move_method/func_naming.A.rbedited @@ -10,7 +10,7 @@ module GreetingModule1 extend T::Sig sig {returns(String)} def self._greeting_ - # ^^^^^^^^^^ apply-code-action: [A] Move method to a new module + # | apply-code-action: [A] Move method to a new module 'Hello' end end diff --git a/test/testdata/lsp/code_actions/move_method/func_naming.rb b/test/testdata/lsp/code_actions/move_method/func_naming.rb index 792efd4dbf..07374b236c 100644 --- a/test/testdata/lsp/code_actions/move_method/func_naming.rb +++ b/test/testdata/lsp/code_actions/move_method/func_naming.rb @@ -12,7 +12,7 @@ module Foo sig {returns(String)} def self._greeting_ - # ^^^^^^^^^^ apply-code-action: [A] Move method to a new module + # | apply-code-action: [A] Move method to a new module 'Hello' end end diff --git a/test/testdata/lsp/code_actions/move_method/instance_method.rb b/test/testdata/lsp/code_actions/move_method/instance_method.rb index 4c90130d6a..2de543d5da 100644 --- a/test/testdata/lsp/code_actions/move_method/instance_method.rb +++ b/test/testdata/lsp/code_actions/move_method/instance_method.rb @@ -9,17 +9,17 @@ class Inc sig {params(x: Integer).returns(Integer)} def inc(x) - # ^ apply-code-action: [A] Move method to a new module + # | apply-code-action: [A] Move method to a new module x + 1 end sig {void} def initialize; end - # ^ apply-code-action: [B] Move method to a new module + # | apply-code-action: [B] Move method to a new module sig {params(x: String).void} def call(x) - # ^ apply-code-action: [C] Move method to a new module + # | apply-code-action: [C] Move method to a new module puts x end end diff --git a/test/testdata/lsp/code_actions/move_method/mismatch_indentation.A.rbedited b/test/testdata/lsp/code_actions/move_method/mismatch_indentation.A.rbedited index 1d5948d4c2..726c1c2bbd 100644 --- a/test/testdata/lsp/code_actions/move_method/mismatch_indentation.A.rbedited +++ b/test/testdata/lsp/code_actions/move_method/mismatch_indentation.A.rbedited @@ -11,7 +11,7 @@ module Foo module Bar module Qux extend T::Sig - # ^ apply-code-action: [A] Move method to a new module + # | apply-code-action: [A] Move method to a new module end end end diff --git a/test/testdata/lsp/code_actions/move_method/mismatch_indentation.rb b/test/testdata/lsp/code_actions/move_method/mismatch_indentation.rb index bd7a3ad00e..57dfcf6897 100644 --- a/test/testdata/lsp/code_actions/move_method/mismatch_indentation.rb +++ b/test/testdata/lsp/code_actions/move_method/mismatch_indentation.rb @@ -7,7 +7,7 @@ module Qux extend T::Sig sig {void} def self.greeting; end - # ^ apply-code-action: [A] Move method to a new module + # | apply-code-action: [A] Move method to a new module end end end diff --git a/test/testdata/lsp/code_actions/move_method/no_resolve.A.rbedited b/test/testdata/lsp/code_actions/move_method/no_resolve.A.rbedited index 20228e9f1b..99108aa9a4 100644 --- a/test/testdata/lsp/code_actions/move_method/no_resolve.A.rbedited +++ b/test/testdata/lsp/code_actions/move_method/no_resolve.A.rbedited @@ -8,7 +8,7 @@ module GreetingModule extend T::Sig sig {returns(String)} def self.greeting - # ^^^^^^^^ apply-code-action: [A] Move method to a new module + # | apply-code-action: [A] Move method to a new module 'Hello' end end @@ -36,7 +36,7 @@ module A GreetingModule.greeting GreetingModule.greeting - print((GreetingModule).greeting) + print(GreetingModule.greeting) end sig {params(m: T.class_of(Foo)).void} diff --git a/test/testdata/lsp/code_actions/move_method/no_resolve.rb b/test/testdata/lsp/code_actions/move_method/no_resolve.rb index ee508ca0ee..6846be0ae6 100644 --- a/test/testdata/lsp/code_actions/move_method/no_resolve.rb +++ b/test/testdata/lsp/code_actions/move_method/no_resolve.rb @@ -9,7 +9,7 @@ module Foo sig {returns(String)} def self.greeting - # ^^^^^^^^ apply-code-action: [A] Move method to a new module + # | apply-code-action: [A] Move method to a new module 'Hello' end diff --git a/test/testdata/lsp/code_actions/move_method/no_sig.A.rbedited b/test/testdata/lsp/code_actions/move_method/no_sig.A.rbedited index 775eec4f14..81705625c2 100644 --- a/test/testdata/lsp/code_actions/move_method/no_sig.A.rbedited +++ b/test/testdata/lsp/code_actions/move_method/no_sig.A.rbedited @@ -1,4 +1,4 @@ -# typed: strict +# typed: true # selective-apply-code-action: refactor.extract # # The test is asserting we can move a method without a sig @@ -6,8 +6,7 @@ module BarModule extend T::Sig def self.bar -# ^^^^^^^^^^^^ error: The method `bar` does not have a `sig` - # ^^^ apply-code-action: [A] Move method to a new module + # | apply-code-action: [A] Move method to a new module 'Hello' end end diff --git a/test/testdata/lsp/code_actions/move_method/no_sig.rb b/test/testdata/lsp/code_actions/move_method/no_sig.rb index 2524c78d98..55a458f944 100644 --- a/test/testdata/lsp/code_actions/move_method/no_sig.rb +++ b/test/testdata/lsp/code_actions/move_method/no_sig.rb @@ -1,4 +1,4 @@ -# typed: strict +# typed: true # selective-apply-code-action: refactor.extract # # The test is asserting we can move a method without a sig @@ -7,8 +7,7 @@ module Foo extend T::Sig def self.bar -# ^^^^^^^^^^^^ error: The method `bar` does not have a `sig` - # ^^^ apply-code-action: [A] Move method to a new module + # | apply-code-action: [A] Move method to a new module 'Hello' end end diff --git a/test/testdata/lsp/code_actions/move_method/operator.rb b/test/testdata/lsp/code_actions/move_method/operator.rb index 1ed5b8ea02..f35afde960 100644 --- a/test/testdata/lsp/code_actions/move_method/operator.rb +++ b/test/testdata/lsp/code_actions/move_method/operator.rb @@ -8,7 +8,7 @@ module Foo extend T::Sig sig {params(obj: T.untyped).returns(T::Boolean)} def self.<=(obj) - # ^ apply-code-action: [A] Move method to a new module + # | apply-code-action: [A] Move method to a new module false end end diff --git a/test/testdata/lsp/code_actions/move_method/private_class_method.A.rbedited b/test/testdata/lsp/code_actions/move_method/private_class_method.A.rbedited index a92c98c748..f40473aaf5 100644 --- a/test/testdata/lsp/code_actions/move_method/private_class_method.A.rbedited +++ b/test/testdata/lsp/code_actions/move_method/private_class_method.A.rbedited @@ -5,7 +5,7 @@ module BarModule extend T::Sig sig {void} private_class_method def self.bar - # ^ apply-code-action: [A] Move method to a new module + # | apply-code-action: [A] Move method to a new module end end diff --git a/test/testdata/lsp/code_actions/move_method/private_class_method.rb b/test/testdata/lsp/code_actions/move_method/private_class_method.rb index 31ccd37be9..4ece0c39f8 100644 --- a/test/testdata/lsp/code_actions/move_method/private_class_method.rb +++ b/test/testdata/lsp/code_actions/move_method/private_class_method.rb @@ -6,6 +6,6 @@ module Foo sig {void} private_class_method def self.bar - # ^ apply-code-action: [A] Move method to a new module + # | apply-code-action: [A] Move method to a new module end end diff --git a/test/testdata/lsp/code_actions/move_method/prop.rb b/test/testdata/lsp/code_actions/move_method/prop.rb index 8f8e2213a3..b2a62912c8 100644 --- a/test/testdata/lsp/code_actions/move_method/prop.rb +++ b/test/testdata/lsp/code_actions/move_method/prop.rb @@ -6,5 +6,5 @@ class Foo < T::Struct prop :bar, Integer - # ^ apply-code-action: [A] Move method to a new module + # | apply-code-action: [A] Move method to a new module end diff --git a/test/testdata/lsp/code_actions/move_method/single_line_def.A.rbedited b/test/testdata/lsp/code_actions/move_method/single_line_def.A.rbedited index 0027f7c5cf..cbc851aadc 100644 --- a/test/testdata/lsp/code_actions/move_method/single_line_def.A.rbedited +++ b/test/testdata/lsp/code_actions/move_method/single_line_def.A.rbedited @@ -10,5 +10,5 @@ end module Bar extend T::Sig - # ^^^ apply-code-action: [A] Move method to a new module + # | apply-code-action: [A] Move method to a new module end diff --git a/test/testdata/lsp/code_actions/move_method/single_line_def.rb b/test/testdata/lsp/code_actions/move_method/single_line_def.rb index 44c62ad213..df7c410d4d 100644 --- a/test/testdata/lsp/code_actions/move_method/single_line_def.rb +++ b/test/testdata/lsp/code_actions/move_method/single_line_def.rb @@ -6,5 +6,5 @@ module Bar sig {void} def self.bar; end - # ^^^ apply-code-action: [A] Move method to a new module + # | apply-code-action: [A] Move method to a new module end diff --git a/test/testdata/lsp/code_actions/move_method/t_sig.A.rbedited b/test/testdata/lsp/code_actions/move_method/t_sig.A.rbedited index b50f2bcff4..8e7f251e2a 100644 --- a/test/testdata/lsp/code_actions/move_method/t_sig.A.rbedited +++ b/test/testdata/lsp/code_actions/move_method/t_sig.A.rbedited @@ -11,5 +11,5 @@ class Module end module Foo - # ^ apply-code-action: [A] Move method to a new module + # | apply-code-action: [A] Move method to a new module end diff --git a/test/testdata/lsp/code_actions/move_method/t_sig.rb b/test/testdata/lsp/code_actions/move_method/t_sig.rb index 82cbf7c297..e2118e8b53 100644 --- a/test/testdata/lsp/code_actions/move_method/t_sig.rb +++ b/test/testdata/lsp/code_actions/move_method/t_sig.rb @@ -8,5 +8,5 @@ class Module module Foo sig {void} def self.bar; end - # ^ apply-code-action: [A] Move method to a new module + # | apply-code-action: [A] Move method to a new module end diff --git a/test/testdata/lsp/code_actions/move_method/utf8_method_name.A.rbedited b/test/testdata/lsp/code_actions/move_method/utf8_method_name.A.rbedited index 4bff94f2d9..eb3426df5a 100644 --- a/test/testdata/lsp/code_actions/move_method/utf8_method_name.A.rbedited +++ b/test/testdata/lsp/code_actions/move_method/utf8_method_name.A.rbedited @@ -11,15 +11,15 @@ end module Foo extend T::Sig - # ^ apply-code-action: [A] Move method to a new module + # | apply-code-action: [A] Move method to a new module sig {void} def self.😱; end - # ^ apply-code-action: [B] Move method to a new module + # | apply-code-action: [B] Move method to a new module sig {void} def self.method_with_?; end - # ^ apply-code-action: [C] Move method to a new module + # | apply-code-action: [C] Move method to a new module end diff --git a/test/testdata/lsp/code_actions/move_method/utf8_method_name.B.rbedited b/test/testdata/lsp/code_actions/move_method/utf8_method_name.B.rbedited index 596812ae64..b1d8dd4212 100644 --- a/test/testdata/lsp/code_actions/move_method/utf8_method_name.B.rbedited +++ b/test/testdata/lsp/code_actions/move_method/utf8_method_name.B.rbedited @@ -13,13 +13,13 @@ module Foo extend T::Sig sig {void} def self.ж; end - # ^ apply-code-action: [A] Move method to a new module + # | apply-code-action: [A] Move method to a new module - # ^ apply-code-action: [B] Move method to a new module + # | apply-code-action: [B] Move method to a new module sig {void} def self.method_with_?; end - # ^ apply-code-action: [C] Move method to a new module + # | apply-code-action: [C] Move method to a new module end diff --git a/test/testdata/lsp/code_actions/move_method/utf8_method_name.C.rbedited b/test/testdata/lsp/code_actions/move_method/utf8_method_name.C.rbedited index 1ab7b0af89..6c947b1b62 100644 --- a/test/testdata/lsp/code_actions/move_method/utf8_method_name.C.rbedited +++ b/test/testdata/lsp/code_actions/move_method/utf8_method_name.C.rbedited @@ -13,13 +13,13 @@ module Foo extend T::Sig sig {void} def self.ж; end - # ^ apply-code-action: [A] Move method to a new module + # | apply-code-action: [A] Move method to a new module sig {void} def self.😱; end - # ^ apply-code-action: [B] Move method to a new module + # | apply-code-action: [B] Move method to a new module - # ^ apply-code-action: [C] Move method to a new module + # | apply-code-action: [C] Move method to a new module end diff --git a/test/testdata/lsp/code_actions/move_method/utf8_method_name.rb b/test/testdata/lsp/code_actions/move_method/utf8_method_name.rb index c99b06a5dc..4c55846c50 100644 --- a/test/testdata/lsp/code_actions/move_method/utf8_method_name.rb +++ b/test/testdata/lsp/code_actions/move_method/utf8_method_name.rb @@ -7,15 +7,15 @@ module Foo extend T::Sig sig {void} def self.ж; end - # ^ apply-code-action: [A] Move method to a new module + # | apply-code-action: [A] Move method to a new module sig {void} def self.😱; end - # ^ apply-code-action: [B] Move method to a new module + # | apply-code-action: [B] Move method to a new module sig {void} def self.method_with_?; end - # ^ apply-code-action: [C] Move method to a new module + # | apply-code-action: [C] Move method to a new module end diff --git a/test/testdata/lsp/command_find_refs.rb b/test/testdata/lsp/command_find_refs.rb new file mode 100644 index 0000000000..e1e5592164 --- /dev/null +++ b/test/testdata/lsp/command_find_refs.rb @@ -0,0 +1,19 @@ +# typed: true + +class Opus::Command + extend T::Sig + def self.call(*args, **kwargs, &blk) + end +end + +class MyCommand < Opus::Command + sig { params(x: Integer, y: Integer).void } + def call(x, y) + # ^ def: MyCommand.call + end +end + +MyCommand.call(1, 2) +# ^ usage: MyCommand.call +MyCommand.call(3, 4) +# ^ usage: MyCommand.call diff --git a/test/testdata/lsp/completion/alias_method.A.rbedited b/test/testdata/lsp/completion/alias_method.A.rbedited new file mode 100644 index 0000000000..44f18eac8d --- /dev/null +++ b/test/testdata/lsp/completion/alias_method.A.rbedited @@ -0,0 +1,15 @@ +# typed: true + +class A + def to_this; end + + alias_method :from_this, :to_this +end + +A.new.from_this${0} # error: does not exist +# ^ completion: from_this +# ^ apply-completion: [A] item: 0 + +A.new.to_thi # error: does not exist +# ^ completion: to_this +# ^ apply-completion: [B] item: 0 diff --git a/test/testdata/lsp/completion/alias_method.B.rbedited b/test/testdata/lsp/completion/alias_method.B.rbedited new file mode 100644 index 0000000000..ce9b2bb596 --- /dev/null +++ b/test/testdata/lsp/completion/alias_method.B.rbedited @@ -0,0 +1,15 @@ +# typed: true + +class A + def to_this; end + + alias_method :from_this, :to_this +end + +A.new.from_thi # error: does not exist +# ^ completion: from_this +# ^ apply-completion: [A] item: 0 + +A.new.to_this${0} # error: does not exist +# ^ completion: to_this +# ^ apply-completion: [B] item: 0 diff --git a/test/testdata/lsp/completion/alias_method.rb b/test/testdata/lsp/completion/alias_method.rb index 5924fcb233..b5bdb2e2fb 100644 --- a/test/testdata/lsp/completion/alias_method.rb +++ b/test/testdata/lsp/completion/alias_method.rb @@ -8,3 +8,8 @@ def to_this; end A.new.from_thi # error: does not exist # ^ completion: from_this +# ^ apply-completion: [A] item: 0 + +A.new.to_thi # error: does not exist +# ^ completion: to_this +# ^ apply-completion: [B] item: 0 diff --git a/test/testdata/lsp/completion/case_1.rb b/test/testdata/lsp/completion/case_1.rb index 832be850e2..73fe05ad25 100644 --- a/test/testdata/lsp/completion/case_1.rb +++ b/test/testdata/lsp/completion/case_1.rb @@ -34,6 +34,6 @@ def self.test4(x) case x # error: Hint: this "case" token might not be properly closed when MethodCompletion. # ^ completion: test1, test2, test3, test4, ... - y = nil # error: Method `y=` does not exist on `T.class_of(MethodCompletion)` + y = nil # error: Setter method `y=` does not exist on `T.class_of(MethodCompletion)` end end # error: unexpected token "end of file" diff --git a/test/testdata/lsp/completion/case_2.rb b/test/testdata/lsp/completion/case_2.rb index a73ec1d262..363e0723d2 100644 --- a/test/testdata/lsp/completion/case_2.rb +++ b/test/testdata/lsp/completion/case_2.rb @@ -37,6 +37,6 @@ def self.test4(x) case x # error: Hint: this "case" token might not be properly closed when ConstantCompletion:: # ^ completion: A, B, ... - y = nil # error: Method `y=` does not exist on `T.class_of(ConstantCompletion)` + y = nil # error: Setter method `y=` does not exist on `T.class_of(ConstantCompletion)` end end # error: unexpected token "end of file" diff --git a/test/testdata/lsp/completion/constants_root.rb b/test/testdata/lsp/completion/constants_root.rb index 8c5f1a9851..cdd4460556 100644 --- a/test/testdata/lsp/completion/constants_root.rb +++ b/test/testdata/lsp/completion/constants_root.rb @@ -1,7 +1,7 @@ # typed: true # Prevents regression against an off-by-one error we used to have that would -# stop before suggestion similiar constants that existed on +# stop before suggestion similar constants that existed on class A extend T::Sig diff --git a/test/testdata/lsp/completion/ignored_file.rb b/test/testdata/lsp/completion/ignored_file.rb new file mode 100644 index 0000000000..61c2e9fb1a --- /dev/null +++ b/test/testdata/lsp/completion/ignored_file.rb @@ -0,0 +1,14 @@ +# typed: ignore + +class AnImportantClass + def important_method(important_parameter) + important + # ^ completion: (file is not `# typed: true` or higher) + end +end + +An +# ^ completion: (file is not `# typed: true` or higher) + +AnImportantClass.n +# ^ completion: (file is not `# typed: true` or higher) diff --git a/test/testdata/lsp/completion/locals.rb b/test/testdata/lsp/completion/locals.rb index 24331feca6..3518f09b02 100644 --- a/test/testdata/lsp/completion/locals.rb +++ b/test/testdata/lsp/completion/locals.rb @@ -53,7 +53,7 @@ class A def a_method; end end -# If there's an explicit reciever, don't suggest similar locals. +# If there's an explicit receiver, don't suggest similar locals. def no_locals_after_dot() a_local = nil A.new.a_ # error: does not exist diff --git a/test/testdata/lsp/completion/missing_const_name.rb.desugar-tree.exp b/test/testdata/lsp/completion/missing_const_name.rb.desugar-tree.exp index 456c43aaf7..7169a3c085 100644 --- a/test/testdata/lsp/completion/missing_const_name.rb.desugar-tree.exp +++ b/test/testdata/lsp/completion/missing_const_name.rb.desugar-tree.exp @@ -22,7 +22,7 @@ class <>> < (::) def test_constant_completion_before_keyword<>(&) begin ::::> - + nil end end diff --git a/test/testdata/lsp/completion/missing_fun.rb b/test/testdata/lsp/completion/missing_fun.rb index aff69f8018..69d88251c1 100644 --- a/test/testdata/lsp/completion/missing_fun.rb +++ b/test/testdata/lsp/completion/missing_fun.rb @@ -72,7 +72,7 @@ def test_first_arg_no_parens(x) def test_before_var_assign(x) x. # ^ completion: bar, foo, qux, ... - y = nil # error: Method `y=` does not exist on `M` + y = nil # error: Setter method `y=` does not exist on `M` end # This is also technically a valid Ruby program, but Sorbet doesn't support it diff --git a/test/testdata/lsp/completion/missing_fun.rb.desugar-tree.exp b/test/testdata/lsp/completion/missing_fun.rb.desugar-tree.exp index 8582c25d77..f6faa5b10e 100644 --- a/test/testdata/lsp/completion/missing_fun.rb.desugar-tree.exp +++ b/test/testdata/lsp/completion/missing_fun.rb.desugar-tree.exp @@ -38,9 +38,9 @@ class <>> < (::) def test_completion_between_keywords<>(x, &) begin - + nil x.() - + nil end end diff --git a/test/testdata/lsp/completion/overloads_test.A.rbedited b/test/testdata/lsp/completion/overloads_test.A.rbedited index f658a89810..27073ad759 100644 --- a/test/testdata/lsp/completion/overloads_test.A.rbedited +++ b/test/testdata/lsp/completion/overloads_test.A.rbedited @@ -6,7 +6,7 @@ class A extend T::Sig sig {params(x: I).void} sig {params(x: S).void} - def my_method(x); end + def my_method(x); end # error: against an overloaded signature end # Not possible to see in the test, but in VS Code though these items have the diff --git a/test/testdata/lsp/completion/overloads_test.B.rbedited b/test/testdata/lsp/completion/overloads_test.B.rbedited index c1d95ce0b5..500346ca5e 100644 --- a/test/testdata/lsp/completion/overloads_test.B.rbedited +++ b/test/testdata/lsp/completion/overloads_test.B.rbedited @@ -6,7 +6,7 @@ class A extend T::Sig sig {params(x: I).void} sig {params(x: S).void} - def my_method(x); end + def my_method(x); end # error: against an overloaded signature end # Not possible to see in the test, but in VS Code though these items have the diff --git a/test/testdata/lsp/completion/overloads_test.rb b/test/testdata/lsp/completion/overloads_test.rb index 3140111992..133b8e26a3 100644 --- a/test/testdata/lsp/completion/overloads_test.rb +++ b/test/testdata/lsp/completion/overloads_test.rb @@ -6,7 +6,7 @@ class A extend T::Sig sig {params(x: I).void} sig {params(x: S).void} - def my_method(x); end + def my_method(x); end # error: against an overloaded signature end # Not possible to see in the test, but in VS Code though these items have the diff --git a/test/testdata/lsp/completion/snippet_before_variable.A.rbedited b/test/testdata/lsp/completion/snippet_before_variable.A.rbedited new file mode 100644 index 0000000000..3639e209ba --- /dev/null +++ b/test/testdata/lsp/completion/snippet_before_variable.A.rbedited @@ -0,0 +1,22 @@ +# typed: true + +class A + extend T::Sig + sig { params(x: Integer).void } + def self.example(x) + puts(x + 1) + end +end + +begin + A. + # ^ completion: example, ... + # ^ apply-completion: [B] item: 0 + + x = nil # error: does not exist + + A.example(${1:Integer})${0} + # ^ completion: example, ... + # ^ apply-completion: [A] item: 0 + +end # error: unexpected token "end" diff --git a/test/testdata/lsp/completion/snippet_before_variable.B.rbedited b/test/testdata/lsp/completion/snippet_before_variable.B.rbedited new file mode 100644 index 0000000000..5161c1160f --- /dev/null +++ b/test/testdata/lsp/completion/snippet_before_variable.B.rbedited @@ -0,0 +1,22 @@ +# typed: true + +class A + extend T::Sig + sig { params(x: Integer).void } + def self.example(x) + puts(x + 1) + end +end + +begin + A.example(${1:Integer})${0} + # ^ completion: example, ... + # ^ apply-completion: [B] item: 0 + + x = nil # error: does not exist + + A. + # ^ completion: example, ... + # ^ apply-completion: [A] item: 0 + +end # error: unexpected token "end" diff --git a/test/testdata/lsp/completion/snippet_before_variable.rb b/test/testdata/lsp/completion/snippet_before_variable.rb new file mode 100644 index 0000000000..c7b0e85eb3 --- /dev/null +++ b/test/testdata/lsp/completion/snippet_before_variable.rb @@ -0,0 +1,22 @@ +# typed: true + +class A + extend T::Sig + sig { params(x: Integer).void } + def self.example(x) + puts(x + 1) + end +end + +begin + A. + # ^ completion: example, ... + # ^ apply-completion: [B] item: 0 + + x = nil # error: does not exist + + A. + # ^ completion: example, ... + # ^ apply-completion: [A] item: 0 + +end # error: unexpected token "end" diff --git a/test/testdata/lsp/completion/typed_false_completion_nudges_disabled.rb b/test/testdata/lsp/completion/typed_false_completion_nudges_disabled.rb new file mode 100644 index 0000000000..4f8a67e70a --- /dev/null +++ b/test/testdata/lsp/completion/typed_false_completion_nudges_disabled.rb @@ -0,0 +1,16 @@ +# typed: false + +# enable-typed-false-completion-nudges: false + +class AnImportantClass + def important_method(important_parameter) + important + # ^ completion: (nothing) + end +end + +An # error: Unable to resolve constant +# ^ completion: AnImportantClass + +AnImportantClass.n +# ^ completion: (nothing) diff --git a/test/testdata/lsp/completion/untyped_file.rb b/test/testdata/lsp/completion/untyped_file.rb index b5bf5b0ebd..a1d6432a47 100644 --- a/test/testdata/lsp/completion/untyped_file.rb +++ b/test/testdata/lsp/completion/untyped_file.rb @@ -8,8 +8,7 @@ def important_method(important_parameter) end An # error: Unable to resolve constant -# ^ completion: (file is not `# typed: true` or higher) +# ^ completion: AnImportantClass AnImportantClass.n # ^ completion: (file is not `# typed: true` or higher) - diff --git a/test/testdata/lsp/def_literal.rb b/test/testdata/lsp/def_literal.rb new file mode 100644 index 0000000000..d81cb84b6c --- /dev/null +++ b/test/testdata/lsp/def_literal.rb @@ -0,0 +1,5 @@ +# typed: true + +x = 'foo/bar' +# ^ def: (nothing) +p(x) diff --git a/test/testdata/lsp/definition_alias.rb b/test/testdata/lsp/definition_alias.rb new file mode 100644 index 0000000000..ca245e8e4b --- /dev/null +++ b/test/testdata/lsp/definition_alias.rb @@ -0,0 +1,14 @@ +# typed: true + +class A; end +class Scope + X = A +# ^ def: X +end + +class Scope + puts(X) + # ^ usage: X +end + +puts(A) diff --git a/test/testdata/lsp/document_symbols.rb b/test/testdata/lsp/document_symbols.rb index 97411347a2..3de1893ea3 100644 --- a/test/testdata/lsp/document_symbols.rb +++ b/test/testdata/lsp/document_symbols.rb @@ -36,7 +36,7 @@ def hi(x) sig(:final) {params(x: String).returns(String)} def bye(x) - # ^^^ hover: sig(:final) {params(x: String).returns(String)} + # ^^^ hover: sig(:final) { params(x: String).returns(String) } "goodbye, #{x}" end end diff --git a/test/testdata/lsp/document_symbols_minitest.rb.document-symbols.exp b/test/testdata/lsp/document_symbols_minitest.rb.document-symbols.exp index fdf5991ea7..6b3ddc37d4 100644 --- a/test/testdata/lsp/document_symbols_minitest.rb.document-symbols.exp +++ b/test/testdata/lsp/document_symbols_minitest.rb.document-symbols.exp @@ -48,8 +48,8 @@ "character": 2 }, "end": { - "line": 18, - "character": 5 + "line": 8, + "character": 2 } }, "children": [ @@ -73,8 +73,8 @@ "character": 4 }, "end": { - "line": 17, - "character": 7 + "line": 16, + "character": 4 } }, "children": [] @@ -99,65 +99,13 @@ "character": 4 }, "end": { - "line": 14, - "character": 7 - } - }, - "children": [] - }, - { - "name": "initialize", - "detail": "", - "kind": 9, - "range": { - "start": { - "line": 9, - "character": 4 - }, - "end": { - "line": 11, - "character": 7 - } - }, - "selectionRange": { - "start": { - "line": 9, + "line": 13, "character": 4 - }, - "end": { - "line": 11, - "character": 7 } }, "children": [] } ] - }, - { - "name": "initialize", - "detail": "", - "kind": 9, - "range": { - "start": { - "line": 4, - "character": 2 - }, - "end": { - "line": 6, - "character": 5 - } - }, - "selectionRange": { - "start": { - "line": 4, - "character": 2 - }, - "end": { - "line": 6, - "character": 5 - } - }, - "children": [] } ] } diff --git a/test/testdata/lsp/fast_path/alias_method_loc.1.rbupdate b/test/testdata/lsp/fast_path/alias_method_loc.1.rbupdate new file mode 100644 index 0000000000..a23624848f --- /dev/null +++ b/test/testdata/lsp/fast_path/alias_method_loc.1.rbupdate @@ -0,0 +1,10 @@ +# typed: true + +class Alias + def foo; end + + alias_method :method_with_specific_name, :foo +end + +Alias.new.method_with_specific_nam +# ^^^^^^^^^^^^^^^^^^^^^^^^ error: Method `method_with_specific_nam` does not exist on `Alias` diff --git a/test/testdata/lsp/fast_path/alias_method_loc.rb b/test/testdata/lsp/fast_path/alias_method_loc.rb new file mode 100644 index 0000000000..e1d7ab77ee --- /dev/null +++ b/test/testdata/lsp/fast_path/alias_method_loc.rb @@ -0,0 +1,20 @@ +# typed: true + +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. + +class Alias + def foo; end + + alias_method :method_with_specific_name, :foo +end + +Alias.new.method_with_specific_nam +# ^^^^^^^^^^^^^^^^^^^^^^^^ error: Method `method_with_specific_nam` does not exist on `Alias` diff --git a/test/testdata/lsp/fast_path/attached_class_loc.rb b/test/testdata/lsp/fast_path/attached_class_loc.rb new file mode 100644 index 0000000000..58ae451e16 --- /dev/null +++ b/test/testdata/lsp/fast_path/attached_class_loc.rb @@ -0,0 +1,38 @@ +# typed: true + +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. + +module Parent + extend T::Generic + has_attached_class! { {upper: Integer} } +end + +class Child + extend T::Generic + extend Parent +end + +# module Parent +# extend T::Generic +# FakeAttachedClass = type_member { {upper: Integer} } +# end + +# class Child +# extend T::Generic +# extend Parent +# FakeAttachedClass = type_template { {upper: Child} } +# end diff --git a/test/testdata/lsp/fast_path/call_with_block__2.1.rbupdate b/test/testdata/lsp/fast_path/call_with_block__2.1.rbupdate index 439357d5b1..30ce3102cc 100644 --- a/test/testdata/lsp/fast_path/call_with_block__2.1.rbupdate +++ b/test/testdata/lsp/fast_path/call_with_block__2.1.rbupdate @@ -2,7 +2,7 @@ # exclude-from-file-update: true int_to_str = T.let(->(x) {x.to_s}, T.proc.params(arg0: Integer).returns(String)) -str_to_int = T.let(->(x) {x.to_s}, T.proc.params(arg0: String).returns(Integer)) +str_to_int = T.let(->(x) { 1234 }, T.proc.params(arg0: String).returns(Integer)) A.takes_blk(&int_to_str) # error: Expected `T.proc.params(arg0: String).returns(Integer)` but found `T.proc.params(arg0: Integer).returns(String)` for block argument A.takes_blk(&str_to_int) diff --git a/test/testdata/lsp/fast_path/call_with_block__2.rb b/test/testdata/lsp/fast_path/call_with_block__2.rb index 2e2aec7ac6..6fd71fd85a 100644 --- a/test/testdata/lsp/fast_path/call_with_block__2.rb +++ b/test/testdata/lsp/fast_path/call_with_block__2.rb @@ -2,7 +2,7 @@ # spacer for exclude-from-file-update int_to_str = T.let(->(x) {x.to_s}, T.proc.params(arg0: Integer).returns(String)) -str_to_int = T.let(->(x) {x.to_s}, T.proc.params(arg0: String).returns(Integer)) +str_to_int = T.let(->(x) { 1234 }, T.proc.params(arg0: String).returns(Integer)) A.takes_blk(&int_to_str) A.takes_blk(&str_to_int) # error: Expected `T.proc.params(arg0: Integer).returns(String)` but found `T.proc.params(arg0: String).returns(Integer)` for block argument diff --git a/test/testdata/lsp/fast_path/location_updates/overloads_test.1.rbupdate b/test/testdata/lsp/fast_path/location_updates/overloads_test.1.rbupdate new file mode 100644 index 0000000000..8abc12b1bc --- /dev/null +++ b/test/testdata/lsp/fast_path/location_updates/overloads_test.1.rbupdate @@ -0,0 +1,15 @@ +# typed: true + + +class I; end +class S; end +class A + extend T::Sig + sig {params(x: I).void} + sig {params(x: S).void} + def my_method(x); end # error: against an overloaded signature +end + + +A.new.my_metho # error: does not exist +# ^ completion: my_method, my_method diff --git a/test/testdata/lsp/fast_path/location_updates/overloads_test.rb b/test/testdata/lsp/fast_path/location_updates/overloads_test.rb new file mode 100644 index 0000000000..2b5d830362 --- /dev/null +++ b/test/testdata/lsp/fast_path/location_updates/overloads_test.rb @@ -0,0 +1,51 @@ +# typed: true + +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. + +class I; end +class S; end +class A + extend T::Sig + sig {params(x: I).void} + sig {params(x: S).void} + def my_method(x); end # error: against an overloaded signature +end + + +A.new.my_metho # error: does not exist +# ^ completion: my_method, my_method diff --git a/test/testdata/lsp/fast_path/method_change_kw_arg_name.1.rbupdate b/test/testdata/lsp/fast_path/method_change_kw_arg_name.1.rbupdate index 173438223c..d0b791e3e8 100644 --- a/test/testdata/lsp/fast_path/method_change_kw_arg_name.1.rbupdate +++ b/test/testdata/lsp/fast_path/method_change_kw_arg_name.1.rbupdate @@ -10,6 +10,7 @@ end A.new.bar # ^ error: Missing required keyword argument `y` for method `A#bar` -A.new.bar(x: 0) # error: Unrecognized keyword argument `x` passed for method `A#bar` +A.new.bar(x: 0) +# ^^^^ error: Unrecognized keyword argument `x` passed for method `A#bar` # ^^^^ error: Missing required keyword argument `y` for method `A#bar` A.new.bar(y: 0) diff --git a/test/testdata/lsp/fast_path/method_change_kw_arg_name.rb b/test/testdata/lsp/fast_path/method_change_kw_arg_name.rb index 40bbafe98d..eb369738a8 100644 --- a/test/testdata/lsp/fast_path/method_change_kw_arg_name.rb +++ b/test/testdata/lsp/fast_path/method_change_kw_arg_name.rb @@ -10,5 +10,6 @@ def bar(x:) A.new.bar # ^ error: Missing required keyword argument `x` for method `A#bar` A.new.bar(x: 0) -A.new.bar(y: 0) # error: Unrecognized keyword argument `y` passed for method `A#bar` +A.new.bar(y: 0) +# ^^^^ error: Unrecognized keyword argument `y` passed for method `A#bar` # ^^^^ error: Missing required keyword argument `x` for method `A#bar` diff --git a/test/testdata/lsp/fast_path/method_signature_update_generics__usage.rb b/test/testdata/lsp/fast_path/method_signature_update_generics__usage.rb index 057e544fa7..42bb5abe4b 100644 --- a/test/testdata/lsp/fast_path/method_signature_update_generics__usage.rb +++ b/test/testdata/lsp/fast_path/method_signature_update_generics__usage.rb @@ -4,6 +4,6 @@ module Bat extend T::Sig sig {returns(String)} def foo Foobar.bar(["hello"]) # error: Expected `T::Array[Integer]` - # ^ hover: sig {params(s: T::Array[Integer]).returns(String)} + # ^ hover: sig { params(s: T::Array[Integer]).returns(String) } end end diff --git a/test/testdata/lsp/fast_path/method_signature_update_static__usage.rb b/test/testdata/lsp/fast_path/method_signature_update_static__usage.rb index 0eda3f15a9..808e19a178 100644 --- a/test/testdata/lsp/fast_path/method_signature_update_static__usage.rb +++ b/test/testdata/lsp/fast_path/method_signature_update_static__usage.rb @@ -4,6 +4,6 @@ module Bat extend T::Sig sig {returns(String)} def foo Foobar.bar("hello") # error: Expected `Integer` - # ^ hover: sig {params(s: Integer).returns(String)} + # ^ hover: sig { params(s: Integer).returns(String) } end end diff --git a/test/testdata/lsp/fast_path/more_arguments.1.rbupdate b/test/testdata/lsp/fast_path/more_arguments.1.rbupdate index 2ec566d1d6..c51c6cc529 100644 --- a/test/testdata/lsp/fast_path/more_arguments.1.rbupdate +++ b/test/testdata/lsp/fast_path/more_arguments.1.rbupdate @@ -7,7 +7,7 @@ class Foo sig {params(x: Integer).returns(Integer)} def foo(x) # ^ hover: ```ruby - # ^ hover: sig {params(x: Integer).returns(Integer)} + # ^ hover: sig { params(x: Integer).returns(Integer) } # ^ hover: def foo(x); end 42 + x end diff --git a/test/testdata/lsp/fast_path/more_arguments.rb b/test/testdata/lsp/fast_path/more_arguments.rb index 14c0237750..f40c2f3902 100644 --- a/test/testdata/lsp/fast_path/more_arguments.rb +++ b/test/testdata/lsp/fast_path/more_arguments.rb @@ -5,7 +5,7 @@ class Foo sig {returns(Integer)} def foo - # ^ hover: sig {returns(Integer)} + # ^ hover: sig { returns(Integer) } # ^ hover: def foo; end 42 end diff --git a/test/testdata/lsp/fast_path/overloads_test.1.rbupdate b/test/testdata/lsp/fast_path/overloads_test.1.rbupdate index f1024a2786..2b9accb0ae 100644 --- a/test/testdata/lsp/fast_path/overloads_test.1.rbupdate +++ b/test/testdata/lsp/fast_path/overloads_test.1.rbupdate @@ -6,7 +6,7 @@ class A sig {params(x: Integer).void} sig {params(x: Integer, y: String).void} - def self.example1(x, y='') + def self.example1(x, y='') # error: against an overloaded signature end end diff --git a/test/testdata/lsp/fast_path/overloads_test.rb b/test/testdata/lsp/fast_path/overloads_test.rb index 48c3bdf22c..2197af19a5 100644 --- a/test/testdata/lsp/fast_path/overloads_test.rb +++ b/test/testdata/lsp/fast_path/overloads_test.rb @@ -5,7 +5,7 @@ class A sig {params(x: Integer).void} sig {params(x: Integer, y: String).void} - def self.example(x, y='') + def self.example(x, y='') # error: against an overloaded signature end end diff --git a/test/testdata/lsp/fast_path/private_overloads__1.1.rbiupdate b/test/testdata/lsp/fast_path/private_overloads__1.1.rbiupdate new file mode 100644 index 0000000000..0baad576b7 --- /dev/null +++ b/test/testdata/lsp/fast_path/private_overloads__1.1.rbiupdate @@ -0,0 +1,14 @@ +# typed: strict +# exclude-from-file-update: true + +# Must be at least one `hover` in this file to trigger the +# LSPTypechecker::retypecheck codepath (fast path with no edit) + +class PrivateOverloads + # ^ hover: T.class_of(PrivateOverloads) + extend T::Sig + + sig {returns(NilClass)} + sig {params(x: Integer).returns(Integer)} + private def foo(x=nil); end +end diff --git a/test/testdata/lsp/fast_path/private_overloads__1.1.rbupdate b/test/testdata/lsp/fast_path/private_overloads__1.1.rbupdate deleted file mode 100644 index c92fbaab0f..0000000000 --- a/test/testdata/lsp/fast_path/private_overloads__1.1.rbupdate +++ /dev/null @@ -1,20 +0,0 @@ -# typed: __STDLIB_INTERNAL -# exclude-from-file-update: true - -class PrivateOverloads - extend T::Sig - - sig {returns(NilClass)} - sig {params(x: Integer).returns(Integer)} - private def foo(x=nil); end -end - -po1 = PrivateOverloads.new.foo # error: Non-private call to private method -T.reveal_type(po1) # error: Revealed type: `NilClass` - - -# Must be at least one `hover` in this file to trigger the -# LSPTypechecker::retypecheck codepath (fast path with no edit) - -po2 = PrivateOverloads.new.foo(0) # error: Non-private call to private method -T.reveal_type(po2) # error: Revealed type: `Integer` diff --git a/test/testdata/lsp/fast_path/private_overloads__1.rb b/test/testdata/lsp/fast_path/private_overloads__1.rb deleted file mode 100644 index 161d3dfbb5..0000000000 --- a/test/testdata/lsp/fast_path/private_overloads__1.rb +++ /dev/null @@ -1,20 +0,0 @@ -# typed: __STDLIB_INTERNAL -# spacer for exclude-from-file-update - -class PrivateOverloads - extend T::Sig - - sig {returns(NilClass)} - sig {params(x: Integer).returns(Integer)} - private def foo(x=nil); end -end - -po1 = PrivateOverloads.new.foo # error: Non-private call to private method -T.reveal_type(po1) # error: Revealed type: `NilClass` -# ^ hover: NilClass - -# Must be at least one `hover` in this file to trigger the -# LSPTypechecker::retypecheck codepath (fast path with no edit) - -po2 = PrivateOverloads.new.foo(0) # error: Non-private call to private method -T.reveal_type(po2) # error: Revealed type: `Integer` diff --git a/test/testdata/lsp/fast_path/private_overloads__1.rbi b/test/testdata/lsp/fast_path/private_overloads__1.rbi new file mode 100644 index 0000000000..036acf8467 --- /dev/null +++ b/test/testdata/lsp/fast_path/private_overloads__1.rbi @@ -0,0 +1,14 @@ +# typed: strict +# spacer for exclude-from-file-update + +# Must be at least one `hover` in this file to trigger the +# LSPTypechecker::retypecheck codepath (fast path with no edit) + +class PrivateOverloads + # ^ hover: T.class_of(PrivateOverloads) + extend T::Sig + + sig {returns(NilClass)} + sig {params(x: Integer).returns(Integer)} + private def foo(x=nil); end +end diff --git a/test/testdata/lsp/fast_path/private_overloads__2.1.rbupdate b/test/testdata/lsp/fast_path/private_overloads__2.1.rbupdate index 30ebb6dc04..298a7fbfa2 100644 --- a/test/testdata/lsp/fast_path/private_overloads__2.1.rbupdate +++ b/test/testdata/lsp/fast_path/private_overloads__2.1.rbupdate @@ -4,3 +4,9 @@ def example PrivateOverloads.new.foo # error: Non-private call to private method end + +po1 = PrivateOverloads.new.foo # error: Non-private call to private method +T.reveal_type(po1) # error: Revealed type: `NilClass` + +po2 = PrivateOverloads.new.foo(0) # error: Non-private call to private method +T.reveal_type(po2) # error: Revealed type: `Integer` diff --git a/test/testdata/lsp/fast_path/private_overloads__2.rb b/test/testdata/lsp/fast_path/private_overloads__2.rb index f3de9386d1..21751b818c 100644 --- a/test/testdata/lsp/fast_path/private_overloads__2.rb +++ b/test/testdata/lsp/fast_path/private_overloads__2.rb @@ -2,3 +2,9 @@ def example end + +po1 = PrivateOverloads.new.foo # error: Non-private call to private method +T.reveal_type(po1) # error: Revealed type: `NilClass` + +po2 = PrivateOverloads.new.foo(0) # error: Non-private call to private method +T.reveal_type(po2) # error: Revealed type: `Integer` diff --git a/test/testdata/lsp/fast_path/sealed_subclasses.1.rbupdate b/test/testdata/lsp/fast_path/sealed_subclasses.1.rbupdate new file mode 100644 index 0000000000..09e288d4ca --- /dev/null +++ b/test/testdata/lsp/fast_path/sealed_subclasses.1.rbupdate @@ -0,0 +1,16 @@ +# typed: true + +class Parent + extend T::Helpers + sealed! +end + +class Child < Parent + extend T::Sig + + sig { override.returns(Integer) } + def self.sealed_subclasses +# ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Return type `Integer` does not match return type of overridden method `Parent.sealed_subclasses` + 0 + end +end diff --git a/test/testdata/lsp/fast_path/sealed_subclasses.rb b/test/testdata/lsp/fast_path/sealed_subclasses.rb new file mode 100644 index 0000000000..bc67d7f72d --- /dev/null +++ b/test/testdata/lsp/fast_path/sealed_subclasses.rb @@ -0,0 +1,32 @@ +# typed: true + +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. + +class Parent + extend T::Helpers + sealed! +end + +class Child < Parent + extend T::Sig + + sig { override.returns(Integer) } + def self.sealed_subclasses +# ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Return type `Integer` does not match return type of overridden method `Parent.sealed_subclasses` + 0 + end +end diff --git a/test/testdata/lsp/fast_path/simple_hover.1.rbupdate b/test/testdata/lsp/fast_path/simple_hover.1.rbupdate index 454cdcd2cb..7b3aae67a3 100644 --- a/test/testdata/lsp/fast_path/simple_hover.1.rbupdate +++ b/test/testdata/lsp/fast_path/simple_hover.1.rbupdate @@ -7,7 +7,7 @@ class Foo sig {returns(Integer)} def bar # ^ hover: ```ruby - # ^ hover: sig {returns(Integer)} + # ^ hover: sig { returns(Integer) } # ^ hover: def bar; end 42 end diff --git a/test/testdata/lsp/fast_path/simple_hover.rb b/test/testdata/lsp/fast_path/simple_hover.rb index 42e6fa850c..f40c2f3902 100644 --- a/test/testdata/lsp/fast_path/simple_hover.rb +++ b/test/testdata/lsp/fast_path/simple_hover.rb @@ -5,8 +5,8 @@ class Foo sig {returns(Integer)} def foo - # ^ hover: sig {returns(Integer)} + # ^ hover: sig { returns(Integer) } # ^ hover: def foo; end 42 end -end \ No newline at end of file +end diff --git a/test/testdata/lsp/fast_path/static_field_loc.1.rbupdate b/test/testdata/lsp/fast_path/static_field_loc.1.rbupdate new file mode 100644 index 0000000000..4ec9f03fd5 --- /dev/null +++ b/test/testdata/lsp/fast_path/static_field_loc.1.rbupdate @@ -0,0 +1,7 @@ +# typed: true +# +class B + R = 1 +end + +T.reveal_type(B::R) # error: Revealed type: `Integer` diff --git a/test/testdata/lsp/fast_path/static_field_loc.rb b/test/testdata/lsp/fast_path/static_field_loc.rb new file mode 100644 index 0000000000..6da168569a --- /dev/null +++ b/test/testdata/lsp/fast_path/static_field_loc.rb @@ -0,0 +1,26 @@ +# typed: true + +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. +# +class B + R = 1 +end + +T.reveal_type(B::R) # error: Revealed type: `Integer` diff --git a/test/testdata/lsp/fast_path/static_field_name.1.rbupdate b/test/testdata/lsp/fast_path/static_field_name.1.rbupdate index f751ba2f78..5f2fa4ebaf 100644 --- a/test/testdata/lsp/fast_path/static_field_name.1.rbupdate +++ b/test/testdata/lsp/fast_path/static_field_name.1.rbupdate @@ -6,7 +6,7 @@ class X extend T::Sig sig {params(x: Integer).void} def foo( - # ^ hover: sig {params(x: Integer).void} + # ^ hover: sig { params(x: Integer).void } # ^ hover: def foo(x); end x # ^ hover: Integer diff --git a/test/testdata/lsp/fast_path/static_field_name.rb b/test/testdata/lsp/fast_path/static_field_name.rb index 0855b9aa93..58d611ff14 100644 --- a/test/testdata/lsp/fast_path/static_field_name.rb +++ b/test/testdata/lsp/fast_path/static_field_name.rb @@ -5,7 +5,7 @@ class X extend T::Sig sig {params(x: Integer).void} def foo( - # ^ hover: sig {params(x: Integer).void} + # ^ hover: sig { params(x: Integer).void } # ^ hover: def foo(x); end x # ^ hover: Integer diff --git a/test/testdata/lsp/fast_path/static_field_superclass_resolution__use.1.rbupdate b/test/testdata/lsp/fast_path/static_field_superclass_resolution__use.1.rbupdate index b58c5f15ed..ab3b72270f 100644 --- a/test/testdata/lsp/fast_path/static_field_superclass_resolution__use.1.rbupdate +++ b/test/testdata/lsp/fast_path/static_field_superclass_resolution__use.1.rbupdate @@ -1,7 +1,7 @@ # typed: true # assert-slow-path: true -class ChildContainer < FieldContainer # error-with-dupes: Unable to resolve constant `FieldContainer` +class ChildContainer < FieldContainer # error: Unable to resolve constant `FieldContainer` def helper_method x = 6 + MEMBER_FIELD # error: Unable to resolve constant `MEMBER_FIELD` T.reveal_type(x) # error: Revealed type: `Integer` diff --git a/test/testdata/lsp/fast_path/static_init_file_locs.1.rbupdate b/test/testdata/lsp/fast_path/static_init_file_locs.1.rbupdate new file mode 100644 index 0000000000..6a15df8c4c --- /dev/null +++ b/test/testdata/lsp/fast_path/static_init_file_locs.1.rbupdate @@ -0,0 +1,6 @@ +# typed: true + +if T.unsafe(nil) + x = 1 +end +T.reveal_type(x) # error: `T.nilable(Integer)` diff --git a/test/testdata/lsp/fast_path/static_init_file_locs.rb b/test/testdata/lsp/fast_path/static_init_file_locs.rb new file mode 100644 index 0000000000..8a259f39e6 --- /dev/null +++ b/test/testdata/lsp/fast_path/static_init_file_locs.rb @@ -0,0 +1,22 @@ +# typed: true + +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. + +if T.unsafe(nil) + x = 1 +end +T.reveal_type(x) # error: `T.nilable(Integer)` diff --git a/test/testdata/lsp/fast_path/static_init_method_locs.1.rbupdate b/test/testdata/lsp/fast_path/static_init_method_locs.1.rbupdate new file mode 100644 index 0000000000..dcfcde3010 --- /dev/null +++ b/test/testdata/lsp/fast_path/static_init_method_locs.1.rbupdate @@ -0,0 +1,8 @@ +# typed: true + +class A + if T.unsafe(nil) + x = 1 + end + T.reveal_type(x) # error: `T.nilable(Integer)` +end diff --git a/test/testdata/lsp/fast_path/static_init_method_locs.rb b/test/testdata/lsp/fast_path/static_init_method_locs.rb new file mode 100644 index 0000000000..09967d3ef6 --- /dev/null +++ b/test/testdata/lsp/fast_path/static_init_method_locs.rb @@ -0,0 +1,18 @@ +# typed: true + +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. + +class A + if T.unsafe(nil) + x = 1 + end + T.reveal_type(x) # error: `T.nilable(Integer)` +end diff --git a/test/testdata/lsp/fast_path/type_alias_in_generic_class__1.1.rbupdate b/test/testdata/lsp/fast_path/type_alias_in_generic_class__1.1.rbupdate index 161c14ef2c..c16702317a 100644 --- a/test/testdata/lsp/fast_path/type_alias_in_generic_class__1.1.rbupdate +++ b/test/testdata/lsp/fast_path/type_alias_in_generic_class__1.1.rbupdate @@ -1,5 +1,5 @@ # typed: true class Box - MyInteger = T.type_alias {String} # error: Type aliases are not allowed in generic classes + MyInteger = T.type_alias {String} end diff --git a/test/testdata/lsp/fast_path/type_alias_in_generic_class__1.rb b/test/testdata/lsp/fast_path/type_alias_in_generic_class__1.rb index c0ef61cfdd..9031aa34c2 100644 --- a/test/testdata/lsp/fast_path/type_alias_in_generic_class__1.rb +++ b/test/testdata/lsp/fast_path/type_alias_in_generic_class__1.rb @@ -1,5 +1,5 @@ # typed: true class Box - MyInteger = T.type_alias {Integer} # error: Type aliases are not allowed in generic classes + MyInteger = T.type_alias {Integer} end diff --git a/test/testdata/lsp/fast_path/type_alias_in_generic_class__2.1.rbupdate b/test/testdata/lsp/fast_path/type_alias_in_generic_class__2.1.rbupdate index 73dd1aff9b..6d37b7153e 100644 --- a/test/testdata/lsp/fast_path/type_alias_in_generic_class__2.1.rbupdate +++ b/test/testdata/lsp/fast_path/type_alias_in_generic_class__2.1.rbupdate @@ -2,6 +2,12 @@ # exclude-from-file-update: true class Box + extend T::Sig extend T::Generic Elem = type_member + + sig { params(x: MyInteger).void } + def example(x) + T.reveal_type(x) # error: `String` + end end diff --git a/test/testdata/lsp/fast_path/type_alias_in_generic_class__2.rb b/test/testdata/lsp/fast_path/type_alias_in_generic_class__2.rb index 806c8d952c..0b2dc5e4a7 100644 --- a/test/testdata/lsp/fast_path/type_alias_in_generic_class__2.rb +++ b/test/testdata/lsp/fast_path/type_alias_in_generic_class__2.rb @@ -2,6 +2,12 @@ # spacer for exclude-from-file-update class Box + extend T::Sig extend T::Generic Elem = type_member + + sig { params(x: MyInteger).void } + def example(x) + T.reveal_type(x) # error: `Integer` + end end diff --git a/test/testdata/lsp/fast_path/type_alias_loc.1.rbupdate b/test/testdata/lsp/fast_path/type_alias_loc.1.rbupdate new file mode 100644 index 0000000000..0863dd0451 --- /dev/null +++ b/test/testdata/lsp/fast_path/type_alias_loc.1.rbupdate @@ -0,0 +1,6 @@ +# typed: true + +X = T.type_alias {String} + +T.reveal_type(X) # error: Revealed type: `Runtime object representing type: String` + diff --git a/test/testdata/lsp/fast_path/type_alias_loc.rb b/test/testdata/lsp/fast_path/type_alias_loc.rb new file mode 100644 index 0000000000..6d74d858a4 --- /dev/null +++ b/test/testdata/lsp/fast_path/type_alias_loc.rb @@ -0,0 +1,24 @@ +# typed: true + +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. + +X = T.type_alias {String} + +T.reveal_type(X) # error: Revealed type: `Runtime object representing type: String` diff --git a/test/testdata/lsp/fast_path/type_args_loc.1.rbupdate b/test/testdata/lsp/fast_path/type_args_loc.1.rbupdate new file mode 100644 index 0000000000..83156c9c21 --- /dev/null +++ b/test/testdata/lsp/fast_path/type_args_loc.1.rbupdate @@ -0,0 +1,10 @@ +# typed: true + +class TypeMemberLoc + extend T::Generic + X = type_member + Y = type_template +end + +T.reveal_type(TypeMemberLoc::X) # error: Revealed type: `Runtime object representing type: TypeMemberLoc::X` +T.reveal_type(TypeMemberLoc::Y) # error: Revealed type: `Runtime object representing type: T.class_of(TypeMemberLoc)::Y` diff --git a/test/testdata/lsp/fast_path/type_args_loc.rb b/test/testdata/lsp/fast_path/type_args_loc.rb new file mode 100644 index 0000000000..5b449c6c57 --- /dev/null +++ b/test/testdata/lsp/fast_path/type_args_loc.rb @@ -0,0 +1,30 @@ +# typed: true + +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. + +class TypeMemberLoc + extend T::Generic + X = type_member + Y = type_template +end + +T.reveal_type(TypeMemberLoc::X) # error: Revealed type: `Runtime object representing type: TypeMemberLoc::X` +T.reveal_type(TypeMemberLoc::Y) # error: Revealed type: `Runtime object representing type: T.class_of(TypeMemberLoc)::Y` + diff --git a/test/testdata/lsp/fast_path/type_member_redeclare_parent__1.1.rbupdate b/test/testdata/lsp/fast_path/type_member_redeclare_parent__1.1.rbupdate index 72b553f19d..371e309b71 100644 --- a/test/testdata/lsp/fast_path/type_member_redeclare_parent__1.1.rbupdate +++ b/test/testdata/lsp/fast_path/type_member_redeclare_parent__1.1.rbupdate @@ -2,7 +2,7 @@ # assert-fast-path: type_member_redeclare_parent__1.rb,type_member_redeclare_parent__2.rb # ^ ideally should also include __3 -# becuse it doesn't, the error in `__3.1.rbupdate` references the old name +# because it doesn't, the error in `__3.1.rbupdate` references the old name class Parent extend T::Sig diff --git a/test/testdata/lsp/find_implementation_overridable.rb b/test/testdata/lsp/find_implementation_overridable.rb new file mode 100644 index 0000000000..b7e9f96be4 --- /dev/null +++ b/test/testdata/lsp/find_implementation_overridable.rb @@ -0,0 +1,15 @@ +# typed: true + +class Parent + extend T::Sig + + sig {overridable.void} + def foo; end + # ^ find-implementation: foo +end + +class Child < Parent + sig {override.void} + def foo; end + # ^ implementation: foo +end diff --git a/test/testdata/lsp/genericMethods.rb b/test/testdata/lsp/genericMethods.rb index 4c3ebeb665..0eb7d68145 100644 --- a/test/testdata/lsp/genericMethods.rb +++ b/test/testdata/lsp/genericMethods.rb @@ -21,18 +21,18 @@ def main foo = Foo.new v1 = foo.id(1) # ^ hover: Integer - # ^ hover: sig {params(a: Integer).returns(Integer)} + # ^ hover: sig { params(a: Integer).returns(Integer) } # ^ usage: id v2 = foo.id("1") # ^ hover: String - # ^ hover: sig {params(a: String).returns(String)} + # ^ hover: sig { params(a: String).returns(String) } # ^ usage: id v3 = Foo.id(1) # ^ hover: Integer - # ^ hover: sig {params(a: Integer).returns(Integer)} + # ^ hover: sig { params(a: Integer).returns(Integer) } # ^ usage: staticid v4 = Foo.id("1") # ^ hover: String - # ^ hover: sig {params(a: String).returns(String)} + # ^ hover: sig { params(a: String).returns(String) } # ^ usage: staticid end diff --git a/test/testdata/lsp/highlight_untyped.rb b/test/testdata/lsp/highlight_untyped.rb index 26449354be..89917436f0 100644 --- a/test/testdata/lsp/highlight_untyped.rb +++ b/test/testdata/lsp/highlight_untyped.rb @@ -65,7 +65,7 @@ def baz "b" end -# use of super +# use of super in class class Base extend T::Sig @@ -80,6 +80,15 @@ class Derived < Base sig { override.returns(String) } def foo super + end +end + +# use of super in module +module CallsSuperInModule + extend T::Sig + sig { returns(String) } + def foo + super # ^^^^^ untyped: Value returned from method is `T.untyped` end end diff --git a/test/testdata/lsp/highlight_untyped_typed_strong.rb b/test/testdata/lsp/highlight_untyped_typed_strong.rb index 5ff4ec27e1..ca679e92b5 100644 --- a/test/testdata/lsp/highlight_untyped_typed_strong.rb +++ b/test/testdata/lsp/highlight_untyped_typed_strong.rb @@ -56,25 +56,6 @@ def baz "y" end -# use of super -class Base - extend T::Sig - - sig { overridable.returns(String) } - def foo - "foo" - end -end - -class Derived < Base - extend T::Sig - sig { override.returns(String) } - def foo - super -# ^^^^^ error: Value returned from method is `T.untyped` - end -end - # untyped varargs sig { params(args: T.untyped).void } def args_fn(*args) diff --git a/test/testdata/lsp/hover.rb b/test/testdata/lsp/hover.rb index 1d28be9f6d..0d0c04bc6c 100644 --- a/test/testdata/lsp/hover.rb +++ b/test/testdata/lsp/hover.rb @@ -33,7 +33,7 @@ def bar a.bar(1) # ^ hover: null # ^ hover: BigFoo::LittleFoo1 - # ^ hover: sig {params(num: Integer).returns(Integer)} + # ^ hover: sig { params(num: Integer).returns(Integer) } end end @@ -42,7 +42,7 @@ def self.bar(num1, num2) # ^ hover: Integer # ^ hover: String 4 + num1 + num2.to_i + @@static_variable.length - # ^ hover: sig {params(arg0: Integer).returns(Integer)} + # ^ hover: sig { params(arg0: Integer).returns(Integer) } # ^^^^^^^^^^^^^^^ hover: Docs for Bar#static_variable # ^^^^^^^^^^^^^^^ hover: String end @@ -58,7 +58,7 @@ def baz(arg) sig {params(num: Integer).returns(String)} private def quux(num) - # ^ hover: sig {params(num: Integer).returns(String)} + # ^ hover: sig { params(num: Integer).returns(String) } # ^ hover: private def quux(num); end if num < 10 s = 1 @@ -70,7 +70,7 @@ def baz(arg) sig {void} protected def protected_fun; end; - # ^ hover: sig {void} + # ^ hover: sig { void } # ^ hover: protected def protected_fun; end sig { returns([Integer, String]) } @@ -82,11 +82,18 @@ def self.anotherFunc() sig {void} def tests_return_markdown # ^^^^^^^^^^^^^^^^^^^^^ hover-line: 1 ```ruby - # ^^^^^^^^^^^^^^^^^^^^^ hover-line: 2 sig {void} + # ^^^^^^^^^^^^^^^^^^^^^ hover-line: 2 sig { void } # ^^^^^^^^^^^^^^^^^^^^^ hover-line: 4 ``` # ^^^^^^^^^^^^^^^^^^^^^ hover-line: 6 --- # ^^^^^^^^^^^^^^^^^^^^^ hover-line: 8 Tests return markdown output end + + sig {returns(T.attached_class)} + def self.factory + # ^ hover: sig { returns(T.attached_class (of BigFoo)) } + # ^ hover: def self.factory; end + new + end end module Mod @@ -94,11 +101,11 @@ module Mod def main BigFoo.bar(10, "hello") - # ^ hover: sig {params(num1: Integer, num2: String).returns(Integer)} + # ^ hover: sig { params(num1: Integer, num2: String).returns(Integer) } # ^ hover: ```ruby # Checks that we're sending Markdown. BigFoo.baz - # ^ hover: sig {void} + # ^ hover: sig { void } l = BigFoo.anotherFunc # ^ hover: [Integer, String] (2-tuple) @@ -139,10 +146,10 @@ def main # ^^^^^^ hover: The docs for BigFoo foo = BigFoo.new - # ^^^ hover: sig {params(args: T.untyped, blk: T.untyped).returns(BigFoo)} - # ^^^ hover: def new(*args, &blk); end + # ^^^ hover: sig { returns(BigFoo) } + # ^^^ hover: private def initialize; end hoo = BigFoo::LittleFoo1.new - # ^^^ hover: sig {returns(BigFoo::LittleFoo1)} + # ^^^ hover: sig { returns(BigFoo::LittleFoo1) } raise "error message" # ^ hover-line: 4 arg0: T.any(T::Class[T.anything], Exception, String) end diff --git a/test/testdata/lsp/hover_abstract.rb b/test/testdata/lsp/hover_abstract.rb index 79c659eaa3..641580f526 100644 --- a/test/testdata/lsp/hover_abstract.rb +++ b/test/testdata/lsp/hover_abstract.rb @@ -6,7 +6,7 @@ class AbstractItem sig {abstract.returns(String)} def self.name; end - # ^ hover: sig {abstract.returns(String)} + # ^ hover: sig { abstract.returns(String) } end class Dog < AbstractItem @@ -14,7 +14,7 @@ class Dog < AbstractItem sig {override.returns(String)} def self.name - # ^ hover: sig {override.returns(String)} + # ^ hover: sig { override.returns(String) } 'Dog' end end diff --git a/test/testdata/lsp/hover_alias.rb b/test/testdata/lsp/hover_alias.rb new file mode 100644 index 0000000000..784179354f --- /dev/null +++ b/test/testdata/lsp/hover_alias.rb @@ -0,0 +1,14 @@ +# typed: true + +# The documentation for `A` itself +class A; end +class Scope + # The documentation for this alias + X = A +end + +class Scope + puts(X) + # ^ hover: The documentation for this alias + # ^ hover: The documentation for `A` itself +end diff --git a/test/testdata/lsp/hover_ampersand_operations.rb b/test/testdata/lsp/hover_ampersand_operations.rb index 5a299e4791..5f055964fa 100644 --- a/test/testdata/lsp/hover_ampersand_operations.rb +++ b/test/testdata/lsp/hover_ampersand_operations.rb @@ -14,8 +14,8 @@ def main breeds = dogs.map(&:breed) # ^ hover: T::Array[String] # ^ hover: blk: T.proc.params(arg0: Dog).returns(String) - # ^ hover: sig {returns(String)} - # ^ hover: sig {returns(String)} + # ^ hover: sig { returns(String) } + # ^ hover: sig { returns(String) } # Safenav dog = Dog.new @@ -24,23 +24,23 @@ def main # ^ hover: String # ^ hover: Dog # ^ hover: Dog - # ^ hover: sig {returns(String)} - # ^ hover: sig {returns(String)} - # ^ hover: sig {returns(String)} + # ^ hover: sig { returns(String) } + # ^ hover: sig { returns(String) } + # ^ hover: sig { returns(String) } maybeDog = T.let(nil, T.nilable(Dog)) maybeBreed = maybeDog&.breed # ^ hover: T.nilable(String) # ^ hover: T.nilable(Dog) # ^ hover: NilClass - # ^ hover: sig {returns(String)} + # ^ hover: sig { returns(String) } breed2 = T.let(nil, T.nilable(String)) breed2 ||= maybeDog&.breed # ^ hover: T.nilable(String) # ^ hover: NilClass - # ^ hover: sig {returns(String)} + # ^ hover: sig { returns(String) } breed2 &&= maybeDog&.breed # ^ hover: T.nilable(String) # ^ hover: NilClass - # ^ hover: sig {returns(String)} + # ^ hover: sig { returns(String) } end diff --git a/test/testdata/lsp/hover_block.rb b/test/testdata/lsp/hover_block.rb new file mode 100644 index 0000000000..57f12cbb4b --- /dev/null +++ b/test/testdata/lsp/hover_block.rb @@ -0,0 +1,34 @@ +# typed: strict +extend T::Sig + +sig {params(classes: T::Array[T.untyped]).returns(T::Array[[T.nilable(Integer), T.nilable(String)]])} +def f(classes) + result = classes.map do |cls| +# ^ hover: T::Array[[T.nilable(Integer), T.nilable(String)]] + begin + cls.call_func + + [nil, "foo"] + rescue => e + [6, nil] + end + end + + T.reveal_type(result) # error: Revealed type: `T::Array[[T.nilable(Integer), T.nilable(String)]]` + result +end + +class Parent; end + +sig {params(classes: T::Array[Module]).void} +def example(classes) + thing = + if classes.empty? + T.unsafe(nil) ? [Hash] : [] + else + classes + end + T.reveal_type(thing) # error: Revealed type: `T::Array[Module]` + thing + # ^ hover: T::Array[Module] +end diff --git a/test/testdata/lsp/hover_call_block_splat.rb b/test/testdata/lsp/hover_call_block_splat.rb index d36437f3e3..e9584d315c 100644 --- a/test/testdata/lsp/hover_call_block_splat.rb +++ b/test/testdata/lsp/hover_call_block_splat.rb @@ -9,9 +9,9 @@ def foo(&blk); end sig {params(blk: T.proc.void).void} def baz(&blk) foo(&blk) - # ^ hover: sig {params(blk: T.proc.void).void} + # ^ hover: sig { params(blk: T.proc.void).void } foo {} - # ^ hover: sig {params(blk: T.proc.void).void} + # ^ hover: sig { params(blk: T.proc.void).void } end sig {params(args: Integer).void} @@ -20,7 +20,7 @@ def splat_fun(*args); end sig {params(x: Integer).void} def call_splat_fun(x) splat_fun(*[x, x]) - # ^ hover: sig {params(args: Integer).void} + # ^ hover: sig { params(args: Integer).void } # ^ hover: def splat_fun(*args); end end end diff --git a/test/testdata/lsp/hover_generics.rb b/test/testdata/lsp/hover_generics.rb index 127b66d5f6..9e1b00e512 100644 --- a/test/testdata/lsp/hover_generics.rb +++ b/test/testdata/lsp/hover_generics.rb @@ -11,5 +11,5 @@ def read end Box[Integer].new.read - # ^ hover: sig {returns(Integer)} + # ^ hover: sig { returns(Integer) } [1].map{|x| x + 1} diff --git a/test/testdata/lsp/hover_method.rb b/test/testdata/lsp/hover_method.rb index e5f2b11c88..2da74168e1 100644 --- a/test/testdata/lsp/hover_method.rb +++ b/test/testdata/lsp/hover_method.rb @@ -4,26 +4,26 @@ class Foo sig {returns(Integer)} def bar - # ^ hover: sig {returns(Integer)} + # ^ hover: sig { returns(Integer) } # N.B. Checking two positions on below function call as they used to return different strings. baz("1") - # ^ hover: sig {params(arg0: String).returns(Integer)} - # ^ hover: sig {params(arg0: String).returns(Integer)} + # ^ hover: sig { params(arg0: String).returns(Integer) } + # ^ hover: sig { params(arg0: String).returns(Integer) } end # Docs for static bar sig {params(a: String).void} def self.bar(a) - # ^ hover: sig {params(a: String).void} + # ^ hover: sig { params(a: String).void } end sig {params(arg0: String).returns(Integer)} def baz(arg0) no_args_and_void - # ^ hover: sig {void} + # ^ hover: sig { void } Foo::bat(1) # ^ hover: T.class_of(Foo) - # ^ hover: sig {params(i: Integer).returns(Integer)} + # ^ hover: sig { params(i: Integer).returns(Integer) } # ^ hover: Integer(1) arg0.to_i end @@ -37,17 +37,17 @@ def self.bat(i) sig {params(arg0: Integer).void} def typed_with_docs(arg0) # ^ hover: Docs above single-line sig - # ^ hover: sig {params(arg0: Integer).void} + # ^ hover: sig { params(arg0: Integer).void } end # Some docs for qux sig {void} def qux # ^^^ hover: Some docs for qux - # ^^^ hover: sig {void} + # ^^^ hover: sig { void } typed_with_docs(1) # ^ hover: Docs above single-line sig - # ^ hover: sig {params(arg0: Integer).void} + # ^ hover: sig { params(arg0: Integer).void } end sig {void} @@ -64,7 +64,7 @@ def always_raises sig {params(blk: T.proc.params(arg0: Integer, arg1: String).returns(String)).returns(String)} def self.blk_arg(&blk) yield(1, "hello") - # ^ hover: sig {params(arg0: Integer, arg1: String).returns(String)} + # ^ hover: sig { params(arg0: Integer, arg1: String).returns(String) } end sig {params(a: String, x: String).returns(T::Array[String])} @@ -79,7 +79,7 @@ def self.splat_arg(a, *x) # Docs are below the sig def docs_below_sig # ^^^^^^^^^^^^^^ hover: Docs are below the sig - # ^^^^^^^^^^^^^^ hover: sig {void} + # ^^^^^^^^^^^^^^ hover: sig { void } end end @@ -91,17 +91,17 @@ def main # ^ hover: String rv2 = Foo.splat_arg("a", "b", "c") # ^ hover: T::Array[String] - # ^ hover: sig {params(a: String, x: String).returns(T::Array[String])} + # ^ hover: sig { params(a: String, x: String).returns(T::Array[String]) } Foo.bar('') # ^^^ hover: T.class_of(Foo) # ^^^ hover: Docs for static bar - # ^^^ hover: sig {params(a: String).void} + # ^^^ hover: sig { params(a: String).void } f = Foo.new # ^ hover: Foo # ^ hover: T.class_of(Foo) f.qux # ^^^ hover: Some docs for qux - # ^^^ hover: sig {void} + # ^^^ hover: sig { void } end diff --git a/test/testdata/lsp/hover_operator_overload.rb b/test/testdata/lsp/hover_operator_overload.rb index 14370b3f6a..8d0e0813ca 100644 --- a/test/testdata/lsp/hover_operator_overload.rb +++ b/test/testdata/lsp/hover_operator_overload.rb @@ -5,7 +5,7 @@ class Dog sig {returns(String)} attr_reader :name -# ^ hover: sig {returns(String)} +# ^ hover: sig { returns(String) } sig {params(name: String).void} def initialize(name) @@ -32,12 +32,12 @@ def ==(eqDog) def main fred = Dog.new('fred') di = Dog.new('di') - freddi = fred + di + freddi = fred + di # ^ sig {params(plusDog: Dog).returns(Dog)} - - fred < di + + fred < di # ^ sig {params(ltDog: Dog).returns(Boolean)} - fred == di + fred == di # ^ sig {params(eqDog: Dog).returns(Boolean)} end diff --git a/test/testdata/lsp/hover_override.rb b/test/testdata/lsp/hover_override.rb index 9049ab5abb..a0877bd9c1 100644 --- a/test/testdata/lsp/hover_override.rb +++ b/test/testdata/lsp/hover_override.rb @@ -4,7 +4,7 @@ class Animal sig {overridable.returns(String)} def self.name; "Animal"; end - # ^ hover: sig {overridable.returns(String)} + # ^ hover: sig { overridable.returns(String) } end class Dog < Animal @@ -12,7 +12,7 @@ class Dog < Animal sig {override.returns(String)} def self.name - # ^ hover: sig {override.returns(String)} + # ^ hover: sig { override.returns(String) } 'Dog' end end diff --git a/test/testdata/lsp/hover_proc_void.rb b/test/testdata/lsp/hover_proc_void.rb index d356a0e6a0..8bfa3a1e9f 100644 --- a/test/testdata/lsp/hover_proc_void.rb +++ b/test/testdata/lsp/hover_proc_void.rb @@ -4,6 +4,6 @@ sig {params(blk: T.proc.void).void} def proc_void(&blk) - # ^ hover: sig {params(blk: T.proc.void).void} + # ^ hover: sig { params(blk: T.proc.void).void } # ^ hover: private def proc_void(&blk); end end diff --git a/test/testdata/lsp/hover_rescue.rb b/test/testdata/lsp/hover_rescue.rb new file mode 100644 index 0000000000..d5939763e5 --- /dev/null +++ b/test/testdata/lsp/hover_rescue.rb @@ -0,0 +1,10 @@ +# typed: true +extend T::Sig + +begin + 5 +rescue TypeError => e + # ^ hover: TypeError + T.reveal_type(e) # error: `TypeError` + raise +end diff --git a/test/testdata/lsp/hover_static_field_alias_to_nothing.rb b/test/testdata/lsp/hover_static_field_alias_to_nothing.rb new file mode 100644 index 0000000000..b330bbc8bb --- /dev/null +++ b/test/testdata/lsp/hover_static_field_alias_to_nothing.rb @@ -0,0 +1,7 @@ +# typed: strict + +SomeConstant = DoesNotExist +# ^^^^^^^^^^^^ error: Unable to resolve constant `DoesNotExist` + +p(SomeConstant) +# ^ hover: SomeConstant = (unable to resolve constant) diff --git a/test/testdata/lsp/hover_typed_false.rb b/test/testdata/lsp/hover_typed_false.rb index 4d7215913d..ab92e738b5 100644 --- a/test/testdata/lsp/hover_typed_false.rb +++ b/test/testdata/lsp/hover_typed_false.rb @@ -1,15 +1,18 @@ # typed: false class Foo - # ^ hover: This file is `# typed: false`. - # ^ hover: Hover, Go To Definition, and other features are disabled in this file. + # ^ hover: T.class_of(Foo) def foo - # ^ hover: This file is `# typed: false`. - # ^ hover: Hover, Go To Definition, and other features are disabled in this file. + # ^ hover: sig { returns(T.untyped) } + # ^ hover: def foo; end x = 10 # ^ hover: This file is `# typed: false`. - # ^ hover: Hover, Go To Definition, and other features are disabled in this file. + # ^ hover: Most Hover results will not appear until the file is `# typed: true` or higher. x end + + X = 1 + p(X) + # ^ hover: Integer end diff --git a/test/testdata/lsp/hover_typed_ignore.rb b/test/testdata/lsp/hover_typed_ignore.rb index b92994f8ef..f403dcdfb2 100644 --- a/test/testdata/lsp/hover_typed_ignore.rb +++ b/test/testdata/lsp/hover_typed_ignore.rb @@ -2,14 +2,14 @@ class Foo # ^ hover: This file is `# typed: ignore`. - # ^ hover: Hover, Go To Definition, and other features are disabled in this file. + # ^ hover: No Sorbet IDE features will work in this file. def foo # ^ hover: This file is `# typed: ignore`. - # ^ hover: Hover, Go To Definition, and other features are disabled in this file. + # ^ hover: No Sorbet IDE features will work in this file. x = 10 # ^ hover: This file is `# typed: ignore`. - # ^ hover: Hover, Go To Definition, and other features are disabled in this file. + # ^ hover: No Sorbet IDE features will work in this file. x end end diff --git a/test/testdata/lsp/hover_typed_no_sigil.rb b/test/testdata/lsp/hover_typed_no_sigil.rb index b7ec9bf574..6810245905 100644 --- a/test/testdata/lsp/hover_typed_no_sigil.rb +++ b/test/testdata/lsp/hover_typed_no_sigil.rb @@ -1,13 +1,16 @@ class Foo - # ^ hover: This file is `# typed: false`. - # ^ hover: Hover, Go To Definition, and other features are disabled in this file. + # ^ hover: T.class_of(Foo) def foo - # ^ hover: This file is `# typed: false`. - # ^ hover: Hover, Go To Definition, and other features are disabled in this file. + # ^ hover: sig { returns(T.untyped) } + # ^ hover: def foo; end x = 10 # ^ hover: This file is `# typed: false`. - # ^ hover: Hover, Go To Definition, and other features are disabled in this file. + # ^ hover: Most Hover results will not appear until the file is `# typed: true` or higher. x end + + X = 1 + p(X) + # ^ hover: Integer end diff --git a/test/testdata/lsp/hover_undefined_constant.rb b/test/testdata/lsp/hover_undefined_constant.rb index 52bbfacbb6..7f461f49e8 100644 --- a/test/testdata/lsp/hover_undefined_constant.rb +++ b/test/testdata/lsp/hover_undefined_constant.rb @@ -3,6 +3,6 @@ class MyClass def foo(x) x + THE_CONSTANT # error: Unable to resolve constant - # ^ hover: This constant is not defined + # ^ hover: (unable to resolve constant) end end diff --git a/test/testdata/lsp/jump_to_def_override.rb b/test/testdata/lsp/jump_to_def_override.rb new file mode 100644 index 0000000000..e74eb35b20 --- /dev/null +++ b/test/testdata/lsp/jump_to_def_override.rb @@ -0,0 +1,30 @@ +# typed: true + +class Parent + extend T::Sig + extend T::Helpers + + abstract! + + sig {abstract.void} + def example1 +# ^ def: Child#example1 + end + + sig {overridable.void} + def example2 +# ^ def: Child#example2 + end +end + +class Child < Parent + sig {override.void} + # ^ go-to-def-special: Child#example1 + def example1 + end + + sig {override.void} + # ^ go-to-def-special: Child#example2 + def example2 + end +end diff --git a/test/testdata/lsp/prop_factory_jump_to_def.rb b/test/testdata/lsp/prop_factory_jump_to_def.rb index f154d283a3..76a1d0d5f9 100644 --- a/test/testdata/lsp/prop_factory_jump_to_def.rb +++ b/test/testdata/lsp/prop_factory_jump_to_def.rb @@ -10,6 +10,6 @@ def self.bar prop :foo, String, factory: -> {A.bar} # ^ usage: self_bar - # ^ hover: sig {void} + # ^ hover: sig { void } # ^ hover: def self.bar; end end diff --git a/test/testdata/lsp/prop_references.rb b/test/testdata/lsp/prop_references.rb new file mode 100644 index 0000000000..a1cbd1fdd0 --- /dev/null +++ b/test/testdata/lsp/prop_references.rb @@ -0,0 +1,50 @@ +# typed: strict +class Module; include T::Sig; end + +class A < T::InexactStruct + const :some_const, String + # ^^^^^^^^^^ def: A#some_const + + prop :some_prop, String + # ^^^^^^^^^ def: A#some_prop + + prop :foreign_b, T.nilable(String), foreign: -> { B } + # ^^^^^^^^^ def: A#foreign_b + # ^^^^^^^ def: A#foreign_b_ +end + +a = A.new(some_const: '', some_prop: '') +a.some_const +# ^^^^^^^^^^ usage: A#some_const +a.some_prop=('') +# ^^^^^^^^^^ usage: A#some_prop +a.some_prop +# ^^^^^^^^^ usage: A#some_prop +a.foreign_b_ +# ^^^^^^^^^^ usage: A#foreign_b_ + +class B < T::Struct + const :some_const, String + # ^^^^^^^^^^ def: B#some_const + # ^^^^^^^^^^ usage: B#some_const + + prop :some_prop, String + # ^^^^^^^^^ def: B#some_prop + # ^^^^^^^^^ usage: B#some_prop + + sig { void } + def example + p(@some_const) + # ^^^^^^^^^^^ usage: B#some_const + p(@some_prop) + # ^^^^^^^^^^ usage: B#some_prop + end +end + +b = B.new(some_const: '', some_prop: '') +b.some_const +# ^^^^^^^^^^ usage: B#some_const +b.some_prop=('') +# ^^^^^^^^^^ usage: B#some_prop +b.some_prop +# ^^^^^^^^^ usage: B#some_prop diff --git a/test/testdata/lsp/queries_in_rbi.A.rbedited b/test/testdata/lsp/queries_in_rbi.A.rbedited new file mode 100644 index 0000000000..9856ebf265 --- /dev/null +++ b/test/testdata/lsp/queries_in_rbi.A.rbedited @@ -0,0 +1,35 @@ +# typed: true + +class A + # There's no `T::Sig` in scope here, because the user forgot or didn't want + # to put `extend T::Sig` (not required in RBI files), which means we can't + # give autocompletion results. + # + # NOTE that codebases which have `class Module; include T::Sig; end`, are able + # to get sig completion here + + sig + # ^ completion: (nothing) + def foo; end +end + +class B + extend T::Sig + + sig { params(x: ${1:T.untyped}).returns(${2:T.untyped}) }${0} + # ^ completion: sig + # ^ apply-completion: [A] item: 0 + def foo(x); end +end + +class C + sig {returns(T.noreturn)} + # ^ hover: For more information, see https://sorbet.org/docs/noreturn + def always_raise; end +end + +class D + sig {returns(T.noret)} # error: Unsupported method `T.noret` + # ^ completion: noreturn + def foo; end +end diff --git a/test/testdata/lsp/queries_in_rbi.rbi b/test/testdata/lsp/queries_in_rbi.rbi new file mode 100644 index 0000000000..3b86e0650a --- /dev/null +++ b/test/testdata/lsp/queries_in_rbi.rbi @@ -0,0 +1,35 @@ +# typed: true + +class A + # There's no `T::Sig` in scope here, because the user forgot or didn't want + # to put `extend T::Sig` (not required in RBI files), which means we can't + # give autocompletion results. + # + # NOTE that codebases which have `class Module; include T::Sig; end`, are able + # to get sig completion here + + sig + # ^ completion: (nothing) + def foo; end +end + +class B + extend T::Sig + + sig + # ^ completion: sig + # ^ apply-completion: [A] item: 0 + def foo(x); end +end + +class C + sig {returns(T.noreturn)} + # ^ hover: For more information, see https://sorbet.org/docs/noreturn + def always_raise; end +end + +class D + sig {returns(T.noret)} # error: Unsupported method `T.noret` + # ^ completion: noreturn + def foo; end +end diff --git a/test/testdata/lsp/rename/overloads_test.A.rbedited b/test/testdata/lsp/rename/overloads_test.A.rbedited index c5339ed99e..4a09bad7a8 100644 --- a/test/testdata/lsp/rename/overloads_test.A.rbedited +++ b/test/testdata/lsp/rename/overloads_test.A.rbedited @@ -8,7 +8,7 @@ class A extend T::Sig sig {params(x: I).void} sig {params(target: S).void} - def my_method(target) + def my_method(target) # error: against an overloaded signature # ^ apply-rename: [A] newName: target placeholderText: x puts(target) end diff --git a/test/testdata/lsp/rename/overloads_test.rb b/test/testdata/lsp/rename/overloads_test.rb index d1cc49bef7..bb6ec494d4 100644 --- a/test/testdata/lsp/rename/overloads_test.rb +++ b/test/testdata/lsp/rename/overloads_test.rb @@ -8,7 +8,7 @@ class A extend T::Sig sig {params(x: I).void} sig {params(x: S).void} - def my_method(x) + def my_method(x) # error: against an overloaded signature # ^ apply-rename: [A] newName: target placeholderText: x puts(x) end diff --git a/test/testdata/lsp/struct_fuzz.rb b/test/testdata/lsp/struct_fuzz.rb deleted file mode 100644 index e456e878e7..0000000000 --- a/test/testdata/lsp/struct_fuzz.rb +++ /dev/null @@ -1,10 +0,0 @@ -# typed: true -# no-stdlib: true - ::B=Struct.new:x # error-with-dupes: Method `keep_for_ide` does not exist on `T.class_of(Sorbet::Private::Static)` -# ^^^^^^^^^^^^^^^^ error: Method `type_member` does not exist on `T.class_of(B)` -# ^^^^^^^^^^^^^^^^ error: Method `params` does not exist on `T.class_of(B)` -# ^^^^^^^^^^^^^^^^ error: Method `unsafe` does not exist on `T.class_of(T)` -# ^^^^^^^^^^^^^^^^ error: Method `unsafe` does not exist on `T.class_of(T)` -# ^^^^^^^^^^^^^^^^ error: Method `unsafe` does not exist on `T.class_of(T)` -# these errors aren't actually what we're checking for, we just want to make sure sorbet doesn't crash on this input -# when no-stdlib is true. diff --git a/test/testdata/minimize-rbi/abstract.rb b/test/testdata/minimize-rbi/abstract.rb index 7beedc5b28..7b6ac3838c 100644 --- a/test/testdata/minimize-rbi/abstract.rb +++ b/test/testdata/minimize-rbi/abstract.rb @@ -14,7 +14,6 @@ module CardPaymentRecord # error: Missing definition for abstract method `Paymen end class Card -# ^^^^^^^^^^ error: Missing definition for abstract method `PaymentRecord#fx_currency` # ^^^^^^^^^^ error: Missing definition for abstract method `PaymentRecord#fx_currency` include CardPaymentRecord end diff --git a/test/testdata/minimize-rbi/algolia.rb.minimize.rbi b/test/testdata/minimize-rbi/algolia.rb.minimize.rbi deleted file mode 100644 index c8a547d7e3..0000000000 --- a/test/testdata/minimize-rbi/algolia.rb.minimize.rbi +++ /dev/null @@ -1,4 +0,0 @@ -# typed: true - -class Algolia::Recommend::Model -end diff --git a/test/testdata/minimize-rbi/algolia.rb.minimized-rbi.exp b/test/testdata/minimize-rbi/algolia.rb.minimized-rbi.exp deleted file mode 100644 index 9625fa9f23..0000000000 --- a/test/testdata/minimize-rbi/algolia.rb.minimized-rbi.exp +++ /dev/null @@ -1,11 +0,0 @@ -# typed: true - -module ::Algolia -end - -module ::Algolia::Recommend -end - -class ::Algolia::Recommend::Model -end - diff --git a/test/testdata/minimize-rbi/algolia.rb b/test/testdata/minimize-rbi/external.rb similarity index 100% rename from test/testdata/minimize-rbi/algolia.rb rename to test/testdata/minimize-rbi/external.rb diff --git a/test/testdata/minimize-rbi/external.rb.minimize.rbi b/test/testdata/minimize-rbi/external.rb.minimize.rbi new file mode 100644 index 0000000000..d36d41bd57 --- /dev/null +++ b/test/testdata/minimize-rbi/external.rb.minimize.rbi @@ -0,0 +1,10 @@ +# typed: true + +class Algolia::Recommend::Model +end + +class Plaid::Models::BaseModel +end + +class Presto::Models::BaseModel +end diff --git a/test/testdata/minimize-rbi/external.rb.minimized-rbi.exp b/test/testdata/minimize-rbi/external.rb.minimized-rbi.exp new file mode 100644 index 0000000000..6530f47e78 --- /dev/null +++ b/test/testdata/minimize-rbi/external.rb.minimized-rbi.exp @@ -0,0 +1,29 @@ +# typed: true + +module ::Algolia +end + +module ::Algolia::Recommend +end + +class ::Algolia::Recommend::Model +end + +module ::Plaid +end + +module ::Plaid::Models +end + +class ::Plaid::Models::BaseModel +end + +module ::Presto +end + +module ::Presto::Models +end + +class ::Presto::Models::BaseModel +end + diff --git a/test/testdata/minimize-rbi/plaid.rb b/test/testdata/minimize-rbi/plaid.rb deleted file mode 100644 index e3aba1c868..0000000000 --- a/test/testdata/minimize-rbi/plaid.rb +++ /dev/null @@ -1,2 +0,0 @@ -# typed: true - diff --git a/test/testdata/minimize-rbi/plaid.rb.minimize.rbi b/test/testdata/minimize-rbi/plaid.rb.minimize.rbi deleted file mode 100644 index 71fc1f5b78..0000000000 --- a/test/testdata/minimize-rbi/plaid.rb.minimize.rbi +++ /dev/null @@ -1,5 +0,0 @@ -# typed: true - -class Plaid::Models::BaseModel - def foo; end -end diff --git a/test/testdata/minimize-rbi/plaid.rb.minimized-rbi.exp b/test/testdata/minimize-rbi/plaid.rb.minimized-rbi.exp deleted file mode 100644 index 4ce48f1a4a..0000000000 --- a/test/testdata/minimize-rbi/plaid.rb.minimized-rbi.exp +++ /dev/null @@ -1,12 +0,0 @@ -# typed: true - -module ::Plaid -end - -module ::Plaid::Models -end - -class ::Plaid::Models::BaseModel - def foo(); end -end - diff --git a/test/testdata/minimize-rbi/private.rb b/test/testdata/minimize-rbi/private.rb new file mode 100644 index 0000000000..c5ca714cb1 --- /dev/null +++ b/test/testdata/minimize-rbi/private.rb @@ -0,0 +1 @@ +# typed: true diff --git a/test/testdata/minimize-rbi/private.rb.minimize.rbi b/test/testdata/minimize-rbi/private.rb.minimize.rbi new file mode 100644 index 0000000000..5b4a60b9f5 --- /dev/null +++ b/test/testdata/minimize-rbi/private.rb.minimize.rbi @@ -0,0 +1,5 @@ +# typed: true + +class Foo + private def private_method; end +end diff --git a/test/testdata/minimize-rbi/private.rb.minimized-rbi.exp b/test/testdata/minimize-rbi/private.rb.minimized-rbi.exp new file mode 100644 index 0000000000..1767253f16 --- /dev/null +++ b/test/testdata/minimize-rbi/private.rb.minimized-rbi.exp @@ -0,0 +1,6 @@ +# typed: true + +class ::Foo + private def private_method(); end +end + diff --git a/test/testdata/namer/alias_cross_file.flatten-tree.exp b/test/testdata/namer/alias_cross_file.flatten-tree.exp index 20c46f0cd4..44308aafa6 100644 --- a/test/testdata/namer/alias_cross_file.flatten-tree.exp +++ b/test/testdata/namer/alias_cross_file.flatten-tree.exp @@ -1,12 +1,9 @@ begin - class <>> < (::) + + def self.<$CENSORED>() - begin - - ::Sorbet::Private::Static.keep_for_ide(::TestSig) - - end + end end class ::TestSig<> < (::) @@ -27,18 +24,11 @@ begin end begin - class <>> < (::) + + def self.<$CENSORED>() - begin - begin - - ::Sorbet::Private::Static.keep_for_ide(::T1) - - end - ::T2 = ::T1 - - end + ::T2 = ::T1 end end class ::T1<> < (::) diff --git a/test/testdata/namer/alias_method.rb b/test/testdata/namer/alias_method.rb index d26f2fbe88..f99486f11a 100644 --- a/test/testdata/namer/alias_method.rb +++ b/test/testdata/namer/alias_method.rb @@ -10,8 +10,8 @@ def f alias_method :f # ^^ error: Not enough arguments provided for method `Module#alias_method`. Expected: `2`, got: `1` alias_method :f, 8 # error: Expected `Symbol` but found `Integer(8)` for argument `old_name` - alias_method :bad_alias, :nonexistant_method - # ^^^^^^^^^^^^^^^^^^^ error: Can't make method alias from `bad_alias` to non existing method `nonexistant_method` + alias_method :bad_alias, :nonexistent_method + # ^^^^^^^^^^^^^^^^^^^ error: Can't make method alias from `bad_alias` to non existing method `nonexistent_method` def alias_user f_alias_1 diff --git a/test/testdata/namer/alias_method.rb.symbol-table-raw.exp b/test/testdata/namer/alias_method.rb.symbol-table-raw.exp index 20982a1d21..740a44e14d 100644 --- a/test/testdata/namer/alias_method.rb.symbol-table-raw.exp +++ b/test/testdata/namer/alias_method.rb.symbol-table-raw.exp @@ -5,13 +5,13 @@ class >> < > () module > < >::>::>::> () @ Loc {file=test/testdata/namer/alias_method.rb start=2:1 end=2:13} method ># () @ Loc {file=test/testdata/namer/alias_method.rb start=16:3 end=16:17} argument @ Loc {file=test/testdata/namer/alias_method.rb start=??? end=???} - method ># (nonexistant_method) -> AliasType { symbol = >::>::>#> } @ Loc {file=test/testdata/namer/alias_method.rb start=13:3 end=13:47} - argument nonexistant_method @ Loc {file=test/testdata/namer/alias_method.rb start=13:28 end=13:47} + method ># (nonexistent_method) -> AliasType { symbol = >::>::>#> } @ Loc {file=test/testdata/namer/alias_method.rb start=13:16 end=13:26} + argument nonexistent_method @ Loc {file=test/testdata/namer/alias_method.rb start=13:28 end=13:47} method ># () @ Loc {file=test/testdata/namer/alias_method.rb start=3:3 end=3:8} argument @ Loc {file=test/testdata/namer/alias_method.rb start=??? end=???} - method ># (f) -> AliasType { symbol = ># } @ Loc {file=test/testdata/namer/alias_method.rb start=6:3 end=6:18} + method ># (f) -> AliasType { symbol = ># } @ Loc {file=test/testdata/namer/alias_method.rb start=6:9 end=6:16} argument f @ Loc {file=test/testdata/namer/alias_method.rb start=6:17 end=6:18} - method ># (f) -> AliasType { symbol = ># } @ Loc {file=test/testdata/namer/alias_method.rb start=7:3 end=7:30} + method ># (f) -> AliasType { symbol = ># } @ Loc {file=test/testdata/namer/alias_method.rb start=7:16 end=7:26} argument f @ Loc {file=test/testdata/namer/alias_method.rb start=7:28 end=7:30} class > $1> < > () @ Loc {file=test/testdata/namer/alias_method.rb start=2:1 end=2:13} method > $1>#> () @ Loc {file=test/testdata/namer/alias_method.rb start=2:1 end=21:4} diff --git a/test/testdata/namer/ancestors.rb.flatten-tree.exp b/test/testdata/namer/ancestors.rb.flatten-tree.exp index 2636b8599a..f69bc5ffc1 100644 --- a/test/testdata/namer/ancestors.rb.flatten-tree.exp +++ b/test/testdata/namer/ancestors.rb.flatten-tree.exp @@ -1,36 +1,17 @@ begin - class <>> < (::) + + + + + + + + + + def self.<$CENSORED>() - begin - begin - - ::Sorbet::Private::Static.keep_for_ide(::Mixin1) - - end - begin - - ::Sorbet::Private::Static.keep_for_ide(::Mixin2) - - end - begin - - ::Sorbet::Private::Static.keep_for_ide(::Parent) - - end - begin - - ::Sorbet::Private::Static.keep_for_ide(::Child) - ::Sorbet::Private::Static.keep_for_ide(::Parent) - - end - begin - - ::Sorbet::Private::Static.keep_for_ide(::MultipleInclude) - - end - - end + end end module ::Mixin1<> < () diff --git a/test/testdata/namer/arguments.rb.flatten-tree-raw.exp b/test/testdata/namer/arguments.rb.flatten-tree-raw.exp index baa74300af..d5e2767f27 100644 --- a/test/testdata/namer/arguments.rb.flatten-tree-raw.exp +++ b/test/testdata/namer/arguments.rb.flatten-tree-raw.exp @@ -1,6 +1,5 @@ InsSeq{ stats = [ - EmptyTree ClassDef{ kind = class name = EmptyTree @@ -10,37 +9,15 @@ InsSeq{ orig = nullptr }] rhs = [ + EmptyTree + MethodDef{ flags = {self} name = ><> $CENSORED>> args = [Local{ localVariable = > }] - rhs = InsSeq{ - stats = [ - EmptyTree - Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::Sorbet::Private::Static) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (class ::A) - orig = UnresolvedConstantLit{ - cnst = > - scope = EmptyTree - } - } - ] - } - ], - expr = EmptyTree - } + rhs = EmptyTree } ] } diff --git a/test/testdata/namer/arguments.rb.flatten-tree.exp b/test/testdata/namer/arguments.rb.flatten-tree.exp index c8288161f9..1a2173b9c8 100644 --- a/test/testdata/namer/arguments.rb.flatten-tree.exp +++ b/test/testdata/namer/arguments.rb.flatten-tree.exp @@ -1,12 +1,9 @@ begin - class <>> < (::) + + def self.<$CENSORED>() - begin - - ::Sorbet::Private::Static.keep_for_ide(::A) - - end + end end class ::A<> < (::) diff --git a/test/testdata/namer/better_arg_loc.rb b/test/testdata/namer/better_arg_loc.rb index 9993cd53ab..ffc3c30c6a 100644 --- a/test/testdata/namer/better_arg_loc.rb +++ b/test/testdata/namer/better_arg_loc.rb @@ -3,7 +3,7 @@ class ::Integer # This used to accidentally overwrite the loc of Integer#times's block # argument to have an empty Loc. - def times + def times # error: Refusing to typecheck `Integer#times` against an overloaded signature end end diff --git a/test/testdata/namer/circular_mixin.rb.symbol-table-raw.exp b/test/testdata/namer/circular_mixin.rb.symbol-table-raw.exp index 45e86188e3..7675eaa8a6 100644 --- a/test/testdata/namer/circular_mixin.rb.symbol-table-raw.exp +++ b/test/testdata/namer/circular_mixin.rb.symbol-table-raw.exp @@ -2,12 +2,12 @@ class >> < > () class >> $1>[>>] < > $1> () method >> $1>#> $CENSORED> () @ Loc {file=test/testdata/namer/circular_mixin.rb start=2:1 end=13:4} argument @ Loc {file=test/testdata/namer/circular_mixin.rb start=??? end=???} - module > < >::>::>::> (>, >) @ Loc {file=test/testdata/namer/circular_mixin.rb start=11:1 end=11:9} + module > < >::>::>::> (>, >>) @ Loc {file=test/testdata/namer/circular_mixin.rb start=11:1 end=11:9} class > $1> < > () @ Loc {file=test/testdata/namer/circular_mixin.rb start=11:1 end=11:9} - method > $1>#> () @ Loc {file=test/testdata/namer/circular_mixin.rb start=2:1 end=2:14} + method > $1>#> () @ Loc {file=test/testdata/namer/circular_mixin.rb start=11:1 end=13:4} argument @ Loc {file=test/testdata/namer/circular_mixin.rb start=??? end=???} - module > < >::>::>::> (>) @ Loc {file=test/testdata/namer/circular_mixin.rb start=7:1 end=7:9} + module > < >::>::>::> (>>) @ Loc {file=test/testdata/namer/circular_mixin.rb start=7:1 end=7:9} class > $1> < > () @ Loc {file=test/testdata/namer/circular_mixin.rb start=7:1 end=7:9} - method > $1>#> () @ Loc {file=test/testdata/namer/circular_mixin.rb start=3:1 end=3:14} + method > $1>#> () @ Loc {file=test/testdata/namer/circular_mixin.rb start=7:1 end=9:4} argument @ Loc {file=test/testdata/namer/circular_mixin.rb start=??? end=???} diff --git a/test/testdata/namer/class_and_alias.rb.flatten-tree.exp b/test/testdata/namer/class_and_alias.rb.flatten-tree.exp index d4eb5375ed..91c464e8bd 100644 --- a/test/testdata/namer/class_and_alias.rb.flatten-tree.exp +++ b/test/testdata/namer/class_and_alias.rb.flatten-tree.exp @@ -1,19 +1,12 @@ begin - class <>> < (::) + + + + def self.<$CENSORED>() begin ::A = 91 - begin - - ::Sorbet::Private::Static.keep_for_ide(::A) - - end - begin - - ::Sorbet::Private::Static.keep_for_ide(::B) - - end ::B = 91 end diff --git a/test/testdata/namer/conflicting_names.rb.flatten-tree.exp b/test/testdata/namer/conflicting_names.rb.flatten-tree.exp index 0dd0f454ae..ad72634748 100644 --- a/test/testdata/namer/conflicting_names.rb.flatten-tree.exp +++ b/test/testdata/namer/conflicting_names.rb.flatten-tree.exp @@ -1,12 +1,9 @@ begin - class <>> < (::) + + def self.<$CENSORED>() - begin - - ::Sorbet::Private::Static.keep_for_ide(::A) - - end + end end module ::A<> < () @@ -14,16 +11,10 @@ begin end + + def self.() - begin - - begin - - ::Sorbet::Private::Static.keep_for_ide(::A::Foo) - - end - - end + end end class ::A::Foo<> < (::) diff --git a/test/testdata/namer/constants.rb.flatten-tree.exp b/test/testdata/namer/constants.rb.flatten-tree.exp index e4e1dd1beb..f2a67978de 100644 --- a/test/testdata/namer/constants.rb.flatten-tree.exp +++ b/test/testdata/namer/constants.rb.flatten-tree.exp @@ -1,13 +1,9 @@ begin - class <>> < (::) + + def self.<$CENSORED>() begin - begin - - ::Sorbet::Private::Static.keep_for_ide(::A) - - end ::A::B::C ::A::B::D ::A @@ -16,16 +12,10 @@ begin end end module ::A<> < () + + def self.() - begin - ::A::B::C = 1 - begin - - ::Sorbet::Private::Static.keep_for_ide(::A::B) - - end - - end + ::A::B::C = 1 end end module ::A::B<> < () diff --git a/test/testdata/namer/defs_in_blocks.rb.flatten-tree.exp b/test/testdata/namer/defs_in_blocks.rb.flatten-tree.exp index 8b66d10563..91bfeba772 100644 --- a/test/testdata/namer/defs_in_blocks.rb.flatten-tree.exp +++ b/test/testdata/namer/defs_in_blocks.rb.flatten-tree.exp @@ -1,15 +1,11 @@ -begin - - class <>> < (::) - def a_method() - - end +class <>> < (::) + def a_method() + + end - def self.<$CENSORED>() - .foobar() do || - - end + def self.<$CENSORED>() + .foobar() do || + end end - end diff --git a/test/testdata/namer/defs_in_blocks.rb.symbol-table-raw.exp b/test/testdata/namer/defs_in_blocks.rb.symbol-table-raw.exp index 3ddb04451c..5d1d01645d 100644 --- a/test/testdata/namer/defs_in_blocks.rb.symbol-table-raw.exp +++ b/test/testdata/namer/defs_in_blocks.rb.symbol-table-raw.exp @@ -2,7 +2,7 @@ class >> < > () class >> $1>[>>] < > $1> () method >> $1>#> $CENSORED> () @ Loc {file=test/testdata/namer/defs_in_blocks.rb start=2:1 end=13:4} argument @ Loc {file=test/testdata/namer/defs_in_blocks.rb start=??? end=???} - class > < > (>) @ Loc {file=https://github.com/sorbet/sorbet/tree/master/rbi/core/object.rbi start=removed end=removed} + class > < > (>, >) @ (Loc {file=https://github.com/sorbet/sorbet/tree/master/rbi/stdlib/json.rbi start=removed end=removed}, Loc {file=https://github.com/sorbet/sorbet/tree/master/rbi/core/object.rbi start=removed end=removed}) method ># : private () @ Loc {file=test/testdata/namer/defs_in_blocks.rb start=5:3 end=5:15} argument @ Loc {file=test/testdata/namer/defs_in_blocks.rb start=??? end=???} diff --git a/test/testdata/namer/dynamic_constant.rb.name-tree.exp b/test/testdata/namer/dynamic_constant.rb.name-tree.exp index 06ec09a89f..94ef46fb9e 100644 --- a/test/testdata/namer/dynamic_constant.rb.name-tree.exp +++ b/test/testdata/namer/dynamic_constant.rb.name-tree.exp @@ -1,16 +1,9 @@ -begin - class <>> < (::) - begin - class ::A<> < (::) - def self.foo() - = 10 - end - - - end - ::Sorbet::Private::Static.keep_for_ide(::A) - +class <>> < (::) + class ::A<> < (::) + def self.foo() + = 10 end + + end - end diff --git a/test/testdata/namer/gvar.rb.flatten-tree.exp b/test/testdata/namer/gvar.rb.flatten-tree.exp index 48e4d8a880..48aa18d027 100644 --- a/test/testdata/namer/gvar.rb.flatten-tree.exp +++ b/test/testdata/namer/gvar.rb.flatten-tree.exp @@ -1,16 +1,9 @@ begin - class <>> < (::) + + def self.<$CENSORED>() - begin - $a = 1 - begin - - ::Sorbet::Private::Static.keep_for_ide(::A) - - end - - end + $a = 1 end end class ::A<> < (::) diff --git a/test/testdata/namer/implicit_module_loc.1.rbupdate b/test/testdata/namer/implicit_module_loc.1.rbupdate new file mode 100644 index 0000000000..9dfa9a96fe --- /dev/null +++ b/test/testdata/namer/implicit_module_loc.1.rbupdate @@ -0,0 +1,5 @@ +# typed: true +# assert-fast-path: implicit_module_loc.rb + +X = 1 # error: constant +X::Y = 1 diff --git a/test/testdata/namer/implicit_module_loc.rb b/test/testdata/namer/implicit_module_loc.rb new file mode 100644 index 0000000000..aa03f2ec6d --- /dev/null +++ b/test/testdata/namer/implicit_module_loc.rb @@ -0,0 +1,14 @@ +# typed: true + +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. +# This is a rather large comment at the start of the file so that when we +# delete it on the fast path, it will make it very likely that we have an array +# access that's out of bounds when reporting errors. + +X = 1 # error: constant +X::Y = 1 diff --git a/test/testdata/namer/locals.rb.flatten-tree.exp b/test/testdata/namer/locals.rb.flatten-tree.exp index ecf6ec5b48..aec4091db7 100644 --- a/test/testdata/namer/locals.rb.flatten-tree.exp +++ b/test/testdata/namer/locals.rb.flatten-tree.exp @@ -1,12 +1,9 @@ begin - class <>> < (::) + + def self.<$CENSORED>() - begin - - ::Sorbet::Private::Static.keep_for_ide(::TestLocals) - - end + end end class ::TestLocals<> < (::) diff --git a/test/testdata/namer/module_function.rb.cfg-text.exp b/test/testdata/namer/module_function.rb.cfg-text.exp index ad35424d3e..405dc1f5a3 100644 --- a/test/testdata/namer/module_function.rb.cfg-text.exp +++ b/test/testdata/namer/module_function.rb.cfg-text.exp @@ -1,13 +1,7 @@ method ::># { -bb0[rubyRegionId=0, firstDead=8](): +bb0[rubyRegionId=0, firstDead=2](): : T.class_of() = cast(: NilClass, T.class_of()); - $7: T.class_of(Sorbet::Private::Static) = alias - $9: T.class_of(Funcs) = alias - $5: Sorbet::Private::Static::Void = $7: T.class_of(Sorbet::Private::Static).keep_for_ide($9: T.class_of(Funcs)) - $14: T.class_of(Sorbet::Private::Static) = alias - $16: T.class_of(C) = alias - $12: Sorbet::Private::Static::Void = $14: T.class_of(Sorbet::Private::Static).keep_for_ide($16: T.class_of(C)) : T.noreturn = return $2: NilClass -> bb1 diff --git a/test/testdata/namer/multiple_stubs.rb.flatten-tree.exp b/test/testdata/namer/multiple_stubs.rb.flatten-tree.exp index ece4ccb489..455a72745b 100644 --- a/test/testdata/namer/multiple_stubs.rb.flatten-tree.exp +++ b/test/testdata/namer/multiple_stubs.rb.flatten-tree.exp @@ -1,13 +1,9 @@ -begin - - class <>> < (::) - def self.<$CENSORED>() - begin - Unresolved: Unresolved: ::::.baz(1) - Unresolved: Unresolved: ::::.baz(2) - - end +class <>> < (::) + def self.<$CENSORED>() + begin + Unresolved: Unresolved: ::::.baz(1) + Unresolved: Unresolved: ::::.baz(2) + end end - end diff --git a/test/testdata/namer/nested_class.rb.flatten-tree.exp b/test/testdata/namer/nested_class.rb.flatten-tree.exp index 5c631e2690..9135094237 100644 --- a/test/testdata/namer/nested_class.rb.flatten-tree.exp +++ b/test/testdata/namer/nested_class.rb.flatten-tree.exp @@ -1,29 +1,18 @@ begin - class <>> < (::) + + + + def self.<$CENSORED>() - begin - begin - - ::Sorbet::Private::Static.keep_for_ide(::A) - - end - begin - - ::Sorbet::Private::Static.keep_for_ide(::C) - - end - - end + end end module ::A<> < () + + def self.() - begin - - ::Sorbet::Private::Static.keep_for_ide(::A::B) - - end + end end class ::A::B<> < (::) diff --git a/test/testdata/namer/next_break.rb.flatten-tree.exp b/test/testdata/namer/next_break.rb.flatten-tree.exp index 9e2494a89f..3f4d40ab85 100644 --- a/test/testdata/namer/next_break.rb.flatten-tree.exp +++ b/test/testdata/namer/next_break.rb.flatten-tree.exp @@ -1,12 +1,9 @@ begin - class <>> < (::) + + def self.<$CENSORED>() - begin - - ::Sorbet::Private::Static.keep_for_ide(::Test) - - end + end end class ::Test<> < (::) diff --git a/test/testdata/namer/parameter_names.rb.symbol-table-raw.exp b/test/testdata/namer/parameter_names.rb.symbol-table-raw.exp index 15633ea0e6..a2e1986029 100644 --- a/test/testdata/namer/parameter_names.rb.symbol-table-raw.exp +++ b/test/testdata/namer/parameter_names.rb.symbol-table-raw.exp @@ -2,7 +2,7 @@ class >> < > () class >> $1>[>>] < > $1> (>) method >> $1>#> $CENSORED> () @ Loc {file=test/testdata/namer/parameter_names.rb start=2:1 end=32:23} argument @ Loc {file=test/testdata/namer/parameter_names.rb start=??? end=???} - class > < > (>) @ Loc {file=https://github.com/sorbet/sorbet/tree/master/rbi/core/object.rbi start=removed end=removed} + class > < > (>, >) @ (Loc {file=https://github.com/sorbet/sorbet/tree/master/rbi/stdlib/json.rbi start=removed end=removed}, Loc {file=https://github.com/sorbet/sorbet/tree/master/rbi/core/object.rbi start=removed end=removed}) method ># : private (argX, ) -> Sorbet::Private::Static::Void @ Loc {file=test/testdata/namer/parameter_names.rb start=8:1 end=8:13} argument argX<> -> T.untyped @ Loc {file=test/testdata/namer/parameter_names.rb start=7:14 end=7:18} argument -> T.untyped @ Loc {file=test/testdata/namer/parameter_names.rb start=??? end=???} diff --git a/test/testdata/namer/payload_name.rb b/test/testdata/namer/payload_name.rb index 4965696bb6..18e6fced4c 100644 --- a/test/testdata/namer/payload_name.rb +++ b/test/testdata/namer/payload_name.rb @@ -1,6 +1,6 @@ # typed: true -# A facinating bug. Since `raise` in a method on Kernel, it is in the payload, +# A fascinating bug. Since `raise` in a method on Kernel, it is in the payload, # so it's Name will be entered into the table during payload. If the same name # is used from userland it will re-use the same Name but that name won't be # owned by the userland GlobalState. This hit an enforce before we tracked the diff --git a/test/testdata/namer/redefines_object.rb.cfg-text.exp b/test/testdata/namer/redefines_object.rb.cfg-text.exp index 8f98289215..34f2d33887 100644 --- a/test/testdata/namer/redefines_object.rb.cfg-text.exp +++ b/test/testdata/namer/redefines_object.rb.cfg-text.exp @@ -1,25 +1,7 @@ method ::># { -bb0[rubyRegionId=0, firstDead=20](): +bb0[rubyRegionId=0, firstDead=2](): : T.class_of() = cast(: NilClass, T.class_of()); - $7: T.class_of(Sorbet::Private::Static) = alias - $9: T.class_of(Object) = alias - $5: Sorbet::Private::Static::Void = $7: T.class_of(Sorbet::Private::Static).keep_for_ide($9: T.class_of(Object)) - $14: T.class_of(Sorbet::Private::Static) = alias - $16: T.class_of(Trigger) = alias - $12: Sorbet::Private::Static::Void = $14: T.class_of(Sorbet::Private::Static).keep_for_ide($16: T.class_of(Trigger)) - $21: T.class_of(Sorbet::Private::Static) = alias - $23: T.class_of(Foo) = alias - $19: Sorbet::Private::Static::Void = $21: T.class_of(Sorbet::Private::Static).keep_for_ide($23: T.class_of(Foo)) - $26: T.class_of(Sorbet::Private::Static) = alias - $28: T.class_of(Bar) = alias - $24: Sorbet::Private::Static::Void = $26: T.class_of(Sorbet::Private::Static).keep_for_ide($28: T.class_of(Bar)) - $33: T.class_of(Sorbet::Private::Static) = alias - $35: T.class_of(Bar) = alias - $31: Sorbet::Private::Static::Void = $33: T.class_of(Sorbet::Private::Static).keep_for_ide($35: T.class_of(Bar)) - $38: T.class_of(Sorbet::Private::Static) = alias - $40: T.class_of(Foo) = alias - $36: Sorbet::Private::Static::Void = $38: T.class_of(Sorbet::Private::Static).keep_for_ide($40: T.class_of(Foo)) : T.noreturn = return $2: NilClass -> bb1 diff --git a/test/testdata/namer/redefinition_method.rb.flatten-tree.exp b/test/testdata/namer/redefinition_method.rb.flatten-tree.exp index 4378e02ce1..e056382be5 100644 --- a/test/testdata/namer/redefinition_method.rb.flatten-tree.exp +++ b/test/testdata/namer/redefinition_method.rb.flatten-tree.exp @@ -1,12 +1,9 @@ begin - class <>> < (::) + + def self.<$CENSORED>() - begin - - ::Sorbet::Private::Static.keep_for_ide(::Main) - - end + end end class ::Main<> < (::) diff --git a/test/testdata/namer/root_private.rb.symbol-table-raw.exp b/test/testdata/namer/root_private.rb.symbol-table-raw.exp index e7a9e313bd..1f9ea4a162 100644 --- a/test/testdata/namer/root_private.rb.symbol-table-raw.exp +++ b/test/testdata/namer/root_private.rb.symbol-table-raw.exp @@ -11,7 +11,7 @@ class >> < > () type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=A) @ Loc {file=test/testdata/namer/root_private.rb start=10:1 end=10:8} method > $1>#> () @ Loc {file=test/testdata/namer/root_private.rb start=10:1 end=14:4} argument @ Loc {file=test/testdata/namer/root_private.rb start=??? end=???} - class > < > (>) @ Loc {file=https://github.com/sorbet/sorbet/tree/master/rbi/core/object.rbi start=removed end=removed} + class > < > (>, >) @ (Loc {file=https://github.com/sorbet/sorbet/tree/master/rbi/stdlib/json.rbi start=removed end=removed}, Loc {file=https://github.com/sorbet/sorbet/tree/master/rbi/core/object.rbi start=removed end=removed}) method ># : private () @ Loc {file=test/testdata/namer/root_private.rb start=5:1 end=5:20} argument @ Loc {file=test/testdata/namer/root_private.rb start=??? end=???} diff --git a/test/testdata/namer/simple.rb.flatten-tree.exp b/test/testdata/namer/simple.rb.flatten-tree.exp index 501986060a..8c2b85f635 100644 --- a/test/testdata/namer/simple.rb.flatten-tree.exp +++ b/test/testdata/namer/simple.rb.flatten-tree.exp @@ -1,46 +1,21 @@ begin - class <>> < (::) + + + + + + + + + + + + + + def self.<$CENSORED>() - begin - begin - - ::Sorbet::Private::Static.keep_for_ide(::NormalClass) - - end - begin - - ::Sorbet::Private::Static.keep_for_ide(::ANamespace) - - end - begin - - ::Sorbet::Private::Static.keep_for_ide(::ANamespace::ClassInNamespace) - - end - begin - - ::Sorbet::Private::Static.keep_for_ide(::Parent) - - end - begin - - ::Sorbet::Private::Static.keep_for_ide(::Mixin) - - end - begin - - ::Sorbet::Private::Static.keep_for_ide(::OtherMixin) - - end - begin - - ::Sorbet::Private::Static.keep_for_ide(::Child) - ::Sorbet::Private::Static.keep_for_ide(::Parent) - - end - - end + end end class ::NormalClass<> < (::) @@ -52,20 +27,14 @@ begin end + + + + def self.() begin - begin - - ::Sorbet::Private::Static.keep_for_ide(::NormalClass::InnerClass) - - end - begin - - ::Sorbet::Private::Static.keep_for_ide(::NormalClass::InnerModule) - - end end end @@ -81,12 +50,10 @@ begin end end module ::ANamespace<> < () + + def self.() - begin - - ::Sorbet::Private::Static.keep_for_ide(::ANamespace::ObviousChild) - - end + end end class ::ANamespace::ObviousChild<> < (::) diff --git a/test/testdata/namer/singleton_class.rb.symbol-table-raw.exp b/test/testdata/namer/singleton_class.rb.symbol-table-raw.exp index 5397aeac76..1bb422346b 100644 --- a/test/testdata/namer/singleton_class.rb.symbol-table-raw.exp +++ b/test/testdata/namer/singleton_class.rb.symbol-table-raw.exp @@ -11,7 +11,7 @@ class >> < > () argument @ Loc {file=test/testdata/namer/singleton_class.rb start=??? end=???} class >::> $1> < > () @ Loc {file=test/testdata/namer/singleton_class.rb start=2:7 end=2:11} class > $1> < > () @ Loc {file=test/testdata/namer/singleton_class.rb start=2:7 end=2:8} - class > < > (>) @ Loc {file=https://github.com/sorbet/sorbet/tree/master/rbi/core/object.rbi start=removed end=removed} + class > < > (>, >) @ (Loc {file=https://github.com/sorbet/sorbet/tree/master/rbi/stdlib/json.rbi start=removed end=removed}, Loc {file=https://github.com/sorbet/sorbet/tree/master/rbi/core/object.rbi start=removed end=removed}) method ># : private () @ Loc {file=test/testdata/namer/singleton_class.rb start=5:1 end=5:9} argument @ Loc {file=test/testdata/namer/singleton_class.rb start=??? end=???} diff --git a/test/testdata/namer/super.rb b/test/testdata/namer/super.rb index e045bb314c..f7e1346a0e 100644 --- a/test/testdata/namer/super.rb +++ b/test/testdata/namer/super.rb @@ -1,2 +1,6 @@ # typed: true -super # error: `super` outside of method +# I'm fine with the weird error, +# because the "outside of method" error will fire first. + super +# ^^^^^ error: `super` outside of method +# ^^^^^ error: `` does not exist on ancestors of diff --git a/test/testdata/namer/superclass_redefinition.rb.symbol-table-raw.exp b/test/testdata/namer/superclass_redefinition.rb.symbol-table-raw.exp index ea17fe2546..d4201d240d 100644 --- a/test/testdata/namer/superclass_redefinition.rb.symbol-table-raw.exp +++ b/test/testdata/namer/superclass_redefinition.rb.symbol-table-raw.exp @@ -4,8 +4,8 @@ class >> < > () argument @ Loc {file=test/testdata/namer/superclass_redefinition.rb start=??? end=???} class > < > () @ Loc {file=test/testdata/namer/superclass_redefinition.rb start=7:1 end=7:12} class > $1>[>>] < > $1> () @ Loc {file=test/testdata/namer/superclass_redefinition.rb start=7:1 end=7:12} - type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=A) @ Loc {file=test/testdata/namer/superclass_redefinition.rb start=2:1 end=2:17} - method > $1>#> () @ Loc {file=test/testdata/namer/superclass_redefinition.rb start=2:1 end=3:4} + type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=A) @ Loc {file=test/testdata/namer/superclass_redefinition.rb start=7:1 end=7:12} + method > $1>#> () @ Loc {file=test/testdata/namer/superclass_redefinition.rb start=7:1 end=8:4} argument @ Loc {file=test/testdata/namer/superclass_redefinition.rb start=??? end=???} class > < > () @ Loc {file=test/testdata/namer/superclass_redefinition.rb start=4:1 end=4:8} class > $1>[>>] < > $1> () @ Loc {file=test/testdata/namer/superclass_redefinition.rb start=4:1 end=4:8} diff --git a/test/testdata/namer/toplevel_attr_writer.rb.symbol-table.exp b/test/testdata/namer/toplevel_attr_writer.rb.symbol-table.exp index 9495dbd96d..22e7e3aa8e 100644 --- a/test/testdata/namer/toplevel_attr_writer.rb.symbol-table.exp +++ b/test/testdata/namer/toplevel_attr_writer.rb.symbol-table.exp @@ -2,7 +2,7 @@ class :: < ::Object () class ::>[] < :: (Sig) method ::># () @ test/testdata/namer/toplevel_attr_writer.rb:3 argument @ Loc {file=test/testdata/namer/toplevel_attr_writer.rb start=??? end=???} - class ::Object < ::BasicObject (Kernel) @ https://github.com/sorbet/sorbet/tree/master/rbi/core/object.rbi#LCENSORED + class ::Object < ::BasicObject (Object, Kernel) @ (https://github.com/sorbet/sorbet/tree/master/rbi/stdlib/json.rbi#LCENSORED, https://github.com/sorbet/sorbet/tree/master/rbi/core/object.rbi#LCENSORED) field ::Object#@foo -> T.untyped @ test/testdata/namer/toplevel_attr_writer.rb:6 method ::Object#foo= : private (foo, ) -> T.untyped @ test/testdata/namer/toplevel_attr_writer.rb:6 argument foo<> -> T.untyped @ Loc {file=test/testdata/namer/toplevel_attr_writer.rb start=6:14 end=6:17} diff --git a/test/testdata/namer/type_alias_not_on_T.rb b/test/testdata/namer/type_alias_not_on_T.rb new file mode 100644 index 0000000000..9d60bc3dec --- /dev/null +++ b/test/testdata/namer/type_alias_not_on_T.rb @@ -0,0 +1,20 @@ +# typed: true +module A + module NotT + def self.type_alias(&blk); end + end + + ValidTypeAlias = T.type_alias {Object} + + NotActuallyATypeAlias = NotT.type_alias {Object} +end + +module KnownLimitation + module T + def self.type_alias(&blk); end + end + + # Known limitation: we can't distinguish between `::KnownLimitation::T` and `::T` in the namer, + # so this will have a false positive, being treated as a type alias when it shouldn't be. + StillATypeAlias = T.type_alias {Object} +end diff --git a/test/testdata/namer/type_alias_not_on_T.rb.symbol-table.exp b/test/testdata/namer/type_alias_not_on_T.rb.symbol-table.exp new file mode 100644 index 0000000000..b030f73fde --- /dev/null +++ b/test/testdata/namer/type_alias_not_on_T.rb.symbol-table.exp @@ -0,0 +1,28 @@ +class :: < ::Object () + class ::>[] < :: () + method ::># () @ test/testdata/namer/type_alias_not_on_T.rb:2 + argument @ Loc {file=test/testdata/namer/type_alias_not_on_T.rb start=??? end=???} + module ::A < ::Sorbet::Private::Static::ImplicitModuleSuperclass () @ test/testdata/namer/type_alias_not_on_T.rb:2 + static-field ::A::NotActuallyATypeAlias -> @ test/testdata/namer/type_alias_not_on_T.rb:9 + module ::A::NotT < ::Sorbet::Private::Static::ImplicitModuleSuperclass () @ test/testdata/namer/type_alias_not_on_T.rb:3 + class ::A:: < ::Module () @ test/testdata/namer/type_alias_not_on_T.rb:3 + method ::A::# () @ test/testdata/namer/type_alias_not_on_T.rb:3 + argument @ Loc {file=test/testdata/namer/type_alias_not_on_T.rb start=??? end=???} + method ::A::#type_alias (blk) @ test/testdata/namer/type_alias_not_on_T.rb:4 + argument blk @ Loc {file=test/testdata/namer/type_alias_not_on_T.rb start=4:26 end=4:29} + static-field-type-alias ::A::ValidTypeAlias -> Object @ test/testdata/namer/type_alias_not_on_T.rb:7 + class :: < ::Module () @ test/testdata/namer/type_alias_not_on_T.rb:2 + method ::# () @ test/testdata/namer/type_alias_not_on_T.rb:2 + argument @ Loc {file=test/testdata/namer/type_alias_not_on_T.rb start=??? end=???} + module ::KnownLimitation < ::Sorbet::Private::Static::ImplicitModuleSuperclass () @ test/testdata/namer/type_alias_not_on_T.rb:12 + static-field-type-alias ::KnownLimitation::StillATypeAlias -> Object @ test/testdata/namer/type_alias_not_on_T.rb:19 + module ::KnownLimitation::T < ::Sorbet::Private::Static::ImplicitModuleSuperclass () @ test/testdata/namer/type_alias_not_on_T.rb:13 + class ::KnownLimitation:: < ::Module () @ test/testdata/namer/type_alias_not_on_T.rb:13 + method ::KnownLimitation::# () @ test/testdata/namer/type_alias_not_on_T.rb:13 + argument @ Loc {file=test/testdata/namer/type_alias_not_on_T.rb start=??? end=???} + method ::KnownLimitation::#type_alias (blk) @ test/testdata/namer/type_alias_not_on_T.rb:14 + argument blk @ Loc {file=test/testdata/namer/type_alias_not_on_T.rb start=14:26 end=14:29} + class :: < ::Module () @ test/testdata/namer/type_alias_not_on_T.rb:12 + method ::# () @ test/testdata/namer/type_alias_not_on_T.rb:12 + argument @ Loc {file=test/testdata/namer/type_alias_not_on_T.rb start=??? end=???} + diff --git a/test/testdata/namer/visibility.rb b/test/testdata/namer/visibility.rb index 03794d51c8..8769dec2ce 100644 --- a/test/testdata/namer/visibility.rb +++ b/test/testdata/namer/visibility.rb @@ -53,3 +53,23 @@ def foo; private :foo end end + +class Foo5 + T.unsafe(nil).private + + def foo; end # this does not end up being private +end + +class Foo6 + self.private + + def foo; end +end + +class Foo7 + bar = nil + bar.private + # ^^^^^^^ error: Method `private` does not exist on `NilClass` + + def foo; end +end diff --git a/test/testdata/namer/visibility.rb.symbol-table-raw.exp b/test/testdata/namer/visibility.rb.symbol-table-raw.exp index dc7e0a5859..5f5d188a2e 100644 --- a/test/testdata/namer/visibility.rb.symbol-table-raw.exp +++ b/test/testdata/namer/visibility.rb.symbol-table-raw.exp @@ -1,6 +1,6 @@ class >> < > () class >> $1>[>>] < > $1> () - method >> $1>#> $CENSORED> () @ Loc {file=test/testdata/namer/visibility.rb start=3:1 end=55:4} + method >> $1>#> $CENSORED> () @ Loc {file=test/testdata/namer/visibility.rb start=3:1 end=75:4} argument @ Loc {file=test/testdata/namer/visibility.rb start=??? end=???} class > < > () @ Loc {file=test/testdata/namer/visibility.rb start=3:1 end=3:8} method ># () @ Loc {file=test/testdata/namer/visibility.rb start=4:3 end=4:9} @@ -75,4 +75,25 @@ class >> < > () type-member(+) > $1> $1>::>> -> LambdaParam(> $1> $1>::>>, lower=T.noreturn, upper=AppliedType { klass = > $1> targs = [ >> = Foo4 ] }) @ Loc {file=test/testdata/namer/visibility.rb start=50:3 end=50:8} method > $1> $1>#> () @ Loc {file=test/testdata/namer/visibility.rb start=50:3 end=54:6} argument @ Loc {file=test/testdata/namer/visibility.rb start=??? end=???} + class > < > () @ Loc {file=test/testdata/namer/visibility.rb start=57:1 end=57:11} + method ># () @ Loc {file=test/testdata/namer/visibility.rb start=60:3 end=60:10} + argument @ Loc {file=test/testdata/namer/visibility.rb start=??? end=???} + class > $1>[>>] < > $1> () @ Loc {file=test/testdata/namer/visibility.rb start=57:1 end=57:11} + type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=Foo5) @ Loc {file=test/testdata/namer/visibility.rb start=57:1 end=57:11} + method > $1>#> () @ Loc {file=test/testdata/namer/visibility.rb start=57:1 end=61:4} + argument @ Loc {file=test/testdata/namer/visibility.rb start=??? end=???} + class > < > () @ Loc {file=test/testdata/namer/visibility.rb start=63:1 end=63:11} + method ># : private () @ Loc {file=test/testdata/namer/visibility.rb start=66:3 end=66:10} + argument @ Loc {file=test/testdata/namer/visibility.rb start=??? end=???} + class > $1>[>>] < > $1> () @ Loc {file=test/testdata/namer/visibility.rb start=63:1 end=63:11} + type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=Foo6) @ Loc {file=test/testdata/namer/visibility.rb start=63:1 end=63:11} + method > $1>#> () @ Loc {file=test/testdata/namer/visibility.rb start=63:1 end=67:4} + argument @ Loc {file=test/testdata/namer/visibility.rb start=??? end=???} + class > < > () @ Loc {file=test/testdata/namer/visibility.rb start=69:1 end=69:11} + method ># () @ Loc {file=test/testdata/namer/visibility.rb start=74:3 end=74:10} + argument @ Loc {file=test/testdata/namer/visibility.rb start=??? end=???} + class > $1>[>>] < > $1> () @ Loc {file=test/testdata/namer/visibility.rb start=69:1 end=69:11} + type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=Foo7) @ Loc {file=test/testdata/namer/visibility.rb start=69:1 end=69:11} + method > $1>#> () @ Loc {file=test/testdata/namer/visibility.rb start=69:1 end=75:4} + argument @ Loc {file=test/testdata/namer/visibility.rb start=??? end=???} diff --git a/test/testdata/namer/yield.rb.cfg-text.exp b/test/testdata/namer/yield.rb.cfg-text.exp index 3b52a665b7..bbd7a1a880 100644 --- a/test/testdata/namer/yield.rb.cfg-text.exp +++ b/test/testdata/namer/yield.rb.cfg-text.exp @@ -1,14 +1,11 @@ method ::># { -bb0[rubyRegionId=0, firstDead=8](): +bb0[rubyRegionId=0, firstDead=5](): : T.class_of() = cast(: NilClass, T.class_of()); - $7: T.class_of(Sorbet::Private::Static) = alias - $9: T.class_of(Main) = alias - $5: Sorbet::Private::Static::Void = $7: T.class_of(Sorbet::Private::Static).keep_for_ide($9: T.class_of(Main)) - $13: T.class_of(Main) = alias - $11: Main = $13: T.class_of(Main).new() - $10: T.untyped = $11: Main.main() - : T.noreturn = return $2: NilClass + $5: T.class_of(Main) = alias + $3: Main = $5: T.class_of(Main).new() + $2: T.untyped = $3: Main.main() + : T.noreturn = return $2: T.untyped -> bb1 # backedges diff --git a/test/testdata/namer/yield.rb.flatten-tree.exp b/test/testdata/namer/yield.rb.flatten-tree.exp index 26123cdf61..fe904cf293 100644 --- a/test/testdata/namer/yield.rb.flatten-tree.exp +++ b/test/testdata/namer/yield.rb.flatten-tree.exp @@ -1,16 +1,9 @@ begin - class <>> < (::) + + def self.<$CENSORED>() - begin - begin - - ::Sorbet::Private::Static.keep_for_ide(::Main) - - end - ::Main.new().main() - - end + ::Main.new().main() end end class ::Main<> < (::) diff --git a/test/testdata/packager/deeply_nested_packages/pass.package-tree.exp b/test/testdata/packager/deeply_nested_packages/pass.package-tree.exp index 1affd547a1..f3614eb475 100644 --- a/test/testdata/packager/deeply_nested_packages/pass.package-tree.exp +++ b/test/testdata/packager/deeply_nested_packages/pass.package-tree.exp @@ -1,6 +1,6 @@ # -- test/testdata/packager/deeply_nested_packages/__package.rb -- class <>> < (::) - class ::::<>> < (::) + class ::::<>> < (::PackageSpec) .import(::::::) .export(::::::) @@ -8,6 +8,14 @@ class <>> < (::) .export(::::::) end end +# -- test/testdata/packager/deeply_nested_packages/subdirectory/subpackage/__package.rb -- +class <>> < (::) + class ::::::<>> < (::PackageSpec) + .import(::::) + + .export(::::::::) + end +end # -- test/testdata/packager/deeply_nested_packages/mainpackage.rb -- class <>> < (::) class ::::<>> < (::) @@ -29,14 +37,6 @@ class <>> < (::) class ::::<>> < (::) end end -# -- test/testdata/packager/deeply_nested_packages/subdirectory/subpackage/__package.rb -- -class <>> < (::) - class ::::::<>> < (::) - .import(::::) - - .export(::::::::) - end -end # -- test/testdata/packager/deeply_nested_packages/subdirectory/subpackage/subpackage.rb -- class <>> < (::) class ::::::<>> < (::) diff --git a/test/testdata/packager/export_for_test/foo/foo.rb b/test/testdata/packager/export_for_test/foo/foo.rb index 9a5db67891..6b45561d3a 100644 --- a/test/testdata/packager/export_for_test/foo/foo.rb +++ b/test/testdata/packager/export_for_test/foo/foo.rb @@ -14,7 +14,7 @@ def self.stub_stuff!; end end - # Check Visiblity + # Check Visibility # via import Opus::Foo::Bar Opus::Foo::Bar::BarClass Test::Opus::Foo::Bar::BarClassTest @@ -36,7 +36,6 @@ def self.stub_stuff!; end Opus::TestImported::TIClass # ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Used `test_import` constant `Opus::TestImported::TIClass` in non-test file Test::Opus::TestImported::TITestClass -# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Used `test_import` constant `Test::Opus::TestImported::TITestClass` in non-test file # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `Test::Opus::TestImported::TITestClass` is defined in a test namespace diff --git a/test/testdata/packager/export_for_test/foo/foo.test.rb b/test/testdata/packager/export_for_test/foo/foo.test.rb index a47f6e58e5..85f830669d 100644 --- a/test/testdata/packager/export_for_test/foo/foo.test.rb +++ b/test/testdata/packager/export_for_test/foo/foo.test.rb @@ -1,7 +1,7 @@ # typed: strict class Test::Opus::Foo::FooTest - # Check Visiblity + # Check Visibility # via import Opus::Foo::Bar Opus::Foo::Bar::BarClass Test::Opus::Foo::Bar::BarClassTest diff --git a/test/testdata/packager/export_for_test/pass.package-tree.exp b/test/testdata/packager/export_for_test/pass.package-tree.exp index 17c9183e33..8bc8a379cc 100644 --- a/test/testdata/packager/export_for_test/pass.package-tree.exp +++ b/test/testdata/packager/export_for_test/pass.package-tree.exp @@ -1,11 +1,11 @@ # -- test/testdata/packager/export_for_test/__package.rb -- class <>> < (::) - class ::::<>> < (::) + class ::::<>> < (::PackageSpec) end end # -- test/testdata/packager/export_for_test/foo/__package.rb -- class <>> < (::) - class ::::::<>> < (::) + class ::::::<>> < (::PackageSpec) .import(::::::::) .import(::::::) @@ -21,12 +21,32 @@ class <>> < (::) end # -- test/testdata/packager/export_for_test/foo/bar/__package.rb -- class <>> < (::) - class ::::::::<>> < (::) + class ::::::::<>> < (::PackageSpec) .export(::::::::::) .export(::::::::::::) end end +# -- test/testdata/packager/export_for_test/test_imported/__package.rb -- +class <>> < (::) + class ::::::<>> < (::PackageSpec) + .export(::::::::) + + .export(::::::::::) + end +end +# -- test/testdata/packager/export_for_test/util/__package.rb -- +class <>> < (::) + class ::::::<>> < (::PackageSpec) + .export(::::::::) + + .export(::::::::::) + + .export(::::::::::) + + .export_for_test(::::::) + end +end # -- test/testdata/packager/export_for_test/foo/bar/bar.rb -- class <>> < (::) class ::::::::<>> < (::) @@ -112,14 +132,6 @@ class <>> < (::) :::::: end end -# -- test/testdata/packager/export_for_test/test_imported/__package.rb -- -class <>> < (::) - class ::::::<>> < (::) - .export(::::::::) - - .export(::::::::::) - end -end # -- test/testdata/packager/export_for_test/test_imported/test_imported.rb -- class <>> < (::) module ::::<>> < () @@ -134,18 +146,6 @@ class <>> < (::) end end end -# -- test/testdata/packager/export_for_test/util/__package.rb -- -class <>> < (::) - class ::::::<>> < (::) - .export(::::::::) - - .export(::::::::::) - - .export(::::::::::) - - .export_for_test(::::::) - end -end # -- test/testdata/packager/export_for_test/util/util.rb -- class <>> < (::) module ::::<>> < () diff --git a/test/testdata/packager/export_imported/pass.package-tree.exp b/test/testdata/packager/export_imported/pass.package-tree.exp index e1f2279c1f..75f671fb02 100644 --- a/test/testdata/packager/export_imported/pass.package-tree.exp +++ b/test/testdata/packager/export_imported/pass.package-tree.exp @@ -1,6 +1,6 @@ # -- test/testdata/packager/export_imported/a/__package.rb -- class <>> < (::) - class ::::<>> < (::) + class ::::<>> < (::PackageSpec) .import(::::) .export(::::::) @@ -8,7 +8,7 @@ class <>> < (::) end # -- test/testdata/packager/export_imported/b/__package.rb -- class <>> < (::) - class ::::<>> < (::) + class ::::<>> < (::PackageSpec) .export(::::::) end end diff --git a/test/testdata/packager/extra_package_paths/pass.package-tree.exp b/test/testdata/packager/extra_package_paths/pass.package-tree.exp index 3dba127bfc..105bcf4630 100644 --- a/test/testdata/packager/extra_package_paths/pass.package-tree.exp +++ b/test/testdata/packager/extra_package_paths/pass.package-tree.exp @@ -1,6 +1,6 @@ # -- test/testdata/packager/extra_package_paths/bar/__package.rb -- class <>> < (::) - class ::::::<>> < (::) + class ::::::<>> < (::PackageSpec) .import(::::::) .import(::::::::) @@ -8,6 +8,28 @@ class <>> < (::) .import(::::::) end end +# -- test/testdata/packager/extra_package_paths/baz/__package.rb -- +class <>> < (::) + class ::::::::<>> < (::PackageSpec) + .export(::::::::::) + + .export(::::::::::) + end +end +# -- test/testdata/packager/extra_package_paths/foo/__package.rb -- +class <>> < (::) + class ::::::<>> < (::PackageSpec) + .export(::::::::) + + .export(::::::::) + end +end +# -- test/testdata/packager/extra_package_paths/foo_bar/__package.rb -- +class <>> < (::) + class ::::::<>> < (::PackageSpec) + .export(::::::::) + end +end # -- test/testdata/packager/extra_package_paths/bar/bar.rb -- class <>> < (::) class ::::::<>> < (::) @@ -41,14 +63,6 @@ class <>> < (::) end end -# -- test/testdata/packager/extra_package_paths/baz/__package.rb -- -class <>> < (::) - class ::::::::<>> < (::) - .export(::::::::::) - - .export(::::::::::) - end -end # -- test/testdata/packager/extra_package_paths/extra/Project_Baz_Package/c.rb -- class <>> < (::) module ::::::<>> < () @@ -133,17 +147,3 @@ class <>> < (::) end end -# -- test/testdata/packager/extra_package_paths/foo/__package.rb -- -class <>> < (::) - class ::::::<>> < (::) - .export(::::::::) - - .export(::::::::) - end -end -# -- test/testdata/packager/extra_package_paths/foo_bar/__package.rb -- -class <>> < (::) - class ::::::<>> < (::) - .export(::::::::) - end -end diff --git a/test/testdata/packager/import_subpackage/pass.package-tree.exp b/test/testdata/packager/import_subpackage/pass.package-tree.exp index 034d10195c..42331692bc 100644 --- a/test/testdata/packager/import_subpackage/pass.package-tree.exp +++ b/test/testdata/packager/import_subpackage/pass.package-tree.exp @@ -1,12 +1,12 @@ # -- test/testdata/packager/import_subpackage/a/__package.rb -- class <>> < (::) - class ::::<>> < (::) + class ::::<>> < (::PackageSpec) .import(::::::) end end # -- test/testdata/packager/import_subpackage/a/b/__package.rb -- class <>> < (::) - class ::::::<>> < (::) + class ::::::<>> < (::PackageSpec) .export(::::::::) end end diff --git a/test/testdata/packager/invalid_imports_and_exports/pass.package-tree.exp b/test/testdata/packager/invalid_imports_and_exports/pass.package-tree.exp index befd1d392d..7e25e5defd 100644 --- a/test/testdata/packager/invalid_imports_and_exports/pass.package-tree.exp +++ b/test/testdata/packager/invalid_imports_and_exports/pass.package-tree.exp @@ -1,6 +1,6 @@ # -- test/testdata/packager/invalid_imports_and_exports/__package.rb -- class <>> < (::) - class ::::<>> < (::) + class ::::<>> < (::PackageSpec) .import(123) .import("hello") @@ -26,6 +26,16 @@ class <>> < (::) .export(::::::) end end +# -- test/testdata/packager/invalid_imports_and_exports/b/__package.rb -- +class <>> < (::) + class ::::<>> < (::PackageSpec) + .import(::::) + + .export(::::::) + + .export(::::::) + end +end # -- test/testdata/packager/invalid_imports_and_exports/a.rb -- class <>> < (::) module ::<>> < () @@ -63,16 +73,6 @@ class <>> < (::) end end end -# -- test/testdata/packager/invalid_imports_and_exports/b/__package.rb -- -class <>> < (::) - class ::::<>> < (::) - .import(::::) - - .export(::::::) - - .export(::::::) - end -end # -- test/testdata/packager/invalid_imports_and_exports/b/b.rb -- class <>> < (::) module ::<>> < () diff --git a/test/testdata/packager/invalid_package_control_flow/__package.rb b/test/testdata/packager/invalid_package_control_flow/__package.rb index fa9fde1daa..9e53c41efc 100644 --- a/test/testdata/packager/invalid_package_control_flow/__package.rb +++ b/test/testdata/packager/invalid_package_control_flow/__package.rb @@ -3,7 +3,7 @@ # enable-packager: true # Constant definitions/assignments are not OK -SomeConstant = PackageSpec # error: Invalid expression in package: `=` +SomeConstant = PackageSpec # error: Invalid expression in package: `Assign` class MyPackage < PackageSpec extend T::Helpers # error: Invalid expression in package: `extend` is not allowed @@ -18,10 +18,10 @@ class MyPackage < PackageSpec # Complex args are not OK (we can choose to relax this) some_method([0,1,2]) # ^^^^^^^ error: Invalid expression in package: Arguments to functions must be literals - # ^^^^^^^ error: Invalid expression in package: array + # ^^^^^^^ error: Invalid expression in package: `Array` some_method({prop: 10}) # ^^^^^^^^^^ error: Invalid expression in package: Arguments to functions must be literals - # ^^^^^^^^^^ error: Invalid expression in package: hash + # ^^^^^^^^^^ error: Invalid expression in package: `Hash` # Literals should be fine. some_method "Literal" @@ -29,18 +29,21 @@ class MyPackage < PackageSpec # Methods defs are not OK sig {void} # error: Invalid expression in package: Arguments to functions must be literals -# ^^^^^^^^^^ error: Invalid expression in package: blocks not allowed +# ^^^^^^^^^^ error: Invalid expression in package: `Block` not allowed def package_method; end -# ^^^^^^^^^^^^^^^^^^^^^^^ error: Invalid expression in package: method definition +# ^^^^^^^^^^^^^^^^^^^^^^^ error: Invalid expression in package: `MethodDef` +# ^^^^^^^^^^^^^^^^^^ error: Invalid expression in package: `RuntimeMethodDefinition` sig {void} # error: Invalid expression in package: Arguments to functions must be literals -# ^^^^^^^^^^ error: Invalid expression in package: blocks not allowed +# ^^^^^^^^^^ error: Invalid expression in package: `Block` not allowed def self.static_method; end -# ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Invalid expression in package: method definition +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Invalid expression in package: `MethodDef` +# ^^^^^^^^^^^^^^^^^^^^^^ error: Invalid expression in package: `RuntimeMethodDefinition` # Var defs / assignments are not OK @hello = T.let(nil, T.nilable(String)) -# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Invalid expression in package: `=` -# ^^^^^^^^^^^^^^^^^ error: Invalid expression in package: Arguments to functions must be literals +# ^^^^^^ error: Invalid expression in package: `UnresolvedIdent` +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Invalid expression in package: `Assign` +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Invalid expression in package: `Cast` not allowed # ^^^^^^ error: Invalid expression in package: Arguments to functions must be literals end diff --git a/test/testdata/packager/invalid_package_control_flow/pass.package-tree.exp b/test/testdata/packager/invalid_package_control_flow/pass.package-tree.exp index 3cea1b92cf..be3d340ccd 100644 --- a/test/testdata/packager/invalid_package_control_flow/pass.package-tree.exp +++ b/test/testdata/packager/invalid_package_control_flow/pass.package-tree.exp @@ -2,7 +2,7 @@ class <>> < (::) :: = :: - class ::::<>> < (::) + class ::::<>> < (::PackageSpec) ::Sorbet::Private::Static.sig() do || .void() end diff --git a/test/testdata/packager/lsp_features/family.rb b/test/testdata/packager/lsp_features/family.rb index 4ddf688157..3d5b1c7738 100644 --- a/test/testdata/packager/lsp_features/family.rb +++ b/test/testdata/packager/lsp_features/family.rb @@ -41,7 +41,6 @@ def son end Test::Krabappel::Popquiz -# ^^^^^^^^^^^^^^^^^^^^^^^^ error: Used `test_import` constant `Test::Krabappel::Popquiz` in non-test file # ^^^^^^^^^^^^^^^^^^^^^^^^ error: `Test::Krabappel::Popquiz` is defined in a test namespace # ^^^^^^^ usage: popquiz # ^^^^^^^^^^^^^^^ importusage: krabappel-pkg diff --git a/test/testdata/packager/nested_inner_namespaces/pass.package-tree.exp b/test/testdata/packager/nested_inner_namespaces/pass.package-tree.exp index bc9fb9e3c3..63aa792706 100644 --- a/test/testdata/packager/nested_inner_namespaces/pass.package-tree.exp +++ b/test/testdata/packager/nested_inner_namespaces/pass.package-tree.exp @@ -1,9 +1,19 @@ # -- test/testdata/packager/nested_inner_namespaces/__package.rb -- class <>> < (::) - class ::::<>> < (::) + class ::::<>> < (::PackageSpec) .import(::::::) end end +# -- test/testdata/packager/nested_inner_namespaces/foo/__package.rb -- +class <>> < (::) + class ::::::<>> < (::PackageSpec) + .export(::::::::) + + .export(::::::::::) + + .export(::::::::::) + end +end # -- test/testdata/packager/nested_inner_namespaces/bar.rb -- class <>> < (::) module ::::<>> < () @@ -28,16 +38,6 @@ class <>> < (::) end end -# -- test/testdata/packager/nested_inner_namespaces/foo/__package.rb -- -class <>> < (::) - class ::::::<>> < (::) - .export(::::::::) - - .export(::::::::::) - - .export(::::::::::) - end -end # -- test/testdata/packager/nested_inner_namespaces/foo/foo.rb -- class <>> < (::) module ::::<>> < () diff --git a/test/testdata/packager/nested_packages/pass.package-tree.exp b/test/testdata/packager/nested_packages/pass.package-tree.exp index 93d5170603..5d92fde906 100644 --- a/test/testdata/packager/nested_packages/pass.package-tree.exp +++ b/test/testdata/packager/nested_packages/pass.package-tree.exp @@ -1,11 +1,19 @@ # -- test/testdata/packager/nested_packages/__package.rb -- class <>> < (::) - class ::::<>> < (::) + class ::::<>> < (::PackageSpec) .import(::::::) .export(::::::) end end +# -- test/testdata/packager/nested_packages/subpackage/__package.rb -- +class <>> < (::) + class ::::::<>> < (::PackageSpec) + .import(::::) + + .export(::::::::) + end +end # -- test/testdata/packager/nested_packages/mainpackage.rb -- class <>> < (::) class ::::<>> < (::) @@ -22,14 +30,6 @@ class <>> < (::) end end -# -- test/testdata/packager/nested_packages/subpackage/__package.rb -- -class <>> < (::) - class ::::::<>> < (::) - .import(::::) - - .export(::::::::) - end -end # -- test/testdata/packager/nested_packages/subpackage/subpackage.rb -- class <>> < (::) class ::::::<>> < (::) diff --git a/test/testdata/packager/non_forcing_constants/foo/foo_no_forcing.rb b/test/testdata/packager/non_forcing_constants/foo/foo_no_forcing.rb index 0d0c642c5f..9db8980d8c 100644 --- a/test/testdata/packager/non_forcing_constants/foo/foo_no_forcing.rb +++ b/test/testdata/packager/non_forcing_constants/foo/foo_no_forcing.rb @@ -15,7 +15,8 @@ def self.global_check_is_bar(arg) sig {params(arg: T.untyped).returns(T::Boolean)} def self.bad_not_keyword_arg(arg) - if T::NonForcingConstants.non_forcing_is_a?(arg, "::Bar", "Project::Bar") # error: Too many positional arguments + if T::NonForcingConstants.non_forcing_is_a?(arg, "::Bar", "Project::Bar") + # ^^^^^^^^^^^^^^ error: Too many arguments true else false @@ -24,7 +25,9 @@ def self.bad_not_keyword_arg(arg) sig {params(arg: T.untyped).returns(T::Boolean)} def self.bad_wrong_keyword_arg(arg) - if T::NonForcingConstants.non_forcing_is_a?(arg, "::Bar", pakige: "Project::Bar") # error: Unrecognized keyword argument + if T::NonForcingConstants.non_forcing_is_a?(arg, "::Bar", pakige: "Project::Bar") + # ^^^^^^^ error: Unable to resolve constant + # ^^^^^^^^^^^^^^^^^^^^^^ error: Too many arguments true else false @@ -32,35 +35,10 @@ def self.bad_wrong_keyword_arg(arg) end sig {params(arg: T.untyped).returns(T::Boolean)} - def self.bad_package_not_a_string(arg) - if T::NonForcingConstants.non_forcing_is_a?(arg, "::Bar", package: 5) # error: Expected `T.nilable(String)` but found `Integer(5)` - true - else - false - end - end - - sig {params(arg: T.untyped).returns(T::Boolean)} - def self.bad_check_for_global_bar(arg) - if T::NonForcingConstants.non_forcing_is_a?(arg, "::Bar", package: "Project::Bar") # error: should not be an absolute constant reference - true - else - false - end - end - - sig {params(arg: T.untyped).returns(T::Boolean)} - def self.bad_non_existent_package(arg) - if T::NonForcingConstants.non_forcing_is_a?(arg, "Bar", package: "Project::Quux") # error: Unable to resolve constant `::Project::Quux` - true - else - false - end - end - - sig {params(arg: T.untyped).returns(T::Boolean)} - def self.check_for_non_existing_bar(arg) - if T::NonForcingConstants.non_forcing_is_a?(arg, "Quux", package: "Project::Bar") # error: Unable to resolve constant `::Project::Bar::Quux` + def self.bad_package(arg) + if T::NonForcingConstants.non_forcing_is_a?(arg, "::Bar", package: 5) + # ^^^^^^^ error: Unable to resolve constant + # ^^^^^^^^^^ error: Too many arguments true else false @@ -69,7 +47,7 @@ def self.check_for_non_existing_bar(arg) sig {params(arg: T.untyped).returns(T::Boolean)} def self.good_check_is_bar(arg) - if T::NonForcingConstants.non_forcing_is_a?(arg, "Bar", package: "Project::Bar") + if T::NonForcingConstants.non_forcing_is_a?(arg, "::Project::Bar::Bar") true else false diff --git a/test/testdata/packager/package-no-spec/__package.rb b/test/testdata/packager/package-no-spec/__package.rb index 0d6c9eaca8..2ae4ec05d1 100644 --- a/test/testdata/packager/package-no-spec/__package.rb +++ b/test/testdata/packager/package-no-spec/__package.rb @@ -1,4 +1,4 @@ # frozen_string_literal: true -# error: Package file must contain a package definition +# error: `__package.rb` file must contain a package definition # typed: strict # enable-packager: true diff --git a/test/testdata/packager/package-prefix-enforcement/critic_prefix/real.test.rb b/test/testdata/packager/package-prefix-enforcement/critic_prefix/real.test.rb index d65f208dfd..32e5f226c4 100644 --- a/test/testdata/packager/package-prefix-enforcement/critic_prefix/real.test.rb +++ b/test/testdata/packager/package-prefix-enforcement/critic_prefix/real.test.rb @@ -1,10 +1,12 @@ # typed: strict module Critic::SomePkg::Real + # ^^^^^^^^^^^^^^^^^^^^^ error: Tests in the `Critic::SomePkg` package must define tests in the `Test::Critic::SomePkg` namespace FOO = 1 end -Critic::SomePkg::RealConst = 2 + Critic::SomePkg::RealConst = 2 +#^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Tests in the `Critic::SomePkg` package must define tests in the `Test::Critic::SomePkg` namespace # Allowed Test::Critic::SomePkg::SomeTestConst = 3 diff --git a/test/testdata/packager/rbi_gen/__package.rbi-gen.exp b/test/testdata/packager/rbi_gen/__package.rbi-gen.exp index 02945c9fff..8a4fcb62c8 100644 --- a/test/testdata/packager/rbi_gen/__package.rbi-gen.exp +++ b/test/testdata/packager/rbi_gen/__package.rbi-gen.exp @@ -102,6 +102,8 @@ class RBIGen::Public::MyEnum < T::Enum abstract! sealed! sig {returns(String)} + def serialize; end + sig {returns(String)} def to_string; end extend T::Sig extend T::Helpers diff --git a/test/testdata/packager/shared_prefix/pass.package-tree.exp b/test/testdata/packager/shared_prefix/pass.package-tree.exp index 03d14d1877..227f24fd35 100644 --- a/test/testdata/packager/shared_prefix/pass.package-tree.exp +++ b/test/testdata/packager/shared_prefix/pass.package-tree.exp @@ -1,30 +1,30 @@ # -- test/testdata/packager/shared_prefix/bar/that/__package.rb -- class <>> < (::) - class ::::::::<>> < (::) + class ::::::::<>> < (::PackageSpec) .export(::::::::::) end end -# -- test/testdata/packager/shared_prefix/bar/that/that.rb -- +# -- test/testdata/packager/shared_prefix/bar/this/__package.rb -- class <>> < (::) - module ::::::<>> < () - :: = (:yeah, , ::) + class ::::::::<>> < (::PackageSpec) + .export(::::::::::) end end -# -- test/testdata/packager/shared_prefix/bar/this/__package.rb -- +# -- test/testdata/packager/shared_prefix/foo/__package.rb -- class <>> < (::) - class ::::::::<>> < (::) - .export(::::::::::) + class ::::::<>> < (::PackageSpec) end end -# -- test/testdata/packager/shared_prefix/bar/this/this.rb -- +# -- test/testdata/packager/shared_prefix/bar/that/that.rb -- class <>> < (::) - module ::::::<>> < () - :: = (:hey, , ::) + module ::::::<>> < () + :: = (:yeah, , ::) end end -# -- test/testdata/packager/shared_prefix/foo/__package.rb -- +# -- test/testdata/packager/shared_prefix/bar/this/this.rb -- class <>> < (::) - class ::::::<>> < (::) + module ::::::<>> < () + :: = (:hey, , ::) end end # -- test/testdata/packager/shared_prefix/foo/foo.rb -- diff --git a/test/testdata/packager/simple_package/pass.package-tree.exp b/test/testdata/packager/simple_package/pass.package-tree.exp index f0bf003e80..d9a46e6eb7 100644 --- a/test/testdata/packager/simple_package/pass.package-tree.exp +++ b/test/testdata/packager/simple_package/pass.package-tree.exp @@ -1,6 +1,6 @@ # -- test/testdata/packager/simple_package/bar/__package.rb -- class <>> < (::) - class ::::::<>> < (::) + class ::::::<>> < (::PackageSpec) .import(::::::) .export(::::::::) @@ -8,6 +8,16 @@ class <>> < (::) .export(::::::::) end end +# -- test/testdata/packager/simple_package/foo/__package.rb -- +class <>> < (::) + class ::::::<>> < (::PackageSpec) + .import(::::::) + + .export(::::::::) + + .export(::::::::) + end +end # -- test/testdata/packager/simple_package/bar/bar.rb -- class <>> < (::) class ::::::<>> < (::) @@ -50,16 +60,6 @@ class <>> < (::) end end -# -- test/testdata/packager/simple_package/foo/__package.rb -- -class <>> < (::) - class ::::::<>> < (::) - .import(::::::) - - .export(::::::::) - - .export(::::::::) - end -end # -- test/testdata/packager/simple_package/foo/calls_bar.rb -- class <>> < (::) module ::::::<>> < () diff --git a/test/testdata/packager/simple_test_import/main_lib/lib.rb b/test/testdata/packager/simple_test_import/main_lib/lib.rb index 65e55991f8..9de9ae63f7 100644 --- a/test/testdata/packager/simple_test_import/main_lib/lib.rb +++ b/test/testdata/packager/simple_test_import/main_lib/lib.rb @@ -12,6 +12,5 @@ class Project::MainLib::Lib # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `Test::Project::Util::UtilHelper` is defined in a test namespace Test::Project::Util::Unexported -# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `Test::Project::Util::Unexported` resolves but is not exported from `Project::Util` # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `Test::Project::Util::Unexported` is defined in a test namespace end diff --git a/test/testdata/packager/simple_test_import/main_lib/lib.test.rb b/test/testdata/packager/simple_test_import/main_lib/lib.test.rb index fc76040e01..2e027cf12e 100644 --- a/test/testdata/packager/simple_test_import/main_lib/lib.test.rb +++ b/test/testdata/packager/simple_test_import/main_lib/lib.test.rb @@ -9,7 +9,7 @@ class Test::Project::MainLib::LibTest # Tests can access `test_import` names Project::TestOnly::SomeHelper.new # access via test_import - Test::Project::Util::UtilHelper # allowd by import + Test::Project::Util::UtilHelper # allowed by import Test::Project::Util::Unexported # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `Test::Project::Util::Unexported` resolves but is not exported from `Project::Util` diff --git a/test/testdata/packager/simple_test_import/pass.package-tree.exp b/test/testdata/packager/simple_test_import/pass.package-tree.exp index d5b37053e0..0ae6a8b878 100644 --- a/test/testdata/packager/simple_test_import/pass.package-tree.exp +++ b/test/testdata/packager/simple_test_import/pass.package-tree.exp @@ -1,6 +1,6 @@ # -- test/testdata/packager/simple_test_import/main_lib/__package.rb -- class <>> < (::) - class ::::::<>> < (::) + class ::::::<>> < (::PackageSpec) .import(::::::) .test_import(::::::) @@ -8,6 +8,20 @@ class <>> < (::) .export_for_test(::::::) end end +# -- test/testdata/packager/simple_test_import/test_only/__package.rb -- +class <>> < (::) + class ::::::<>> < (::PackageSpec) + .export(::::::::) + end +end +# -- test/testdata/packager/simple_test_import/util/__package.rb -- +class <>> < (::) + class ::::::<>> < (::PackageSpec) + .export(::::::::) + + .export(::::::::::) + end +end # -- test/testdata/packager/simple_test_import/main_lib/lib.rb -- class <>> < (::) class ::::::<>> < (::) @@ -34,25 +48,11 @@ class <>> < (::) :::::::: end end -# -- test/testdata/packager/simple_test_import/test_only/__package.rb -- -class <>> < (::) - class ::::::<>> < (::) - .export(::::::::) - end -end # -- test/testdata/packager/simple_test_import/test_only/some_helper.rb -- class <>> < (::) class ::::::<>> < (::) end end -# -- test/testdata/packager/simple_test_import/util/__package.rb -- -class <>> < (::) - class ::::::<>> < (::) - .export(::::::::) - - .export(::::::::::) - end -end # -- test/testdata/packager/simple_test_import/util/my_util.rb -- class <>> < (::) class ::::::<>> < (::) diff --git a/test/testdata/packager/skip_check/library/__package.rb b/test/testdata/packager/skip_check/library/__package.rb new file mode 100644 index 0000000000..e872f37a38 --- /dev/null +++ b/test/testdata/packager/skip_check/library/__package.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true +# typed: strict +# enable-packager: true + +class Library < PackageSpec + export Library::Constant +end diff --git a/test/testdata/packager/skip_check/library/_impl.rb b/test/testdata/packager/skip_check/library/_impl.rb new file mode 100644 index 0000000000..9995a9a936 --- /dev/null +++ b/test/testdata/packager/skip_check/library/_impl.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true +# typed: strict + +module Library + Constant = :yeah_baby + UnexportedConstant = :no_baby +end diff --git a/test/testdata/packager/skip_check/normal_package/__package.rb b/test/testdata/packager/skip_check/normal_package/__package.rb new file mode 100644 index 0000000000..3ce05c36b9 --- /dev/null +++ b/test/testdata/packager/skip_check/normal_package/__package.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true +# typed: strict +# enable-packager: true + +class NormalPackage < PackageSpec + import Library +end diff --git a/test/testdata/packager/skip_check/normal_package/_impl.rb b/test/testdata/packager/skip_check/normal_package/_impl.rb new file mode 100644 index 0000000000..f4f22359c1 --- /dev/null +++ b/test/testdata/packager/skip_check/normal_package/_impl.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true +# typed: strict + +module NormalPackage + # acceptable, because `Library::Constant` is exported + Library::Constant + # unacceptable! + Library::UnexportedConstant # error: `Library::UnexportedConstant` resolves but is not exported +end diff --git a/test/testdata/packager/skip_check/special_package/__package.rb b/test/testdata/packager/skip_check/special_package/__package.rb new file mode 100644 index 0000000000..aa9f195fea --- /dev/null +++ b/test/testdata/packager/skip_check/special_package/__package.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true +# typed: strict +# enable-packager: true +# allow-relaxed-packager-checks-for: SpecialPackage + +class SpecialPackage < PackageSpec + import Library +end diff --git a/test/testdata/packager/skip_check/special_package/_impl.rb b/test/testdata/packager/skip_check/special_package/_impl.rb new file mode 100644 index 0000000000..9d1ae10994 --- /dev/null +++ b/test/testdata/packager/skip_check/special_package/_impl.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true +# typed: strict + +module SpecialPackage + # acceptable, because `Library::Constant` is exported + Library::Constant + # acceptable, because `SpecialPackage` is opted in to bypass typical checks + Library::UnexportedConstant +end diff --git a/test/testdata/packager/unimported_namespace/pass.package-tree.exp b/test/testdata/packager/unimported_namespace/pass.package-tree.exp index 30e5a5812d..5e414d2315 100644 --- a/test/testdata/packager/unimported_namespace/pass.package-tree.exp +++ b/test/testdata/packager/unimported_namespace/pass.package-tree.exp @@ -1,9 +1,20 @@ # -- test/testdata/packager/unimported_namespace/aaa/__package.rb -- class <>> < (::) - class ::::<>> < (::) + class ::::<>> < (::PackageSpec) .export(::::::) end end +# -- test/testdata/packager/unimported_namespace/bbb/__package.rb -- +class <>> < (::) + class ::::<>> < (::PackageSpec) + .import(::::) + end +end +# -- test/testdata/packager/unimported_namespace/ccc/__package.rb -- +class <>> < (::) + class ::::<>> < (::PackageSpec) + end +end # -- test/testdata/packager/unimported_namespace/aaa/a_class.rb -- class <>> < (::) class ::::<>> < (::) @@ -14,19 +25,8 @@ class <>> < (::) :: end end -# -- test/testdata/packager/unimported_namespace/bbb/__package.rb -- -class <>> < (::) - class ::::<>> < (::) - .import(::::) - end -end # -- test/testdata/packager/unimported_namespace/bbb/b_class.rb -- class <>> < (::) class ::::<>> < (::) end end -# -- test/testdata/packager/unimported_namespace/ccc/__package.rb -- -class <>> < (::) - class ::::<>> < (::) - end -end diff --git a/test/testdata/packager/visibility/foo/__package.rb b/test/testdata/packager/visibility/foo/__package.rb index 27e483ee21..992542f7af 100644 --- a/test/testdata/packager/visibility/foo/__package.rb +++ b/test/testdata/packager/visibility/foo/__package.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # typed: strict # enable-packager: true -# skip-package-import-visibility-check-for: SkipCheck::For +# allow-relaxed-packager-checks-for: SkipCheck::For class Foo < PackageSpec visible_to Bar @@ -11,4 +11,22 @@ class Foo < PackageSpec visible_to 'tests' visible_to 'a different string' # ^^^^^^^^^^^^^^^^^^^^ error: Argument to `visible_to` must be a constant or + + visible_to Nested::* + + visible_to Nested::*(0) + # ^^^^^^^^^^^^ error: Argument to `visible_to` must be a constant or + # ^^^^^^ error: Unable to resolve constant `Nested` + visible_to Nested::*(x: 0) + # ^^^^^^^^^^^^^^^ error: Argument to `visible_to` must be a constant or + # ^^^^^^ error: Unable to resolve constant `Nested` + visible_to Nested::* {} + # ^^^^^^^^^^^^ error: Argument to `visible_to` must be a constant or + # ^^^^^^^^^^^^ error: Invalid expression in package: `Block` not allowed + # ^^^^^^ error: Unable to resolve constant `Nested` + + visible_to Nested::*::Blah + # ^^^^^^^^^^^^^^^ error: Argument to `visible_to` must be a constant + # ^^^^^^ error: Unable to resolve constant `Nested` + # ^^^^^^^^^^^^^^^ error: Dynamic constant references end diff --git a/test/testdata/packager/visibility/nested/that/__package.rb b/test/testdata/packager/visibility/nested/that/__package.rb new file mode 100644 index 0000000000..8206bc7a8d --- /dev/null +++ b/test/testdata/packager/visibility/nested/that/__package.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true +# typed: strict +# enable-packager: true +# allow-relaxed-packager-checks-for: SkipCheck::For + +class Nested::That < PackageSpec + import Foo +end diff --git a/test/testdata/packager/visibility/nested/this/__package.rb b/test/testdata/packager/visibility/nested/this/__package.rb new file mode 100644 index 0000000000..5830745d24 --- /dev/null +++ b/test/testdata/packager/visibility/nested/this/__package.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true +# typed: strict +# enable-packager: true +# allow-relaxed-packager-checks-for: SkipCheck::For + +class Nested::This < PackageSpec + import Foo +end diff --git a/test/testdata/packager/visibility/nested/this/deeply/nested/package/__package.rb b/test/testdata/packager/visibility/nested/this/deeply/nested/package/__package.rb new file mode 100644 index 0000000000..5a8d61b561 --- /dev/null +++ b/test/testdata/packager/visibility/nested/this/deeply/nested/package/__package.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true +# typed: strict +# enable-packager: true +# allow-relaxed-packager-checks-for: SkipCheck::For + +class Nested::This::Deeply::Nested::Package < PackageSpec + import Foo +end diff --git a/test/testdata/parser/compare_overload_parse_error.rb b/test/testdata/parser/compare_overload_parse_error.rb index 112f8bce4c..9ee56435c0 100644 --- a/test/testdata/parser/compare_overload_parse_error.rb +++ b/test/testdata/parser/compare_overload_parse_error.rb @@ -1,6 +1,6 @@ # typed: false class A # error: class definition in method body - def greater_eqaul>=(foo) # error: unexpected token ">=" + def greater_equal>=(foo) # error: unexpected token ">=" end end diff --git a/test/testdata/parser/misc.rb.desugar-tree.exp b/test/testdata/parser/misc.rb.desugar-tree.exp index 088595b03a..441cc70a42 100644 --- a/test/testdata/parser/misc.rb.desugar-tree.exp +++ b/test/testdata/parser/misc.rb.desugar-tree.exp @@ -25,7 +25,7 @@ class <>> < (::) xaaaaz = [.yayayaya(), .tutututu()] - + nil nil @@ -187,7 +187,7 @@ rescue :: => x begin $14 = ::.(y) $15 = ::.($14, 0, 0) - x = $15.slice(::Range.new(0, -1, false)) + x = $15.to_ary() $14 end diff --git a/test/testdata/rbi/array.rb b/test/testdata/rbi/array.rb index afbd0031d2..48b154bb22 100644 --- a/test/testdata/rbi/array.rb +++ b/test/testdata/rbi/array.rb @@ -70,3 +70,12 @@ T.reveal_type(arr.fetch(0, 'error')) # error: Revealed type: `T.any(Integer, String)` T.reveal_type(arr.fetch(0) { 'error' }) # error: Revealed type: `T.any(Integer, String)` + +x = [[1, "a"], [2, "b"]] +T.reveal_type(x.transpose) # error: Revealed type: `[T::Array[Integer], T::Array[String]] (2-tuple)` +x = [[1, "a"], [2]] +T.reveal_type(x.transpose) # error: Revealed type: `T::Array[T::Array[T.untyped]]` +x = [[3.4], [1, "a"]] +T.reveal_type(x.transpose) # error: Revealed type: `T::Array[T::Array[T.untyped]]` +x = [[3.4, "a"], [:a, 5]] +T.reveal_type(x.transpose) # error: Revealed type: `[T::Array[T.any(Float, Symbol)], T::Array[T.any(Integer, String)]] (2-tuple)` diff --git a/test/testdata/rbi/class.rb b/test/testdata/rbi/class.rb index cb16b11aea..f073bf1df9 100644 --- a/test/testdata/rbi/class.rb +++ b/test/testdata/rbi/class.rb @@ -9,16 +9,16 @@ def self.foo; end Parent.new.singleton_class.attached_object c1 = Class.new -T.reveal_type(c1) # error: Revealed type: `T::Class[Object]` -T.reveal_type(c1.new) # error: Revealed type: `Object` +T.reveal_type(c1) # error: Revealed type: `T::Class[T.untyped]` +T.reveal_type(c1.new) # error: Revealed type: `T.untyped` c2 = Class.new(Parent) T.reveal_type(c2) # error: Revealed type: `T.class_of(Parent)` T.reveal_type(c2.new) # error: Revealed type: `Parent` c3 = Class.new { |cls| cls.superclass } -T.reveal_type(c3) # error: Revealed type: `T::Class[Object]` -T.reveal_type(c3.new) # error: Revealed type: `Object` +T.reveal_type(c3) # error: Revealed type: `T::Class[T.untyped]` +T.reveal_type(c3.new) # error: Revealed type: `T.untyped` c4 = Class.new(Parent) { |cls| cls.superclass } T.reveal_type(c4) # error: Revealed type: `T.class_of(Parent)` diff --git a/test/testdata/rbi/enumerable.rb b/test/testdata/rbi/enumerable.rb index 91bd02a057..56aa209126 100644 --- a/test/testdata/rbi/enumerable.rb +++ b/test/testdata/rbi/enumerable.rb @@ -84,3 +84,14 @@ def example(xs) end T.reveal_type(res) # error: `T.untyped` end + +int_or_str_array = T::Array[T.any(Integer, String)].new +int_array = int_or_str_array.grep(Integer) +T.reveal_type(int_array) # error: `T::Array[Integer]` + +int_or_str_array = T::Array[T.any(Integer, String)].new +bool_array = int_or_str_array.grep(Integer) do |x| + T.reveal_type(x) # error: `Integer` + x > 5 +end +T.reveal_type(bool_array) # error: `T::Array[T::Boolean]` diff --git a/test/testdata/rbi/hash.rb b/test/testdata/rbi/hash.rb index f62f451f9a..2aa05b4c16 100644 --- a/test/testdata/rbi/hash.rb +++ b/test/testdata/rbi/hash.rb @@ -100,3 +100,7 @@ T.assert_type!({a: 1}.shift, T.nilable(T::Array[T.untyped])) T.assert_type!({}.shift, T.nilable(T::Array[T.untyped])) + +[:a, :b].each do |key| + T.assert_type!({a: 1}.slice(key), T::Hash[Symbol, Integer]) +end diff --git a/test/testdata/rbi/io.rb b/test/testdata/rbi/io.rb index 0035024a7e..d3289a4fee 100644 --- a/test/testdata/rbi/io.rb +++ b/test/testdata/rbi/io.rb @@ -31,3 +31,11 @@ def test_io_read_return(io) T.assert_type!(io.read(1), T.nilable(String)) T.assert_type!(io.read, String) end + +sig {void} +def test_io_pipe + result = IO.pipe do + 1 + end + T.assert_type!(result, Integer) +end diff --git a/test/testdata/rbi/json.rb b/test/testdata/rbi/json.rb index fd111ff606..7f2ce5ab92 100644 --- a/test/testdata/rbi/json.rb +++ b/test/testdata/rbi/json.rb @@ -1,41 +1,5 @@ # typed: true -class Array - include JSON::Ext::Generator::GeneratorMethods::Array -end - -class FalseClass - include JSON::Ext::Generator::GeneratorMethods::FalseClass -end - -class Float - include JSON::Ext::Generator::GeneratorMethods::Float -end - -class Hash - include JSON::Ext::Generator::GeneratorMethods::Hash -end - -class Integer - include JSON::Ext::Generator::GeneratorMethods::Integer -end - -class NilClass - include JSON::Ext::Generator::GeneratorMethods::NilClass -end - -class Object - include JSON::Ext::Generator::GeneratorMethods::Object -end - -class String - include JSON::Ext::Generator::GeneratorMethods::String -end - -class TrueClass - include JSON::Ext::Generator::GeneratorMethods::TrueClass -end - T.reveal_type([1, "2", :foo].to_json) # error: Revealed type: `String` T.reveal_type(false.to_json) # error: Revealed type: `String` T.reveal_type(1.23.to_json) # error: Revealed type: `String` diff --git a/test/testdata/rbi/kernel.rb b/test/testdata/rbi/kernel.rb index 93d68f34f9..10f8065858 100644 --- a/test/testdata/rbi/kernel.rb +++ b/test/testdata/rbi/kernel.rb @@ -5,6 +5,10 @@ T.assert_type!(caller, T::Array[String]) T.assert_type!(caller(10), T.nilable(T::Array[String])) +T.assert_type!(caller_locations, T::Array[Thread::Backtrace::Location]) +T.assert_type!(caller_locations(2, 10), T.nilable(T::Array[Thread::Backtrace::Location])) +T.assert_type!(caller_locations(1..10), T.nilable(T::Array[Thread::Backtrace::Location])) + T.assert_type!(BigDecimal('123', 1, exception: true), BigDecimal) T.assert_type!(Complex('123', exception: false), Complex) T.assert_type!(Float('123.0'), Float) diff --git a/test/testdata/rbi/regexp.rb b/test/testdata/rbi/regexp.rb index 86d5068095..62a9820bfd 100644 --- a/test/testdata/rbi/regexp.rb +++ b/test/testdata/rbi/regexp.rb @@ -37,3 +37,5 @@ T.reveal_type(Regexp.timeout = 3.0) # error: type: `Float(3.000000)` T.reveal_type(Regexp.timeout) # error: type: `T.nilable(Float)` T.reveal_type(Regexp.timeout = nil) # error: type: `NilClass` + +Regexp::TimeoutError.new diff --git a/test/testdata/rbi/ruby_typer.rb b/test/testdata/rbi/ruby_typer.rb index daccd24fa8..bf1a71bead 100644 --- a/test/testdata/rbi/ruby_typer.rb +++ b/test/testdata/rbi/ruby_typer.rb @@ -7,5 +7,5 @@ sig {params(x: Sorbet::Private::Static::IOLike).void} def foo(x) - T.reveal_type(x) # error: Revealed type: `T.any(IO, StringIO)` + T.reveal_type(x) # error: Revealed type: `T.any(IO, StringIO, Tempfile)` end diff --git a/test/testdata/resolver/abstract_override.rb b/test/testdata/resolver/abstract_override.rb index fc713a5914..6570ba2e7c 100644 --- a/test/testdata/resolver/abstract_override.rb +++ b/test/testdata/resolver/abstract_override.rb @@ -35,7 +35,7 @@ class Abstract include InterfaceChild end -class AbstractBadChild < Abstract # error-with-dupes: Missing definition for abstract method `Interface#a_method` +class AbstractBadChild < Abstract # error: Missing definition for abstract method `Interface#a_method` end class AbstractChild < Abstract @@ -68,7 +68,7 @@ class AbstractSingleton def self.abstract_method; end end -class AbstractSingletonBadChild < AbstractSingleton # error-with-dupes: Missing definition for abstract method +class AbstractSingletonBadChild < AbstractSingleton # error: Missing definition for abstract method end class AbstractSingletonGoodChild < AbstractSingleton diff --git a/test/testdata/resolver/abstract_validation.rb b/test/testdata/resolver/abstract_validation.rb index c11c6f8381..12679f72bf 100644 --- a/test/testdata/resolver/abstract_validation.rb +++ b/test/testdata/resolver/abstract_validation.rb @@ -52,15 +52,14 @@ def self.foo; end # error: Static methods in a module cannot be abstract # it fails if a concrete module doesn't implement abstract methods module M2 -# ^^^^^^^^^ error: Missing definition for abstract method `AbstractMixin#bar` -# ^^^^^^^^^ error: Missing definition for abstract method `AbstractMixin#foo` +# ^^^^^^^^^ error: Missing definitions for abstract methods in `M2` extend T::Helpers include AbstractMixin end # it fails if a class method is unimplemented class C3 < AbstractClass -# ^^^^^^^^^^^^^^^^^^^^^^^^ error: Missing definition for abstract method `AbstractClass.foo` +# ^^^^^^^^^^^^^^^^^^^^^^^^ error: Missing definition for abstract method `AbstractClass.foo` in `T.class_of(C3)` extend T::Sig extend T::Helpers sig { override.returns(Object) } @@ -69,7 +68,7 @@ def bar; end # it fails if an instance method is unimplemented class C4 < AbstractClass -# ^^^^^^^^^^^^^^^^^^^^^^^^ error: Missing definition for abstract method `AbstractClass#bar` +# ^^^^^^^^^^^^^^^^^^^^^^^^ error: Missing definition for abstract method `AbstractClass#bar` in `C4` extend T::Sig extend T::Helpers sig {override.returns(Object)} @@ -164,5 +163,5 @@ module BadTypedImpl include GoodInterface sig {override.returns(Integer)} - def foo; 1; end # error: Return type `Integer` does not match return type of abstract method `GoodInterface#foo` + def foo; 1; end end diff --git a/test/testdata/resolver/alias_chain.rb.resolve-tree.exp b/test/testdata/resolver/alias_chain.rb.resolve-tree.exp index ca84d78c6a..86aebdf9dc 100644 --- a/test/testdata/resolver/alias_chain.rb.resolve-tree.exp +++ b/test/testdata/resolver/alias_chain.rb.resolve-tree.exp @@ -1,30 +1,15 @@ -begin - class <>> < (::) - begin - module ::Constants<> < () - ::Constants::MAX_NUM_ADDITIONAL_DEVBOXES = 200 - end - ::Sorbet::Private::Static.keep_for_ide(::Constants) - - end - - begin - module ::Routes::Helpers<> < () - ::Routes::Helpers::MAX_NUM_ADDITIONAL_DEVBOXES = ::Constants::MAX_NUM_ADDITIONAL_DEVBOXES - end - ::Sorbet::Private::Static.keep_for_ide(::Routes::Helpers) - - end +class <>> < (::) + module ::Constants<> < () + ::Constants::MAX_NUM_ADDITIONAL_DEVBOXES = 200 + end - begin - module ::Routes::WillCollapseOut<> < () - ::Routes::WillCollapseOut::Helpers = ::Routes::Helpers - end - ::Sorbet::Private::Static.keep_for_ide(::Routes::WillCollapseOut) - - end + module ::Routes::Helpers<> < () + ::Routes::Helpers::MAX_NUM_ADDITIONAL_DEVBOXES = ::Constants::MAX_NUM_ADDITIONAL_DEVBOXES + end - ::Routes::Helpers::MAX_NUM_ADDITIONAL_DEVBOXES + module ::Routes::WillCollapseOut<> < () + ::Routes::WillCollapseOut::Helpers = ::Routes::Helpers end - + + ::Routes::Helpers::MAX_NUM_ADDITIONAL_DEVBOXES end diff --git a/test/testdata/resolver/alias_suggest_unsafe.rb b/test/testdata/resolver/alias_suggest_unsafe.rb new file mode 100644 index 0000000000..77d26e9735 --- /dev/null +++ b/test/testdata/resolver/alias_suggest_unsafe.rb @@ -0,0 +1,7 @@ +# typed: true +# enable-suggest-unsafe: true + +class Child + alias_method :bar, :foo + # ^^^^ error: Can't make method alias from `bar` to non existing method `foo` +end diff --git a/test/testdata/resolver/alias_suggest_unsafe.rb.autocorrects.exp b/test/testdata/resolver/alias_suggest_unsafe.rb.autocorrects.exp new file mode 100644 index 0000000000..fdd7939e29 --- /dev/null +++ b/test/testdata/resolver/alias_suggest_unsafe.rb.autocorrects.exp @@ -0,0 +1,9 @@ +# -- test/testdata/resolver/alias_suggest_unsafe.rb -- +# typed: true +# enable-suggest-unsafe: true + +class Child + T.unsafe(self).alias_method :bar, :foo + # ^^^^ error: Can't make method alias from `bar` to non existing method `foo` +end +# ------------------------------ diff --git a/test/testdata/resolver/ancestor_scope.rb.flatten-tree.exp b/test/testdata/resolver/ancestor_scope.rb.flatten-tree.exp index d0bab47f0b..d4e238794a 100644 --- a/test/testdata/resolver/ancestor_scope.rb.flatten-tree.exp +++ b/test/testdata/resolver/ancestor_scope.rb.flatten-tree.exp @@ -1,19 +1,11 @@ begin - class <>> < (::) + + + + def self.<$CENSORED>() begin - begin - - ::Sorbet::Private::Static.keep_for_ide(::Other) - - end - begin - - ::Sorbet::Private::Static.keep_for_ide(::Test) - ::Sorbet::Private::Static.keep_for_ide(::Other) - - end (::Test.new(), Test::Mixin, ::Test::Mixin) (::Test.new(), Test::Other, ::Test::Other) (::Test.new(), Other, ::Other) @@ -27,18 +19,12 @@ begin end end class ::Test<> < (::Other, ::Test::Mixin, ::Test::Other) + + + + def self.() begin - begin - - ::Sorbet::Private::Static.keep_for_ide(::Test::Mixin) - - end - begin - - ::Sorbet::Private::Static.keep_for_ide(::Test::Other) - - end .include(::Test::Mixin) .include(::Test::Other) diff --git a/test/testdata/resolver/assume_type_class_alias.rb b/test/testdata/resolver/assume_type_class_alias.rb new file mode 100644 index 0000000000..c31c72853e --- /dev/null +++ b/test/testdata/resolver/assume_type_class_alias.rb @@ -0,0 +1,15 @@ +# typed: strict + +class A; end + +AliasToA = A +X = AliasToA.new +T.reveal_type(X) # error: `A` + +TypeAliasToA = T.type_alias { A } + +# There should also be a type error on the call to `.new`, but that's also broken. +# See test/testdata/infer/metatype_new.rb for more. +Y = TypeAliasToA.new # error: Constants must have type annotations with `T.let` when specifying `# typed: strict` +T.reveal_type(Y) # error: `T.untyped` + diff --git a/test/testdata/resolver/bad_alias.rb b/test/testdata/resolver/bad_alias.rb index 988fcb75bf..e0340ebaf2 100644 --- a/test/testdata/resolver/bad_alias.rb +++ b/test/testdata/resolver/bad_alias.rb @@ -1,10 +1,9 @@ # typed: true -# disable-fast-path: true class A extend T::Generic B = type_member - S = T.type_alias {Integer} # error: Type aliases are not allowed in generic classes + S = T.type_alias {Integer} end diff --git a/test/testdata/resolver/basic_constant_out_of_order__1.rb b/test/testdata/resolver/basic_constant_out_of_order__1.rb index 894ac91799..662960608b 100644 --- a/test/testdata/resolver/basic_constant_out_of_order__1.rb +++ b/test/testdata/resolver/basic_constant_out_of_order__1.rb @@ -2,9 +2,8 @@ # typed: false module Foo - # This reports an error despite Foo::X also having a definition in the RBI file. - A = X - # ^ error: `Foo::X` referenced before it is defined + # Sorbet doesn't report an error because Foo::X also has a definition in the RBI file. + A = X def self.foo(arg:) X # this is ok diff --git a/test/testdata/resolver/class_instance_vars.rb.flatten-tree.exp b/test/testdata/resolver/class_instance_vars.rb.flatten-tree.exp index dd9b6eff7a..9d0367dc46 100644 --- a/test/testdata/resolver/class_instance_vars.rb.flatten-tree.exp +++ b/test/testdata/resolver/class_instance_vars.rb.flatten-tree.exp @@ -1,33 +1,15 @@ begin - class <>> < (::) + + + + + + + + def self.<$CENSORED>() - begin - begin - - ::Sorbet::Private::Static.keep_for_ide(::Parent) - - end - begin - - ::Sorbet::Private::Static.keep_for_ide(::Mixin) - - end - begin - - ::Sorbet::Private::Static.keep_for_ide(::Child) - ::Sorbet::Private::Static.keep_for_ide(::Parent) - - end - ::Alias = ::Parent - begin - - ::Sorbet::Private::Static.keep_for_ide(::Child1) - ::Sorbet::Private::Static.keep_for_ide(::Alias) - - end - - end + ::Alias = ::Parent end end class ::Parent<> < (::) diff --git a/test/testdata/resolver/defined.rb b/test/testdata/resolver/defined.rb index 8232ddc430..730c305276 100644 --- a/test/testdata/resolver/defined.rb +++ b/test/testdata/resolver/defined.rb @@ -2,3 +2,4 @@ class Foo end T.reveal_type(defined?(Foo)) # error: T.nilable(String) +T.reveal_type(defined?(@foo)) # error: `T.nilable(String)` diff --git a/test/testdata/resolver/duplicate_abstract_errors.rb b/test/testdata/resolver/duplicate_abstract_errors.rb index 813312b107..ed582abb82 100644 --- a/test/testdata/resolver/duplicate_abstract_errors.rb +++ b/test/testdata/resolver/duplicate_abstract_errors.rb @@ -8,8 +8,8 @@ class Parent def self.foo; end end -class Child < Parent - class << self # error: Missing definition for abstract method `Parent.foo` +class Child < Parent # error: Missing definition for abstract method `Parent.foo` + class << self extend T::Sig end end diff --git a/test/testdata/resolver/field.rb.flatten-tree-raw.exp b/test/testdata/resolver/field.rb.flatten-tree-raw.exp index 1295c10bc1..d754340296 100644 --- a/test/testdata/resolver/field.rb.flatten-tree-raw.exp +++ b/test/testdata/resolver/field.rb.flatten-tree-raw.exp @@ -1,44 +1,38 @@ -InsSeq{ - stats = [ - EmptyTree - ClassDef{ - kind = class - name = EmptyTree - symbol = >> - ancestors = [ConstantLit{ - symbol = (class ::) - orig = nullptr +ClassDef{ + kind = class + name = EmptyTree + symbol = >> + ancestors = [ConstantLit{ + symbol = (class ::) + orig = nullptr + }] + rhs = [ + MethodDef{ + flags = {} + name = <> + args = [Local{ + localVariable = + }, Local{ + localVariable = > }] - rhs = [ - MethodDef{ - flags = {} - name = <> - args = [Local{ - localVariable = - }, Local{ - localVariable = > - }] - rhs = Assign{ - lhs = UnresolvedIdent{ - kind = Instance - name = - } - rhs = Local{ - localVariable = - } - } + rhs = Assign{ + lhs = UnresolvedIdent{ + kind = Instance + name = } - - MethodDef{ - flags = {self} - name = ><> $CENSORED>> - args = [Local{ - localVariable = > - }] - rhs = + rhs = Local{ + localVariable = } - ] + } + } + + MethodDef{ + flags = {self} + name = ><> $CENSORED>> + args = [Local{ + localVariable = > + }] + rhs = } - ], - expr = EmptyTree + ] } diff --git a/test/testdata/resolver/flatten.rb.flatten-tree.exp b/test/testdata/resolver/flatten.rb.flatten-tree.exp index 48ba08cadb..860a90faed 100644 --- a/test/testdata/resolver/flatten.rb.flatten-tree.exp +++ b/test/testdata/resolver/flatten.rb.flatten-tree.exp @@ -1,13 +1,9 @@ begin - class <>> < (::) + + def self.<$CENSORED>() begin - begin - - ::Sorbet::Private::Static.keep_for_ide(::A) - - end ::A.new().foo() ::A.foo() ::A.new().food() diff --git a/test/testdata/resolver/fuzz_ancester.rb b/test/testdata/resolver/fuzz_ancestor.rb similarity index 100% rename from test/testdata/resolver/fuzz_ancester.rb rename to test/testdata/resolver/fuzz_ancestor.rb diff --git a/test/testdata/resolver/generic_enum.rb b/test/testdata/resolver/generic_enum.rb index 429c39efcf..9f487e0a14 100644 --- a/test/testdata/resolver/generic_enum.rb +++ b/test/testdata/resolver/generic_enum.rb @@ -14,14 +14,14 @@ class MyEnum < T::Enum extend T::Sig sig {params(xy: T.any(MyEnum::X[Integer], MyEnum::Y[Integer])).void} # ^^^^^^^^^^^^^^^^^^ error: Expected a class or module -# ^^^^^^^^^^^^^^^^^^ error: Method `[]` does not exist +# ^^^^^^^^^ error: Method `[]` does not exist # ^^^^^^^^^^^^^^^^^^ error: Expected a class or module -# ^^^^^^^^^^^^^^^^^^ error: Method `[]` does not exist +# ^^^^^^^^^ error: Method `[]` does not exist def foo(xy) end XY = T.type_alias {T.any(MyEnum::X[Integer], MyEnum::Y[Integer])} # ^^^^^^^^^^^^^^^^^^ error: Expected a class or module -# ^^^^^^^^^^^^^^^^^^ error: Method `[]` does not exist +# ^^^^^^^^^ error: Method `[]` does not exist # ^^^^^^^^^^^^^^^^^^ error: Expected a class or module -# ^^^^^^^^^^^^^^^^^^ error: Method `[]` does not exist +# ^^^^^^^^^ error: Method `[]` does not exist diff --git a/test/testdata/resolver/generic_type_alias.rb b/test/testdata/resolver/generic_type_alias.rb new file mode 100644 index 0000000000..e38893d065 --- /dev/null +++ b/test/testdata/resolver/generic_type_alias.rb @@ -0,0 +1,82 @@ +# typed: true +class Module; include T::Sig; end + +class Box + extend T::Generic + Elem = type_member + + sig { params(val: Elem).void } + def initialize(val); @val = val; end + + sig { returns(Elem) } + def val; @val; end +end + +class ComplicatedBox < Box + ReturnType = T.type_alias do + T.any(Integer, String, Float, Symbol) + end + Elem = type_member { {fixed: ReturnType} } +end + +sig { params(box: ComplicatedBox).returns(ComplicatedBox::ReturnType) } +def example_good3(box) + T.reveal_type(box.val) # error: `T.any(Integer, String, Float, Symbol)` + nil # error: Expected `T.any(Integer, String, Float, Symbol)` but found `NilClass` for method result type +end + +class BoxBad + extend T::Generic + abstract! + + Elem = type_member + MyElem = T.type_alias { Elem } # error: Defining a `type_alias` to a generic `type_member` is not allowed + MyElem2 = T.type_alias { T.nilable(Elem) } # error: Defining a `type_alias` to a generic `type_member` is not allowed + + sig { abstract.returns(Elem) } + def returns_elem; end + + sig { returns(MyElem) } + def returns_my_elem + T.let(returns_my_elem, Elem) + T.let(returns_my_elem, MyElem) + my_elem = returns_my_elem + T.reveal_type(my_elem) # error: `T.untyped` + nil + end + + sig { returns(MyElem) } + def self.returns_my_elem + T.let( + returns_my_elem, + Elem # error: `type_member` type `Elem` used in a singleton method definition + ) + T.let( + returns_my_elem, + MyElem + ) + my_elem = returns_my_elem + T.reveal_type(my_elem) # error: `T.untyped` + nil + end +end + +sig do + params( + x: BoxBad::Elem, + # ^^^^^^^^^^^^ error: `type_member` type `BoxBad::Elem` used outside of the class definition + y: BoxBad::MyElem, + z: BoxBad::MyElem2 + ) + .void +end +def bad_box_example( + x, + y, + z +) + T.reveal_type(x) # error: `T.untyped` + T.reveal_type(y) # error: `T.untyped` + T.reveal_type(z) # error: `T.untyped` +end + diff --git a/test/testdata/resolver/invalid_alias.rb.symbol-table-raw.exp b/test/testdata/resolver/invalid_alias.rb.symbol-table-raw.exp index f07425c379..f3229e69e4 100644 --- a/test/testdata/resolver/invalid_alias.rb.symbol-table-raw.exp +++ b/test/testdata/resolver/invalid_alias.rb.symbol-table-raw.exp @@ -13,7 +13,7 @@ class >> < > () type-member(+) > $1> $1>::>> -> LambdaParam(> $1> $1>::>>, lower=T.noreturn, upper=AppliedType { klass = > $1> targs = [ >> = BadAliasingClassMethod1 ] }) @ Loc {file=test/testdata/resolver/invalid_alias.rb start=4:3 end=4:8} method > $1> $1>#> () @ Loc {file=test/testdata/resolver/invalid_alias.rb start=4:3 end=10:6} argument @ Loc {file=test/testdata/resolver/invalid_alias.rb start=??? end=???} - class > < > (>) @ Loc {file=https://github.com/sorbet/sorbet/tree/master/rbi/core/object.rbi start=removed end=removed} + class > < > (>, >) @ (Loc {file=https://github.com/sorbet/sorbet/tree/master/rbi/stdlib/json.rbi start=removed end=removed}, Loc {file=https://github.com/sorbet/sorbet/tree/master/rbi/core/object.rbi start=removed end=removed}) method ># : private () @ Loc {file=test/testdata/resolver/invalid_alias.rb start=13:1 end=13:9} argument @ Loc {file=test/testdata/resolver/invalid_alias.rb start=??? end=???} diff --git a/test/testdata/resolver/linearization/includes_class.rb.symbol-table-raw.exp b/test/testdata/resolver/linearization/includes_class.rb.symbol-table-raw.exp index 4a0941a212..89ca68a6d4 100644 --- a/test/testdata/resolver/linearization/includes_class.rb.symbol-table-raw.exp +++ b/test/testdata/resolver/linearization/includes_class.rb.symbol-table-raw.exp @@ -7,15 +7,15 @@ class >> < > () type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=A) @ Loc {file=test/testdata/resolver/linearization/includes_class.rb start=5:1 end=5:14} method > $1>#> () @ Loc {file=test/testdata/resolver/linearization/includes_class.rb start=5:1 end=6:4} argument @ Loc {file=test/testdata/resolver/linearization/includes_class.rb start=??? end=???} - class > < > (>, >, >, >, >) @ Loc {file=test/testdata/resolver/linearization/includes_class.rb start=8:1 end=8:8} - class > $1>[>>] < > $1> (>, >, >, >, >) @ Loc {file=test/testdata/resolver/linearization/includes_class.rb start=8:1 end=8:8} + class > < > (>, >, >, >, >, >) @ Loc {file=test/testdata/resolver/linearization/includes_class.rb start=8:1 end=8:8} + class > $1>[>>] < > $1> (>, >, >, >, >, >) @ Loc {file=test/testdata/resolver/linearization/includes_class.rb start=8:1 end=8:8} type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=B) @ Loc {file=test/testdata/resolver/linearization/includes_class.rb start=8:1 end=8:8} method > $1>#> () @ Loc {file=test/testdata/resolver/linearization/includes_class.rb start=8:1 end=11:4} argument @ Loc {file=test/testdata/resolver/linearization/includes_class.rb start=??? end=???} - module > < >::>::>::> (>, >, >, >, >) @ Loc {file=test/testdata/resolver/linearization/includes_class.rb start=13:1 end=13:9} + module > < >::>::>::> (>, >, >, >, >, >) @ Loc {file=test/testdata/resolver/linearization/includes_class.rb start=13:1 end=13:9} method ># () @ Loc {file=test/testdata/resolver/linearization/includes_class.rb start=16:3 end=16:10} argument @ Loc {file=test/testdata/resolver/linearization/includes_class.rb start=??? end=???} - class > $1> < > (>, >, >, >, >) @ Loc {file=test/testdata/resolver/linearization/includes_class.rb start=13:1 end=13:9} + class > $1> < > (>, >, >, >, >, >) @ Loc {file=test/testdata/resolver/linearization/includes_class.rb start=13:1 end=13:9} method > $1>#> () @ Loc {file=test/testdata/resolver/linearization/includes_class.rb start=13:1 end=19:4} argument @ Loc {file=test/testdata/resolver/linearization/includes_class.rb start=??? end=???} module > < >::>::>::> (>) @ Loc {file=test/testdata/resolver/linearization/includes_class.rb start=21:1 end=21:18} diff --git a/test/testdata/resolver/missing_alias_target.rb.symbol-table.exp b/test/testdata/resolver/missing_alias_target.rb.symbol-table.exp index 79aa2020a2..b5a0299c36 100644 --- a/test/testdata/resolver/missing_alias_target.rb.symbol-table.exp +++ b/test/testdata/resolver/missing_alias_target.rb.symbol-table.exp @@ -3,5 +3,5 @@ class :: < ::Object () method ::># () @ test/testdata/resolver/missing_alias_target.rb:2 argument @ Loc {file=test/testdata/resolver/missing_alias_target.rb start=??? end=???} static-field ::A -> Integer @ test/testdata/resolver/missing_alias_target.rb:2 - static-field ::K -> @ test/testdata/resolver/missing_alias_target.rb:4 + static-field ::K -> > @ test/testdata/resolver/missing_alias_target.rb:4 diff --git a/test/testdata/resolver/missing_type_combinator_args.rb.flatten-tree.exp b/test/testdata/resolver/missing_type_combinator_args.rb.flatten-tree.exp index 4c02cc1cc1..c725f4b836 100644 --- a/test/testdata/resolver/missing_type_combinator_args.rb.flatten-tree.exp +++ b/test/testdata/resolver/missing_type_combinator_args.rb.flatten-tree.exp @@ -1,20 +1,16 @@ -begin - - class <>> < (::) - def self.<$CENSORED>() - begin - (nil, T.untyped, ::T.nilable()) - (nil, T.untyped, ::T.nilable(::Integer, ::Integer)) - (nil, T.untyped, ::T.any()) - (nil, T.untyped, ::T.all()) - ::T.must() - ::T.let(1) - (1, Integer, 2) - ::T.reveal_type() - ::T.reveal_type(1, 2) - - end +class <>> < (::) + def self.<$CENSORED>() + begin + (nil, T.untyped, ::T.nilable()) + (nil, T.untyped, ::T.nilable(::Integer, ::Integer)) + (nil, T.untyped, ::T.any()) + (nil, T.untyped, ::T.all()) + ::T.must() + ::T.let(1) + (1, Integer, 2) + ::T.reveal_type() + ::T.reveal_type(1, 2) + end end - end diff --git a/test/testdata/resolver/module_superclass.rb b/test/testdata/resolver/module_superclass.rb index 370d10046c..9ecd4ffc42 100644 --- a/test/testdata/resolver/module_superclass.rb +++ b/test/testdata/resolver/module_superclass.rb @@ -11,7 +11,12 @@ class SubclassObject < Object; end class SubclassModule2 < Module; end # It's not okay to subclass Class -class SubclassClass < Class; end # error: `SubclassClass` is a subclass of `Class` which is not allowed +class SubclassClass < Class # error: `SubclassClass` is a subclass of `Class` which is not allowed + # ... but we can't ENFORCE that it _never_ happens + def example + new + end +end module M; end diff --git a/test/testdata/resolver/multi_alias_method.rb b/test/testdata/resolver/multi_alias_method.rb index a58c7ceb36..0e8d4a7659 100644 --- a/test/testdata/resolver/multi_alias_method.rb +++ b/test/testdata/resolver/multi_alias_method.rb @@ -6,7 +6,7 @@ def to; end alias_method :from, :to alias_method :from, :to_bad # ^^^^^^^ error: Can't make method alias from `from` to non existing method `to_bad` -# ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Redefining method alias `A#from` from `A#to` to `Sorbet::Private::Static#` + # ^^^^^ error: Redefining method alias `A#from` from `A#to` to `Sorbet::Private::Static#` end class B diff --git a/test/testdata/resolver/multi_alias_method.rb.symbol-table.exp b/test/testdata/resolver/multi_alias_method.rb.symbol-table.exp index f9ba5a279f..acbb02cf96 100644 --- a/test/testdata/resolver/multi_alias_method.rb.symbol-table.exp +++ b/test/testdata/resolver/multi_alias_method.rb.symbol-table.exp @@ -13,7 +13,7 @@ class :: < ::Object () method ::# () @ test/testdata/resolver/multi_alias_method.rb:3 argument @ Loc {file=test/testdata/resolver/multi_alias_method.rb start=??? end=???} class ::B < ::Object () @ test/testdata/resolver/multi_alias_method.rb:12 - method ::B#from (does_not_exist1, does_not_exist2) -> > @ test/testdata/resolver/multi_alias_method.rb:13 + method ::B#from (does_not_exist1, does_not_exist2) -> > @ test/testdata/resolver/multi_alias_method.rb:14 argument does_not_exist1 @ Loc {file=test/testdata/resolver/multi_alias_method.rb start=13:23 end=13:39} argument does_not_exist2 @ Loc {file=test/testdata/resolver/multi_alias_method.rb start=14:23 end=14:39} class ::[] < :: () @ test/testdata/resolver/multi_alias_method.rb:12 diff --git a/test/testdata/resolver/multi_assign.rb b/test/testdata/resolver/multi_assign.rb new file mode 100644 index 0000000000..e76a2c8a89 --- /dev/null +++ b/test/testdata/resolver/multi_assign.rb @@ -0,0 +1,54 @@ +# typed: true + +arr0 = [] +a, *b = arr0 +T.reveal_type(a) # error: Revealed type: `NilClass` +T.reveal_type(b) # error: Revealed type: `T::Array[NilClass]` + +b = *arr0 +arr1 = [1, 2, 3] +a, *b = arr1 +T.reveal_type(a) # error: Revealed type: `Integer(1)` +T.reveal_type(b) # error: Revealed type: `T::Array[Integer]` + +arr2 = [1, :foo, "foo"] +a, *b = arr2 +T.reveal_type(a) # error: Revealed type: `Integer(1)` +T.reveal_type(b) # error: Revealed type: `T::Array[T.any(Integer, Symbol, String)]` + +arr3 = T.let([], T::Array[T.untyped]) +a, *b = arr3 +T.reveal_type(a) # error: Revealed type: `T.untyped` +T.reveal_type(b) # error: Revealed type: `T::Array[T.untyped]` + +arr4 = T.unsafe([]) +a, *b = arr4 +T.reveal_type(a) # error: Revealed type: `T.untyped` +T.reveal_type(b) # error: Revealed type: `T.untyped` + +arr5 = [1, 2, 3] +a, *b, c = arr5 +T.reveal_type(a) # error: Revealed type: `Integer(1)` +T.reveal_type(b) # error: Revealed type: `T::Array[Integer]` +T.reveal_type(c) # error: Revealed type: `Integer(3)` + +arr6 = [1, 2, 3] +a, b, *c = arr6 +T.reveal_type(a) # error: Revealed type: `Integer(1)` +T.reveal_type(b) # error: Revealed type: `Integer(2)` +T.reveal_type(c) # error: Revealed type: `T::Array[Integer]` + +arr7 = [1, 2, 3] +a, *b = *arr7 +T.reveal_type(a) # error: Revealed type: `Integer(1)` +T.reveal_type(b) # error: Revealed type: `T::Array[Integer]` + +arr8 = [] +a, *b = *T.unsafe(arr8) +T.reveal_type(a) # error: Revealed type: `T.untyped` +T.reveal_type(b) # error: Revealed type: `T.untyped` + +arr9 = T.let(nil, T.nilable(T::Array[Integer])) +a, *b = arr9 +T.reveal_type(a) # error: Revealed type: `T.nilable(Integer)` +T.reveal_type(b) # error: Revealed type: `T::Array[T.nilable(Integer)]` diff --git a/test/testdata/resolver/optional_nil.rb.flatten-tree.exp b/test/testdata/resolver/optional_nil.rb.flatten-tree.exp index 931db39aac..cce401c5b5 100644 --- a/test/testdata/resolver/optional_nil.rb.flatten-tree.exp +++ b/test/testdata/resolver/optional_nil.rb.flatten-tree.exp @@ -1,12 +1,9 @@ begin - class <>> < (::) + + def self.<$CENSORED>() - begin - - ::Sorbet::Private::Static.keep_for_ide(::Test) - - end + end end class ::Test<> < (::) diff --git a/test/testdata/resolver/optional_nil.rb.name-tree.exp b/test/testdata/resolver/optional_nil.rb.name-tree.exp index 882687c734..5f63f99557 100644 --- a/test/testdata/resolver/optional_nil.rb.name-tree.exp +++ b/test/testdata/resolver/optional_nil.rb.name-tree.exp @@ -1,52 +1,45 @@ -begin - class <>> < (::) - begin - class ::Test<> < (::) - ::Sorbet::Private::Static.sig() do || - .params(:x, ::).returns(::) - end +class <>> < (::) + class ::Test<> < (::) + ::Sorbet::Private::Static.sig() do || + .params(:x, ::).returns(::) + end - def foo(x, ) - x - end + def foo(x, ) + x + end - ::Sorbet::Private::Static.sig() do || - .params(:y, ::).returns(::) - end + ::Sorbet::Private::Static.sig() do || + .params(:y, ::).returns(::) + end - def bar(y, ) - y - end + def bar(y, ) + y + end - ::Sorbet::Private::Static.sig() do || - .params(:z, ::).returns(::) - end + ::Sorbet::Private::Static.sig() do || + .params(:z, ::).returns(::) + end - def qux(z, ) - z - end + def qux(z, ) + z + end - ::Sorbet::Private::Static.sig() do || - .params(:w, ::).returns(::) - end + ::Sorbet::Private::Static.sig() do || + .params(:w, ::).returns(::) + end - def baz(w, ) - w - end + def baz(w, ) + w + end - .extend(::::) + .extend(::::) - + - + - + - - end - ::Sorbet::Private::Static.keep_for_ide(::Test) - - end + end - end diff --git a/test/testdata/resolver/out_of_order_thread.rb b/test/testdata/resolver/out_of_order_thread.rb new file mode 100644 index 0000000000..7777231dda --- /dev/null +++ b/test/testdata/resolver/out_of_order_thread.rb @@ -0,0 +1,8 @@ +# check-out-of-order-constant-references: true +# frozen_string_literal: true +# typed: false + + +class ::Thread + Thread.current[:opus_thread_is_root_fiber] = true +end diff --git a/test/testdata/resolver/overloads_test.rb b/test/testdata/resolver/overloads_test.rb index 2ac980181d..533c6f39e3 100644 --- a/test/testdata/resolver/overloads_test.rb +++ b/test/testdata/resolver/overloads_test.rb @@ -3,11 +3,11 @@ sig {params(s: Integer).void} sig {params(s: String).void} -def f(s); end +def f(s); end # error: against an overloaded signature sig {params(s: Symbol).void} sig {params(s: Float).void} -def f(s); end +def f(s); end # error: against an overloaded signature f(0) # error: Expected `Symbol` f('') # error: Expected `Symbol` @@ -19,19 +19,19 @@ class Wrap1 sig {params(x: Integer).void} sig {params(x: Integer, blk: T.proc.returns(Integer)).void} - def one_kw(x:, &blk); end + def one_kw(x:, &blk); end # error: against an overloaded signature sig {params(x: Integer).void} sig {params(x: Integer, blk: T.proc.returns(Integer)).void} - def opt_kw(x: 0, &blk); end + def opt_kw(x: 0, &blk); end # error: against an overloaded signature sig {params(s: String, x: Integer).void} sig {params(s: String, x: Integer, blk: T.proc.returns(Integer)).void} - def opt_pos_opt_kw(s='', x: 0, &blk); end + def opt_pos_opt_kw(s='', x: 0, &blk); end # error: against an overloaded signature sig {params(x: Integer, y: String).void} sig {params(x: Integer, y: String, blk: T.proc.returns(Integer)).void} - def two_kw(x:, y:, &blk); end + def two_kw(x:, y:, &blk); end # error: against an overloaded signature sig {params(x: Integer, y: String).void} # ^ error: Overloaded functions cannot have keyword arguments @@ -39,29 +39,31 @@ def two_kw(x:, y:, &blk); end sig {params(x: Integer, y: String, blk: T.proc.returns(Integer)).void} # ^ error: Overloaded functions cannot have keyword arguments # ^ error: Unknown argument name - def arg_in_sig_but_not_method(x:, &blk); end + def arg_in_sig_but_not_method(x:, &blk); end # error: against an overloaded signature sig {params(x: Integer, y: String).void} # error-with-dupes: Overloaded functions cannot have keyword arguments sig {params(y: String, x: Integer).void} # error-with-dupes: Overloaded functions cannot have keyword arguments - def keyword_ordering_matters(x:, y:); end # error-with-dupes: Bad parameter ordering + def keyword_ordering_matters(x:, y:); end # error: against an overloaded signature + # ^^ error: Bad parameter ordering + # ^^ error: Bad parameter ordering sig {params(x: T::Boolean).void} # ^ error: Overloaded functions cannot have keyword arguments sig {params(x: FalseClass, blk: T.proc.returns(Integer)).void} # ^ error: Overloaded functions cannot have keyword arguments - def arg_types_must_match(x:, &blk); end + def arg_types_must_match(x:, &blk); end # error: against an overloaded signature sig {params(x: Integer, blk: T.proc.returns(Integer)).void} # ^ error: Overloaded functions cannot have keyword arguments sig {params(x: Integer, blk: T.proc.returns(String)).void} # ^ error: Overloaded functions cannot have keyword arguments - def only_one_block_per_pair(x:, &blk); end + def only_one_block_per_pair(x:, &blk); end # error: against an overloaded signature sig {params(x: Integer).void} # ^ error: Overloaded functions cannot have keyword arguments sig {params(x: Integer).void} # ^ error: Overloaded functions cannot have keyword arguments - def needs_one_block_per_pair(x:); end + def needs_one_block_per_pair(x:); end # error: against an overloaded signature # We currently require type equivalence for parameters in sigs for overloaded methods # with keyword arguments. This is probably a little too strong, but fixing it would @@ -76,7 +78,7 @@ def needs_one_block_per_pair(x:); end .params(x: T.type_parameter(:U), y: Integer, blk: T.proc.returns(Integer)) # error: Overloaded functions cannot have keyword arguments .void end - def matching_positional_type_parameters_not_allowed(x, y:, &blk); end + def matching_positional_type_parameters_not_allowed(x, y:, &blk); end # error: against an overloaded signature sig do type_parameters(:U) @@ -88,7 +90,7 @@ def matching_positional_type_parameters_not_allowed(x, y:, &blk); end .params(x: T.type_parameter(:V), y: Integer, blk: T.proc.returns(Integer)) # error: Overloaded functions cannot have keyword arguments .void end - def mismatched_positional_type_parameters_not_allowed(x, y:, &blk); end + def mismatched_positional_type_parameters_not_allowed(x, y:, &blk); end # error: against an overloaded signature sig do type_parameters(:U) @@ -100,7 +102,7 @@ def mismatched_positional_type_parameters_not_allowed(x, y:, &blk); end .params(x: T.type_parameter(:U), blk: T.proc.returns(Integer)) # error: Overloaded functions cannot have keyword arguments .void end - def matching_kwarg_type_parameters_not_allowed(x:, &blk); end + def matching_kwarg_type_parameters_not_allowed(x:, &blk); end # error: against an overloaded signature sig do type_parameters(:U) @@ -112,7 +114,7 @@ def matching_kwarg_type_parameters_not_allowed(x:, &blk); end .params(x: T.type_parameter(:V), blk: T.proc.returns(Integer)) # error: Overloaded functions cannot have keyword arguments .void end - def mismatched_kwargs_type_parameters_not_allowed(x:, &blk); end + def mismatched_kwargs_type_parameters_not_allowed(x:, &blk); end # error: against an overloaded signature end Wrap1.new.opt_kw() @@ -128,26 +130,26 @@ class Wrap2 sig {params(x: Integer).void} # error: Overloaded functions cannot have keyword arguments sig {params(x: Integer, y: String).void} # error-with-dupes: Overloaded functions cannot have keyword arguments - def missing_kw1(x:, y: ''); end + def missing_kw1(x:, y: ''); end # error: against an overloaded signature sig {params(x: Integer, y: String).void} # error-with-dupes: Overloaded functions cannot have keyword arguments sig {params(x: Integer).void} # error: Overloaded functions cannot have keyword arguments - def missing_kw2(x:, y: ''); end + def missing_kw2(x:, y: ''); end # error: against an overloaded signature # Same pattern as the above, but 3 sigs will error where two will not. # TODO(froydnj): can we test what happens when the relevant error is suppressed? sig {params(x: Integer).void} # error: Overloaded functions cannot have keyword arguments sig {params(x: Integer, y: String).void} # error-with-dupes: Overloaded functions cannot have keyword arguments sig {params(x: Integer, y: String, z: Float).void} # error-with-dupes: Overloaded functions cannot have keyword arguments - def multiple_missing_kw(x:, y: '', z: 0.0); end + def multiple_missing_kw(x:, y: '', z: 0.0); end # error: against an overloaded signature sig {params(z: String, x: Integer).void} # error: Overloaded functions cannot have keyword arguments sig {params(x: Integer).void} # error: Overloaded functions cannot have keyword arguments - def mismatched_positional_args1(z='', x:); end + def mismatched_positional_args1(z='', x:); end # error: against an overloaded signature sig {params(x: Integer).void} # error: Overloaded functions cannot have keyword arguments sig {params(z: String, x: Integer).void} # error: Overloaded functions cannot have keyword arguments - def mismatched_positional_args2(z='', x:); end + def mismatched_positional_args2(z='', x:); end # error: against an overloaded signature end Wrap2.new.missing_kw1(x: 0, y: 'foo') # error: Unrecognized keyword argument @@ -178,7 +180,7 @@ class WrapGeneric1 params(x: Elem, y: Integer, blk: T.proc.returns(Integer)) .void end - def matching_positional_type_members(x, y:, &blk); end + def matching_positional_type_members(x, y:, &blk); end # error: against an overloaded signature sig do params(x: Elem) @@ -188,7 +190,7 @@ def matching_positional_type_members(x, y:, &blk); end params(x: Elem, blk: T.proc.returns(Integer)) .void end - def matching_kwarg_type_members(x:, &blk); end + def matching_kwarg_type_members(x:, &blk); end # error: against an overloaded signature end class WrapGeneric2 @@ -205,7 +207,7 @@ class WrapGeneric2 params(x: Elem, y: Integer, blk: T.proc.returns(Integer)) .void end - def self.matching_positional_type_templates(x, y:, &blk); end + def self.matching_positional_type_templates(x, y:, &blk); end # error: against an overloaded signature sig do params(x: Elem) @@ -215,7 +217,7 @@ def self.matching_positional_type_templates(x, y:, &blk); end params(x: Elem, blk: T.proc.returns(Integer)) .void end - def self.matching_kwarg_type_templates(x:, &blk); end + def self.matching_kwarg_type_templates(x:, &blk); end # error: against an overloaded signature end class WrapGeneric3 @@ -235,7 +237,7 @@ class WrapGeneric3 # ^ error: Overloaded functions cannot have keyword arguments .void end - def mismatched_positional_type_members(x, y:, &blk); end + def mismatched_positional_type_members(x, y:, &blk); end # error: against an overloaded signature sig do params(x: Elem) @@ -247,7 +249,7 @@ def mismatched_positional_type_members(x, y:, &blk); end # ^ error: Overloaded functions cannot have keyword arguments .void end - def mismatched_kwarg_type_members(x:, &blk); end + def mismatched_kwarg_type_members(x:, &blk); end # error: against an overloaded signature end class WrapGeneric4 @@ -267,7 +269,7 @@ class WrapGeneric4 # ^ error: Overloaded functions cannot have keyword arguments .void end - def self.mismatched_positional_type_templates(x, y:, &blk); end + def self.mismatched_positional_type_templates(x, y:, &blk); end # error: against an overloaded signature sig do params(x: Elem) @@ -279,7 +281,7 @@ def self.mismatched_positional_type_templates(x, y:, &blk); end # ^ error: Overloaded functions cannot have keyword arguments .void end - def self.mismatched_kwarg_type_templates(x:, &blk); end + def self.mismatched_kwarg_type_templates(x:, &blk); end # error: against an overloaded signature end diff --git a/test/testdata/resolver/override_initialize.rb b/test/testdata/resolver/override_initialize.rb new file mode 100644 index 0000000000..088573606e --- /dev/null +++ b/test/testdata/resolver/override_initialize.rb @@ -0,0 +1,9 @@ +# typed: true + +class A + extend T::Sig + + sig { override.params(x: Integer).void } + def initialize(x) + end +end diff --git a/test/testdata/resolver/overrides.rb b/test/testdata/resolver/overrides.rb index 9882971a13..d9800cdb9a 100644 --- a/test/testdata/resolver/overrides.rb +++ b/test/testdata/resolver/overrides.rb @@ -87,5 +87,4 @@ class B7 < A7 extend T::Sig sig {override.returns(String)} def foo; 'foo' end -# ^^^^^^^ error: Return type `String` does not match return type of overridden method `A7#foo` end diff --git a/test/testdata/resolver/payload_superclass_redefinition.rb b/test/testdata/resolver/payload_superclass_redefinition.rb new file mode 100644 index 0000000000..e2c1154dc3 --- /dev/null +++ b/test/testdata/resolver/payload_superclass_redefinition.rb @@ -0,0 +1,8 @@ +# typed: true + +class Parent +end + +class IRB::RelineInputMethod < Parent + # ^^^^^^ error: Parent of class `IRB::RelineInputMethod` redefined from `IRB::InputMethod` to `Parent` +end diff --git a/test/testdata/resolver/recover_from_bad_superclass.rb.symbol-table-raw.exp b/test/testdata/resolver/recover_from_bad_superclass.rb.symbol-table-raw.exp index 2771b345b8..57a41f8a80 100644 --- a/test/testdata/resolver/recover_from_bad_superclass.rb.symbol-table-raw.exp +++ b/test/testdata/resolver/recover_from_bad_superclass.rb.symbol-table-raw.exp @@ -8,18 +8,18 @@ class >> < > () method > $1>#> () @ Loc {file=test/testdata/resolver/recover_from_bad_superclass.rb start=7:1 end=8:4} argument @ Loc {file=test/testdata/resolver/recover_from_bad_superclass.rb start=??? end=???} static-field-type-alias > -> Object @ Loc {file=test/testdata/resolver/recover_from_bad_superclass.rb start=16:1 end=16:6} - class > < >::>::>::> () @ Loc {file=test/testdata/resolver/recover_from_bad_superclass.rb start=12:1 end=12:24} - class > $1>[>>] < >::>::>::> $1> () @ Loc {file=test/testdata/resolver/recover_from_bad_superclass.rb start=12:1 end=12:24} + class > < >::>::>::>> () @ Loc {file=test/testdata/resolver/recover_from_bad_superclass.rb start=12:1 end=12:24} + class > $1>[>>] < >::>::>::>> $1> () @ Loc {file=test/testdata/resolver/recover_from_bad_superclass.rb start=12:1 end=12:24} type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=B) @ Loc {file=test/testdata/resolver/recover_from_bad_superclass.rb start=12:1 end=12:24} method > $1>#> () @ Loc {file=test/testdata/resolver/recover_from_bad_superclass.rb start=12:1 end=13:4} argument @ Loc {file=test/testdata/resolver/recover_from_bad_superclass.rb start=??? end=???} - class > < >::>::>::> () @ Loc {file=test/testdata/resolver/recover_from_bad_superclass.rb start=17:1 end=17:16} - class > $1>[>>] < >::>::>::> $1> () @ Loc {file=test/testdata/resolver/recover_from_bad_superclass.rb start=17:1 end=17:16} + class > < >::>::>::>> () @ Loc {file=test/testdata/resolver/recover_from_bad_superclass.rb start=17:1 end=17:16} + class > $1>[>>] < >::>::>::>> $1> () @ Loc {file=test/testdata/resolver/recover_from_bad_superclass.rb start=17:1 end=17:16} type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=C) @ Loc {file=test/testdata/resolver/recover_from_bad_superclass.rb start=17:1 end=17:16} method > $1>#> () @ Loc {file=test/testdata/resolver/recover_from_bad_superclass.rb start=17:1 end=18:4} argument @ Loc {file=test/testdata/resolver/recover_from_bad_superclass.rb start=??? end=???} - class > < >::>::>::> () @ Loc {file=test/testdata/resolver/recover_from_bad_superclass.rb start=21:1 end=21:12} - class > $1>[>>] < >::>::>::> $1> () @ Loc {file=test/testdata/resolver/recover_from_bad_superclass.rb start=21:1 end=21:12} + class > < >::>::>::>> () @ Loc {file=test/testdata/resolver/recover_from_bad_superclass.rb start=21:1 end=21:12} + class > $1>[>>] < >::>::>::>> $1> () @ Loc {file=test/testdata/resolver/recover_from_bad_superclass.rb start=21:1 end=21:12} type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=D) @ Loc {file=test/testdata/resolver/recover_from_bad_superclass.rb start=21:1 end=21:12} method > $1>#> () @ Loc {file=test/testdata/resolver/recover_from_bad_superclass.rb start=21:1 end=22:4} argument @ Loc {file=test/testdata/resolver/recover_from_bad_superclass.rb start=??? end=???} @@ -29,12 +29,12 @@ class >> < > () type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=F) @ Loc {file=test/testdata/resolver/recover_from_bad_superclass.rb start=25:1 end=25:12} method > $1>#> () @ Loc {file=test/testdata/resolver/recover_from_bad_superclass.rb start=25:1 end=26:4} argument @ Loc {file=test/testdata/resolver/recover_from_bad_superclass.rb start=??? end=???} - class > < >::>::>::> () @ Loc {file=test/testdata/resolver/recover_from_bad_superclass.rb start=29:1 end=29:12} - class > $1>[>>] < >::>::>::> $1> () @ Loc {file=test/testdata/resolver/recover_from_bad_superclass.rb start=29:1 end=29:12} + class > < >::>::>::>> () @ Loc {file=test/testdata/resolver/recover_from_bad_superclass.rb start=29:1 end=29:12} + class > $1>[>>] < >::>::>::>> $1> () @ Loc {file=test/testdata/resolver/recover_from_bad_superclass.rb start=29:1 end=29:12} type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=G) @ Loc {file=test/testdata/resolver/recover_from_bad_superclass.rb start=29:1 end=29:12} method > $1>#> () @ Loc {file=test/testdata/resolver/recover_from_bad_superclass.rb start=29:1 end=30:4} argument @ Loc {file=test/testdata/resolver/recover_from_bad_superclass.rb start=??? end=???} - class > < > (>) @ Loc {file=https://github.com/sorbet/sorbet/tree/master/rbi/core/object.rbi start=removed end=removed} + class > < > (>, >) @ (Loc {file=https://github.com/sorbet/sorbet/tree/master/rbi/stdlib/json.rbi start=removed end=removed}, Loc {file=https://github.com/sorbet/sorbet/tree/master/rbi/core/object.rbi start=removed end=removed}) method ># : private () @ Loc {file=test/testdata/resolver/recover_from_bad_superclass.rb start=3:1 end=3:17} argument @ Loc {file=test/testdata/resolver/recover_from_bad_superclass.rb start=??? end=???} diff --git a/test/testdata/resolver/redefined_and_overloaded/overloads_test.1.rbupdate b/test/testdata/resolver/redefined_and_overloaded/overloads_test.1.rbupdate index 4510686301..39ce2138b4 100644 --- a/test/testdata/resolver/redefined_and_overloaded/overloads_test.1.rbupdate +++ b/test/testdata/resolver/redefined_and_overloaded/overloads_test.1.rbupdate @@ -6,13 +6,14 @@ class A sig {params(x: Integer).void} sig {params(x: Integer, y: String).void} - def self.example(x, y='') + def self.example(x, y='') # error: against an overloaded signature end sig {params(x: Integer).void} sig {params(x: Integer, y: String).void} sig {params(x: Integer, y: String, z: Float).void} def self.example1(x, y='', z=0.0) +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: against an overloaded signature end end diff --git a/test/testdata/resolver/redefined_and_overloaded/overloads_test.rb b/test/testdata/resolver/redefined_and_overloaded/overloads_test.rb index 477157b867..5fa7819af5 100644 --- a/test/testdata/resolver/redefined_and_overloaded/overloads_test.rb +++ b/test/testdata/resolver/redefined_and_overloaded/overloads_test.rb @@ -5,13 +5,14 @@ class A sig {params(x: Integer).void} sig {params(x: Integer, y: String).void} - def self.example(x, y='') + def self.example(x, y='') # error: against an overloaded signature end sig {params(x: Integer).void} sig {params(x: Integer, y: String).void} sig {params(x: Integer, y: String, z: Float).void} def self.example(x, y='', z=0.0) # error: Method `A.example` redefined without matching argument count. Expected: `2`, got: `3` +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: against an overloaded signature end end diff --git a/test/testdata/resolver/requires_ancestor_errors.rb b/test/testdata/resolver/requires_ancestor_errors.rb index fa90e367d0..df46f8528f 100644 --- a/test/testdata/resolver/requires_ancestor_errors.rb +++ b/test/testdata/resolver/requires_ancestor_errors.rb @@ -92,7 +92,7 @@ module Helper11 extend T::Helpers requires_ancestor (Kernel) - # ^^^^^^ error: Too many arguments provided for method `T::Helpers#requires_ancestor`. Expected: `0`, got: `1` + # ^^^^^^^^ error: Too many arguments provided for method `T::Helpers#requires_ancestor`. Expected: `0`, got: `1` # ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `requires_ancestor` only accepts a block # ^ error: `requires_ancestor` requires a block parameter, but no block was passed end @@ -101,6 +101,6 @@ module Helper12 extend T::Helpers requires_ancestor (Kernel) { Kernel } - # ^^^^^^ error: Too many arguments provided for method `T::Helpers#requires_ancestor`. Expected: `0`, got: `1` + # ^^^^^^^^ error: Too many arguments provided for method `T::Helpers#requires_ancestor`. Expected: `0`, got: `1` # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `requires_ancestor` only accepts a block end diff --git a/test/testdata/resolver/resolution_order.rb.symbol-table-raw.exp b/test/testdata/resolver/resolution_order.rb.symbol-table-raw.exp index d6f78ee1d4..a399b32ffa 100644 --- a/test/testdata/resolver/resolution_order.rb.symbol-table-raw.exp +++ b/test/testdata/resolver/resolution_order.rb.symbol-table-raw.exp @@ -34,7 +34,7 @@ class >> < > () class > $1> < > () @ Loc {file=test/testdata/resolver/resolution_order.rb start=47:1 end=47:9} method > $1>#> () @ Loc {file=test/testdata/resolver/resolution_order.rb start=47:1 end=54:4} argument @ Loc {file=test/testdata/resolver/resolution_order.rb start=??? end=???} - class > < > (>) @ Loc {file=test/testdata/resolver/resolution_order.rb start=10:1 end=10:15} + class > < > (>>) @ Loc {file=test/testdata/resolver/resolution_order.rb start=10:1 end=10:15} class > $1>[>>] < > $1> () @ Loc {file=test/testdata/resolver/resolution_order.rb start=10:1 end=10:15} type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=HasError) @ Loc {file=test/testdata/resolver/resolution_order.rb start=10:1 end=10:15} method > $1>#> () @ Loc {file=test/testdata/resolver/resolution_order.rb start=10:1 end=12:4} diff --git a/test/testdata/resolver/resolution_scoping.rb.symbol-table-raw.exp b/test/testdata/resolver/resolution_scoping.rb.symbol-table-raw.exp index fbd8d98718..17af777f7b 100644 --- a/test/testdata/resolver/resolution_scoping.rb.symbol-table-raw.exp +++ b/test/testdata/resolver/resolution_scoping.rb.symbol-table-raw.exp @@ -2,7 +2,7 @@ class >> < > () class >> $1>[>>] < > $1> () method >> $1>#> $CENSORED> () @ Loc {file=test/testdata/resolver/resolution_scoping.rb start=2:1 end=15:4} argument @ Loc {file=test/testdata/resolver/resolution_scoping.rb start=??? end=???} - class > < >::>::>::> (>, >) @ Loc {file=test/testdata/resolver/resolution_scoping.rb start=2:1 end=2:12} + class > < >::>::>::>> (>, >) @ Loc {file=test/testdata/resolver/resolution_scoping.rb start=2:1 end=2:12} module >::> < >::>::>::> () @ Loc {file=test/testdata/resolver/resolution_scoping.rb start=5:3 end=5:11} class >::> $1> < > () @ Loc {file=test/testdata/resolver/resolution_scoping.rb start=5:3 end=5:11} method >::> $1>#> () @ Loc {file=test/testdata/resolver/resolution_scoping.rb start=5:3 end=6:6} @@ -11,7 +11,7 @@ class >> < > () class >::> $1> < > () @ Loc {file=test/testdata/resolver/resolution_scoping.rb start=3:3 end=3:11} method >::> $1>#> () @ Loc {file=test/testdata/resolver/resolution_scoping.rb start=3:3 end=4:6} argument @ Loc {file=test/testdata/resolver/resolution_scoping.rb start=??? end=???} - class > $1>[>>] < >::>::>::> $1> () @ Loc {file=test/testdata/resolver/resolution_scoping.rb start=2:1 end=2:12} + class > $1>[>>] < >::>::>::>> $1> () @ Loc {file=test/testdata/resolver/resolution_scoping.rb start=2:1 end=2:12} type-member(+) > $1>::>> -> LambdaParam(> $1>::>>, lower=T.noreturn, upper=A) @ Loc {file=test/testdata/resolver/resolution_scoping.rb start=2:1 end=2:12} method > $1>#> () @ Loc {file=test/testdata/resolver/resolution_scoping.rb start=2:1 end=9:4} argument @ Loc {file=test/testdata/resolver/resolution_scoping.rb start=??? end=???} diff --git a/test/testdata/resolver/resolve_tree_printing.rb.flatten-tree-raw.exp b/test/testdata/resolver/resolve_tree_printing.rb.flatten-tree-raw.exp index 682882a7d6..d0efc8f1d8 100644 --- a/test/testdata/resolver/resolve_tree_printing.rb.flatten-tree-raw.exp +++ b/test/testdata/resolver/resolve_tree_printing.rb.flatten-tree-raw.exp @@ -1,6 +1,5 @@ InsSeq{ stats = [ - EmptyTree ClassDef{ kind = class name = EmptyTree @@ -10,37 +9,15 @@ InsSeq{ orig = nullptr }] rhs = [ + EmptyTree + MethodDef{ flags = {self} name = ><> $CENSORED>> args = [Local{ localVariable = > }] - rhs = InsSeq{ - stats = [ - EmptyTree - Send{ - flags = {} - recv = ConstantLit{ - symbol = (module ::Sorbet::Private::Static) - orig = nullptr - } - fun = - block = nullptr - pos_args = 1 - args = [ - ConstantLit{ - symbol = (class ::A) - orig = UnresolvedConstantLit{ - cnst = > - scope = EmptyTree - } - } - ] - } - ], - expr = EmptyTree - } + rhs = EmptyTree } ] } @@ -88,7 +65,7 @@ InsSeq{ localVariable = > }] rhs = ConstantLit{ - symbol = (module ::Sorbet::Private::Static::StubModule) + symbol = (module ::Sorbet::Private::Static::) orig = UnresolvedConstantLit{ cnst = > scope = EmptyTree diff --git a/test/testdata/resolver/sig_bad.rb b/test/testdata/resolver/sig_bad.rb index 19860b58a2..fab1841048 100644 --- a/test/testdata/resolver/sig_bad.rb +++ b/test/testdata/resolver/sig_bad.rb @@ -38,10 +38,10 @@ def self.unsupported; end # ^^^^^^^^^^^ error: Unsupported type literal # ^^^^ error: Method `enum` does not exist on `T.class_of(T)` b1: T.deprecated_enum, # error: Not enough arguments provided for method `T.deprecated_enum`. Expected: `1`, got: `0` - c11: T.deprecated_enum(1), - c12: T.deprecated_enum(1.0), - c13: T.deprecated_enum("d"), - c14: T.deprecated_enum(:e), + c11: T.deprecated_enum(1), # error: Expected `T.any(T::Set[T.anything], T::Array[T.anything])` + c12: T.deprecated_enum(1.0), # error: Expected `T.any(T::Set[T.anything], T::Array[T.anything])` + c13: T.deprecated_enum("d"), # error: Expected `T.any(T::Set[T.anything], T::Array[T.anything])` + c14: T.deprecated_enum(:e), # error: Expected `T.any(T::Set[T.anything], T::Array[T.anything])` d1: T.deprecated_enum([]), # error: enum([]) is invalid e1: T.deprecated_enum([unsupported]), # error: Unsupported type literal f: 0, # error: Unsupported literal in type syntax diff --git a/test/testdata/resolver/sig_on_failure.rb b/test/testdata/resolver/sig_on_failure.rb index 4a2ef99671..a6a10a90d3 100644 --- a/test/testdata/resolver/sig_on_failure.rb +++ b/test/testdata/resolver/sig_on_failure.rb @@ -7,7 +7,7 @@ class Main def on_failure end - # Since it is an experiement, all these illegal things are ok for now + # Since it is an experiment, all these illegal things are ok for now sig {returns(NilClass).on_failure(notify: 'pt').on_failure(notify: 'pt')} def two_on_failure end diff --git a/test/testdata/resolver/simple.rb.flatten-tree.exp b/test/testdata/resolver/simple.rb.flatten-tree.exp index c6f5ff277d..ca9eefff18 100644 --- a/test/testdata/resolver/simple.rb.flatten-tree.exp +++ b/test/testdata/resolver/simple.rb.flatten-tree.exp @@ -1,20 +1,11 @@ begin - class <>> < (::) + + + + def self.<$CENSORED>() - begin - begin - - ::Sorbet::Private::Static.keep_for_ide(::Outer1) - - end - begin - - ::Sorbet::Private::Static.keep_for_ide(::Outer2) - - end - - end + end end class ::Outer1<> < (::) @@ -23,31 +14,16 @@ begin end end class ::Outer2<> < (::) + + + + + + + + def self.() - begin - begin - - ::Sorbet::Private::Static.keep_for_ide(::Outer2::C) - - end - begin - - ::Sorbet::Private::Static.keep_for_ide(::Outer2::Inner1) - - end - begin - - ::Sorbet::Private::Static.keep_for_ide(::Outer2::Inner1::Inner2) - - end - begin - - ::Sorbet::Private::Static.keep_for_ide(::Outer2::Other) - ::Sorbet::Private::Static.keep_for_ide(::Outer2::Inner1::Inner2) - - end - - end + end end class ::Outer2::C<> < (::) @@ -56,20 +32,12 @@ begin end end class ::Outer2::Inner1<> < (::) + + + + def self.() - begin - begin - - ::Sorbet::Private::Static.keep_for_ide(::Outer2::Inner1::A) - - end - begin - - ::Sorbet::Private::Static.keep_for_ide(::Outer2::Inner1::Inner2) - - end - - end + end end class ::Outer2::Inner1::A<> < (::) diff --git a/test/testdata/resolver/simple.rb.symbol-table-raw.exp b/test/testdata/resolver/simple.rb.symbol-table-raw.exp index 5befecad85..b37cd094b4 100644 --- a/test/testdata/resolver/simple.rb.symbol-table-raw.exp +++ b/test/testdata/resolver/simple.rb.symbol-table-raw.exp @@ -21,8 +21,8 @@ class >> < > () argument @ Loc {file=test/testdata/resolver/simple.rb start=??? end=???} class >::>::> < > () @ Loc {file=test/testdata/resolver/simple.rb start=21:3 end=21:23} class >::>::> $1>[>>] < > $1> () @ Loc {file=test/testdata/resolver/simple.rb start=21:3 end=21:23} - type-member(+) >::>::> $1>::>> -> LambdaParam(>::>::> $1>::>>, lower=T.noreturn, upper=Outer2::Inner1::Inner2) @ Loc {file=test/testdata/resolver/simple.rb start=13:5 end=13:17} - method >::>::> $1>#> () @ Loc {file=test/testdata/resolver/simple.rb start=13:5 end=18:8} + type-member(+) >::>::> $1>::>> -> LambdaParam(>::>::> $1>::>>, lower=T.noreturn, upper=Outer2::Inner1::Inner2) @ Loc {file=test/testdata/resolver/simple.rb start=21:3 end=21:23} + method >::>::> $1>#> () @ Loc {file=test/testdata/resolver/simple.rb start=21:3 end=24:6} argument @ Loc {file=test/testdata/resolver/simple.rb start=??? end=???} class >::> $1>[>>] < > $1> () @ Loc {file=test/testdata/resolver/simple.rb start=9:3 end=9:15} type-member(+) >::> $1>::>> -> LambdaParam(>::> $1>::>>, lower=T.noreturn, upper=Outer2::Inner1) @ Loc {file=test/testdata/resolver/simple.rb start=9:3 end=9:15} diff --git a/test/testdata/resolver/stub_missing_class_alias.rb.symbol-table-raw.exp b/test/testdata/resolver/stub_missing_class_alias.rb.symbol-table-raw.exp index 6a1f5dafe4..149ce4eac9 100644 --- a/test/testdata/resolver/stub_missing_class_alias.rb.symbol-table-raw.exp +++ b/test/testdata/resolver/stub_missing_class_alias.rb.symbol-table-raw.exp @@ -5,7 +5,7 @@ class >> < > () module > < >::>::>::> () @ Loc {file=test/testdata/resolver/stub_missing_class_alias.rb start=4:7 end=4:8} class >::> < > () @ Loc {file=test/testdata/resolver/stub_missing_class_alias.rb start=4:1 end=4:11} static-field >::>::> -> Integer @ Loc {file=test/testdata/resolver/stub_missing_class_alias.rb start=9:3 end=9:7} - static-field >::>::> -> AliasType { symbol = >::>::>::> } @ Loc {file=test/testdata/resolver/stub_missing_class_alias.rb start=8:3 end=8:11} + static-field >::>::> -> AliasType { symbol = >::>::>::>> } @ Loc {file=test/testdata/resolver/stub_missing_class_alias.rb start=8:3 end=8:11} class >::>::> < > () @ Loc {file=test/testdata/resolver/stub_missing_class_alias.rb start=5:3 end=5:10} class >::>::> $1>[>>] < > $1> () @ Loc {file=test/testdata/resolver/stub_missing_class_alias.rb start=5:3 end=5:10} type-member(+) >::>::> $1>::>> -> LambdaParam(>::>::> $1>::>>, lower=T.noreturn, upper=O::B::J) @ Loc {file=test/testdata/resolver/stub_missing_class_alias.rb start=5:3 end=5:10} diff --git a/test/testdata/resolver/stubs_typed_untyped.flatten-tree.exp b/test/testdata/resolver/stubs_typed_untyped.flatten-tree.exp index 5460d0dbd6..2578c362de 100644 --- a/test/testdata/resolver/stubs_typed_untyped.flatten-tree.exp +++ b/test/testdata/resolver/stubs_typed_untyped.flatten-tree.exp @@ -1,13 +1,9 @@ begin - class <>> < (::) + + def self.<$CENSORED>() begin - begin - - ::Sorbet::Private::Static.keep_for_ide(::Foo) - - end Unresolved: :: Unresolved: ::Foo:: @@ -21,16 +17,12 @@ begin end end -begin - - class <>> < (::) - def self.<$CENSORED>() - begin - Unresolved: :: - Unresolved: ::Foo:: - - end +class <>> < (::) + def self.<$CENSORED>() + begin + Unresolved: :: + Unresolved: ::Foo:: + end end - end diff --git a/test/testdata/resolver/t_class_autocorrect.rb b/test/testdata/resolver/t_class_autocorrect.rb new file mode 100644 index 0000000000..c444239540 --- /dev/null +++ b/test/testdata/resolver/t_class_autocorrect.rb @@ -0,0 +1,33 @@ +# typed: true + +TypeAlias = T.type_alias { Integer } + +class A + extend T::Sig + extend T::Generic + + X = type_member + + Const = 0 + + sig {returns(T.class_of(X))} + # ^^^^^^^^^^^^^ error: T.class_of can't be used with a T.type_member + def example1; end + + sig {returns(T.class_of(TypeAlias))} + # ^^^^^^^^^^^^^^^^^^^^^ error: T.class_of can't be used with a T.type_alias + def example2; end + + sig {returns(T.class_of(Const))} + # ^^^^^^^^^^^^^^^^^ error: T.class_of can't be used with a constant field + # ^^^^^ error: Unexpected bare `Integer` value found in type position + def example3; end + + sig {returns(T.class_of(T.any(Integer, String)))} + # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `T.class_of` must wrap each individual class type, not the outer `T.any` + def example4; end + + sig {returns(T.class_of(T.any(X, String)))} + # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `T.class_of` needs a class or module as its argument + def example5; end +end diff --git a/test/testdata/resolver/t_class_autocorrect.rb.autocorrects.exp b/test/testdata/resolver/t_class_autocorrect.rb.autocorrects.exp new file mode 100644 index 0000000000..548f0ad467 --- /dev/null +++ b/test/testdata/resolver/t_class_autocorrect.rb.autocorrects.exp @@ -0,0 +1,35 @@ +# -- test/testdata/resolver/t_class_autocorrect.rb -- +# typed: true + +TypeAlias = T.type_alias { Integer } + +class A + extend T::Sig + extend T::Generic + + X = type_member + + Const = 0 + + sig {returns(T::Class[X])} + # ^^^^^^^^^^^^^ error: T.class_of can't be used with a T.type_member + def example1; end + + sig {returns(T::Class[TypeAlias])} + # ^^^^^^^^^^^^^^^^^^^^^ error: T.class_of can't be used with a T.type_alias + def example2; end + + sig {returns(T::Class[Const])} + # ^^^^^^^^^^^^^^^^^ error: T.class_of can't be used with a constant field + # ^^^^^ error: Unexpected bare `Integer` value found in type position + def example3; end + + sig {returns(T.any(T.class_of(Integer), T.class_of(String)))} + # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `T.class_of` must wrap each individual class type, not the outer `T.any` + def example4; end + + sig {returns(T::Class[T.any(X, String)])} + # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `T.class_of` needs a class or module as its argument + def example5; end +end +# ------------------------------ diff --git a/test/testdata/resolver/t_combinator_kwargs.rb b/test/testdata/resolver/t_combinator_kwargs.rb index 6014683a25..76eb86b0ed 100644 --- a/test/testdata/resolver/t_combinator_kwargs.rb +++ b/test/testdata/resolver/t_combinator_kwargs.rb @@ -18,6 +18,7 @@ module M param: T.type_parameter(a: Integer), # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `T.type_parameter` does not accept keyword arguments enum: T.deprecated_enum(a: Integer), + # ^^^^^^^^^^ error: Expected `T.any(T::Set[T.anything], T::Array[T.anything])` # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `T.deprecated_enum` does not accept keyword arguments klass: T.class_of(a: Integer), # ^^^^^^^^^^^^^^^^^^^^^^ error: `T.class_of` does not accept keyword arguments diff --git a/test/testdata/resolver/type_alias_not_on_T.rb b/test/testdata/resolver/type_alias_not_on_T.rb new file mode 100644 index 0000000000..1ec9a52252 --- /dev/null +++ b/test/testdata/resolver/type_alias_not_on_T.rb @@ -0,0 +1,15 @@ +# typed: true + +module A + extend T::Sig + + module NotT + def self.type_alias(&blk); end + end + + NotATypeAlias = NotT.type_alias {Object} + + sig {returns(NotATypeAlias)} + # ^^^^^^^^^^^^^ error: Constant `A::NotATypeAlias` is not a class or type alias + def this_has_an_invalid_return_type; end +end diff --git a/test/testdata/resolver/type_alias_self_type.rb b/test/testdata/resolver/type_alias_self_type.rb new file mode 100644 index 0000000000..245f861f61 --- /dev/null +++ b/test/testdata/resolver/type_alias_self_type.rb @@ -0,0 +1,27 @@ +# typed: strict + +class Example + extend T::Sig + + # This magically becomes "the selftype of wherever this type alias is used" + # instead of "the self type of `self` right here" (i.e., the singleton class) + # I'm not sure if this behavior is intentional or accidental, but it is what + # it is so now we have a test for it. At least we don't crash. + X = T.type_alias { T.self_type } + + sig { returns(X) } + def example + self + end + + sig { returns(X) } + def self.example_singleton + self + end +end + +x = Example.new.example +T.reveal_type(x) # error: `Example` + +y = Example.example_singleton +T.reveal_type(y) # error: `T.class_of(Example)` diff --git a/test/testdata/rewriter/attr.rb.flatten-tree.exp b/test/testdata/rewriter/attr.rb.flatten-tree.exp index d8925cfe59..a7ad69c512 100644 --- a/test/testdata/rewriter/attr.rb.flatten-tree.exp +++ b/test/testdata/rewriter/attr.rb.flatten-tree.exp @@ -1,12 +1,9 @@ begin - class <>> < (::) + + def self.<$CENSORED>() - begin - - ::Sorbet::Private::Static.keep_for_ide(::TestAttr) - - end + end end class ::TestAttr<> < (::) diff --git a/test/testdata/rewriter/before_do.rb b/test/testdata/rewriter/before_do.rb new file mode 100644 index 0000000000..0f7fc7dcca --- /dev/null +++ b/test/testdata/rewriter/before_do.rb @@ -0,0 +1,27 @@ +# typed: strict +class Module; include T::Sig; end + +class MyTestGrandParent + sig {params(name: String).void} + def initialize(name) + end +end + +class MyTestParent < MyTestGrandParent + before do + @x = T.let(1, Integer) + end +end + +class MyTestChild < MyTestParent + sig {params(name: String).void} + def initialize(name) + super + end + + describe 'example' do + it 'does thing' do + T.reveal_type(@x) # error: `Integer` + end + end +end diff --git a/test/testdata/rewriter/before_do.rb.rewrite-tree.exp b/test/testdata/rewriter/before_do.rb.rewrite-tree.exp new file mode 100644 index 0000000000..c32b5390be --- /dev/null +++ b/test/testdata/rewriter/before_do.rb.rewrite-tree.exp @@ -0,0 +1,56 @@ +class <>> < (::) + class ::<>> < (::) + .include(::::) + end + + class ::<>> < (::) + ::Sorbet::Private::Static.sig() do || + .params(:name, ::).void() + end + + def initialize<>(name, &) + + end + + + end + + class ::<>> < (::) + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .void() + end + + def <>(&) + @x = (1, , ::) + end + + > + end + + class ::<>> < (::) + ::Sorbet::Private::Static.sig() do || + .params(:name, ::).void() + end + + def initialize<>(name, &) + .(ZSuperArgs) + end + + + + class ::><>> < () + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .void() + end + + def <>(&) + ::.reveal_type(@x) + end + + begin + "does thing" + > + end + end + end +end diff --git a/test/testdata/rewriter/class_new_strict.rb.cfg-text.exp b/test/testdata/rewriter/class_new_strict.rb.cfg-text.exp index cf8e882c30..1070b840e0 100644 --- a/test/testdata/rewriter/class_new_strict.rb.cfg-text.exp +++ b/test/testdata/rewriter/class_new_strict.rb.cfg-text.exp @@ -1,10 +1,7 @@ method ::># { -bb0[rubyRegionId=0, firstDead=5](): +bb0[rubyRegionId=0, firstDead=2](): : T.class_of() = cast(: NilClass, T.class_of()); - $6: T.class_of(Sorbet::Private::Static) = alias - $8: T.class_of(A) = alias - $4: Sorbet::Private::Static::Void = $6: T.class_of(Sorbet::Private::Static).keep_for_ide($8: T.class_of(A)) : T.noreturn = return $2: NilClass -> bb1 @@ -39,7 +36,7 @@ bb2[rubyRegionId=1, firstDead=-1](: T.class_of(A), $6 # backedges # - bb2(rubyRegionId=1) bb3[rubyRegionId=0, firstDead=-1]($6: Sorbet::Private::Static::Void, $7: T.class_of(A)): - _cls: T::Class[Object] = Solve<$6, new> + _cls: T::Class[T.untyped] = Solve<$6, new> : T.class_of(A) = $7 $18: T.class_of(Class)[T::Class[T.anything]] = alias $20: T.class_of(A) = alias diff --git a/test/testdata/rewriter/class_new_strict.rb.flatten-tree.exp b/test/testdata/rewriter/class_new_strict.rb.flatten-tree.exp index bd4de53938..9c1d6fd278 100644 --- a/test/testdata/rewriter/class_new_strict.rb.flatten-tree.exp +++ b/test/testdata/rewriter/class_new_strict.rb.flatten-tree.exp @@ -1,12 +1,9 @@ begin - class <>> < (::) + + def self.<$CENSORED>() - begin - - ::Sorbet::Private::Static.keep_for_ide(::A) - - end + end end class ::A<> < (::) diff --git a/test/testdata/rewriter/command.rb b/test/testdata/rewriter/command.rb index a807652e9a..42edb6f017 100644 --- a/test/testdata/rewriter/command.rb +++ b/test/testdata/rewriter/command.rb @@ -21,7 +21,7 @@ def call(x) T.assert_type!(OtherCommand.call("8"), Integer) -class NotACommand < Llamas::Opus::Command # error-with-dupes: Unable to resolve constant +class NotACommand < Llamas::Opus::Command # error: Unable to resolve constant extend T::Sig sig {params(x: String).returns(Integer)} diff --git a/test/testdata/rewriter/data.rb b/test/testdata/rewriter/data.rb index cbef1da821..afdc32b226 100644 --- a/test/testdata/rewriter/data.rb +++ b/test/testdata/rewriter/data.rb @@ -67,7 +67,7 @@ class BadUsages C = Data.define(:c) c_data = C.new(1) - c_data.c = 6 # error: Method `c=` does not exist on `BadUsages::C` + c_data.c = 6 # error: Setter method `c=` does not exist on `BadUsages::C` end class Main diff --git a/test/testdata/rewriter/flatten_nested.rb.flatten-tree.exp b/test/testdata/rewriter/flatten_nested.rb.flatten-tree.exp index aa281339f8..ec4e7f3817 100644 --- a/test/testdata/rewriter/flatten_nested.rb.flatten-tree.exp +++ b/test/testdata/rewriter/flatten_nested.rb.flatten-tree.exp @@ -1,35 +1,22 @@ begin - class <>> < (::) + + + + + + + + def self.<$CENSORED>() begin - begin - - ::Sorbet::Private::Static.keep_for_ide(::A) - - end ::A.new().outer_method() ::A.new().inner_method() - begin - - ::Sorbet::Private::Static.keep_for_ide(::B) - - end b = ::B.new() b.outer_method() b.inner_method() - begin - - ::Sorbet::Private::Static.keep_for_ide(::C) - - end ::C.outer_method() ::C.new().inner_method() - begin - - ::Sorbet::Private::Static.keep_for_ide(::D) - - end ::D.outer_method() ::D.inner_method() diff --git a/test/testdata/rewriter/fuzz_optinal_crash.rb b/test/testdata/rewriter/fuzz_optional_crash.rb similarity index 100% rename from test/testdata/rewriter/fuzz_optinal_crash.rb rename to test/testdata/rewriter/fuzz_optional_crash.rb diff --git a/test/testdata/rewriter/initializer.rb.autocorrects.exp b/test/testdata/rewriter/initializer.rb.autocorrects.exp new file mode 100644 index 0000000000..997a0b5621 --- /dev/null +++ b/test/testdata/rewriter/initializer.rb.autocorrects.exp @@ -0,0 +1,219 @@ +# -- test/testdata/rewriter/initializer.rb -- +# typed: true +class Foo + extend T::Sig + + sig { params(x: Integer).void } + def initialize(x) + @x = x # this will get a synthetic `T.let` + @y = x + 1 # This should not, because the RHS is not a simple local + end + + sig { void } + def foo + T.reveal_type(@x) # error: Revealed type: `Integer` + T.reveal_type(@y) # error: Revealed type: `T.untyped` + end +end + +class Bar + extend T::Sig + + # we should find `params` correctly here + sig { overridable.params(x: Integer).overridable.void } + def initialize(x) + @x = x + end + + sig { void } + def foo + T.reveal_type(@x) # error: Revealed type: `Integer` + end +end + +class Baz + extend T::Sig + + # nobody should ever write this, but just in case, we should make + # sure not to include type_parameter types in a generated `T.let` + sig { type_parameters(:U).params(x: T.type_parameter(:U)).void } + def initialize(x) + @x = x + end + + sig { void } + def foo + T.reveal_type(@x) # error: Revealed type: `T.untyped` + end +end + + +# Some miscellaneous weirder cases +class InterspersedExprs + extend T::Sig + + sig { params(x: Integer).void } + def initialize(x) + ["hello", "world"].each do |w| + puts w + end + @x = x + puts "goodbye" + end + + sig { void } + def foo + T.reveal_type(@x) # error: Revealed type: `Integer` + end +end + +# Some less straightforward situations with arguments +class DifferentArgs + extend T::Sig + + sig { params(x: Integer, y: T.nilable(String), z: T.any(T::Array[T::Boolean], T.proc.void)).void } + def initialize(x, y, z) + @a = y # it works out-of-order + r = x + @b = r # it does NOT work with a variable that's not explicitly a param + @c = y # a single param can be used with multiple instance variables + [1, 2].each do + @d = z # it does NOT work within blocks + end + if true + @e = z # it does NOT work within other control flow + end + + @f = z # it does work with more sophisticated types + end + + sig { void } + def foo + T.reveal_type(@a) # error: Revealed type: `T.nilable(String)` + T.reveal_type(@b) # error: Revealed type: `T.untyped` + T.reveal_type(@c) # error: Revealed type: `T.nilable(String) + T.reveal_type(@d) # error: Revealed type: `T.untyped` + T.reveal_type(@e) # error: Revealed type: `T.untyped` + T.reveal_type(@f) # error: Revealed type: `T.any(T::Array[T::Boolean], T.proc.void)` + end +end + +class ClassVar + extend T::Sig + sig { params(x: Integer).void } + def initialize(x) + @@a = x # it does NOT currently work with class variables + end + + sig { void } + def foo + T.reveal_type(@@a) # error: Revealed type: `T.untyped` + end +end + +class NestedConstructor + extend T::Sig + + sig {void} + def self.foo + sig {params(x: Integer).void} + def initialize(x) + # it does not work if the constructor is not declared as a + # top-level method of the class (i.e. if it is nested inside + # something else) due to limitations about how we associate sigs + @x = x + end + end + + def foo + T.reveal_type(@x) # error: Revealed type: `T.untyped` + end +end + +class Checked + extend T::Sig + + sig {params(x: Integer).void.checked(:never)} + def initialize(x) + @x = x + end + + sig {void} + def foo + T.reveal_type(@x) # error: Revealed type: `Integer` + end +end + +class OnFailure + extend T::Sig + + sig {params(x: Integer).void.on_failure(:soft, notify: 'sorbet')} + def initialize(x) + @x = x + end + + sig {void} + def foo + T.reveal_type(@x) # error: Revealed type: `Integer` + end +end + +class BadSigReturn + extend T::Sig + + sig {params(x: Integer).void} # error: The initialize method should always return void + def initialize(x) + @x = x + end +end + +class BadSigReturnNotLastStatement + extend T::Sig + + sig {params(x: Integer).void.on_failure(:soft, notify: 'sorbet')} # error: The initialize method should always return void + def initialize(x) + @x = x + end +end + +class TProcBindInInitializerLet + extend T::Sig + + sig {params(blk: T.proc.bind(String).void).void } + def initialize(&blk) + @blk = blk + end +end + +class NoBindInTProcInitializerLet + extend T::Sig + + sig {params(blk: T.proc.void).void } + def initialize(&blk) + @blk = blk + end +end + +class TProcBindInInitializerLastSend + extend T::Sig + + sig {params(blk: T.proc.bind(String)).void } + # ^^^^^^^^^^^^^^^^^^^ error: Malformed T.proc: You must specify a return type + # ^^^^^^^^^^^^^^^^^^^ error: Malformed T.proc: You must specify a return type + # ^^^^^^^^^^^^^^^^^^^ error: Using `bind` is not permitted here + def initialize(&blk) + @blk = blk + end +end + +class TProcBindInInitializerManyBinds + extend T::Sig + + sig {params(blk: T.proc.bind(String).bind(String).void).void } + # ^^^^^^^^^^^^^^^^^^^ error: Malformed `bind`: Multiple calls to `.bind` + # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Using `bind` is not permitted here + def initialize(&blk) + @blk = blk + end +end +# ------------------------------ diff --git a/test/testdata/rewriter/interface_wrapper.rb b/test/testdata/rewriter/interface_wrapper.rb index 87bdccd82d..2f998b0c19 100644 --- a/test/testdata/rewriter/interface_wrapper.rb +++ b/test/testdata/rewriter/interface_wrapper.rb @@ -21,12 +21,12 @@ def self.wrap_instance(x, y=nil) def testit s = SomeClass.new - wrap = Interface.wrap_instance(s) - T.assert_type!(wrap, Interface) - wrap.other_method # error: does not exist + wrap = Interface.wrap_instance(s) # error: does not exist + T.reveal_type(wrap) # error: `T.untyped` + wrap.other_method wrap.some_method - Other.wrap_instance("hi", "there") # error: Wrong number of arguments + Other.wrap_instance("hi", "there") o = Other - o.wrap_instance("hi") # error: Unsupported wrap_instance() on a non-constant-literal + o.wrap_instance("hi") end diff --git a/test/testdata/rewriter/interface_wrapper.rb.rewrite-tree.exp b/test/testdata/rewriter/interface_wrapper.rb.rewrite-tree.exp index 18ca70972c..fb3deeb054 100644 --- a/test/testdata/rewriter/interface_wrapper.rb.rewrite-tree.exp +++ b/test/testdata/rewriter/interface_wrapper.rb.rewrite-tree.exp @@ -2,8 +2,8 @@ class <>> < (::) def testit<>(&) begin s = ::.new() - wrap = (s, , ::) - (wrap, , ::) + wrap = ::.wrap_instance(s) + ::.reveal_type(wrap) wrap.other_method() wrap.some_method() ::.wrap_instance("hi", "there") diff --git a/test/testdata/rewriter/minitest.rb.rewrite-tree.exp b/test/testdata/rewriter/minitest.rb.rewrite-tree.exp index ee82705e97..451f955307 100644 --- a/test/testdata/rewriter/minitest.rb.rewrite-tree.exp +++ b/test/testdata/rewriter/minitest.rb.rewrite-tree.exp @@ -56,7 +56,7 @@ class <>> < (::) .void() end - def initialize<>(&) + def <>(&) begin @foo = (3, , ::) .instance_helper() @@ -156,7 +156,7 @@ class <>> < (::) - + > > diff --git a/test/testdata/rewriter/minitest_hover.rb b/test/testdata/rewriter/minitest_hover.rb new file mode 100644 index 0000000000..c3f6bd171f --- /dev/null +++ b/test/testdata/rewriter/minitest_hover.rb @@ -0,0 +1,17 @@ +# typed: true + +def describe(name, &blk); end +def it(name, &blk); end +def before(name, &blk); end +def after(name, &blk); end +def test_each(arg, &blk); end +def test_each_hash(hash, &blk); end + +describe 'example' do + test_each(['foo']) do |x| + it "bar #{x}" do + # No results + # ^ hover: null + end + end +end diff --git a/test/testdata/rewriter/minitest_pinning.rb b/test/testdata/rewriter/minitest_pinning.rb new file mode 100644 index 0000000000..9cdc1a1cf8 --- /dev/null +++ b/test/testdata/rewriter/minitest_pinning.rb @@ -0,0 +1,15 @@ +# typed: true + +it '' do + x = nil + 1.times do + x = 1 + # ^ error: Changing the type of a variable in a loop is not permitted + end +end + +x = nil +1.times do + x = 1 + # ^ error: Changing the type of a variable in a loop is not permitted +end diff --git a/test/testdata/rewriter/minitest_pinning.rb.autocorrects.exp b/test/testdata/rewriter/minitest_pinning.rb.autocorrects.exp new file mode 100644 index 0000000000..64bc353c48 --- /dev/null +++ b/test/testdata/rewriter/minitest_pinning.rb.autocorrects.exp @@ -0,0 +1,17 @@ +# -- test/testdata/rewriter/minitest_pinning.rb -- +# typed: true + +it '' do + x = T.let(nil, T.nilable(Integer)) + 1.times do + x = 1 + # ^ error: Changing the type of a variable in a loop is not permitted + end +end + +x = T.let(nil, T.nilable(Integer)) +1.times do + x = 1 + # ^ error: Changing the type of a variable in a loop is not permitted +end +# ------------------------------ diff --git a/test/testdata/rewriter/minitest_tables.rb b/test/testdata/rewriter/minitest_tables.rb index 12c3dcc17e..62b0524e2c 100644 --- a/test/testdata/rewriter/minitest_tables.rb +++ b/test/testdata/rewriter/minitest_tables.rb @@ -68,15 +68,15 @@ def self.it(name, &blk); end end test_each [Parent.new, Child.new] do |x| - y = x # error: Only valid `it`-blocks can appear within `test_each` + y = x # error: Only valid `it`, `before`, `after`, and `describe` blocks can appear within `test_each` end test_each_hash({}) do |k, v| - y = k + v # error: Only valid `it`-blocks can appear within `test_each_hash` + y = k + v # error: Only valid `it`, `before`, `after`, and `describe` blocks can appear within `test_each_hash` end test_each CONST_LIST do |value| - x = value.foo # error: Only valid `it`-blocks can appear within `test_each` + x = value.foo # error: Only valid `it`, `before`, `after`, and `describe` blocks can appear within `test_each` it "fails with non-it statements" do puts x end @@ -181,7 +181,7 @@ def self.it(name, &blk); end # We don't allow manual destructuring currently. test_each [[1,'a'], [2,'b']] do |value| - i, s = value # error: Only valid `it`-blocks can appear within `test_each` + i, s = value # error: Only valid `it`, `before`, `after`, and `describe` blocks can appear within `test_each` it "rejects manual destructuring of the list argument" do T.reveal_type(i) # error: type: `NilClass` T.reveal_type(s) # error: type: `NilClass` diff --git a/test/testdata/rewriter/prop.rb b/test/testdata/rewriter/prop.rb index f2da0d7012..7ceb63ad10 100644 --- a/test/testdata/rewriter/prop.rb +++ b/test/testdata/rewriter/prop.rb @@ -104,9 +104,9 @@ def main T.reveal_type(AdvancedODM.new.hash_of) # error: Revealed type: `T::Hash[Symbol, String]` T.reveal_type(AdvancedODM.new.const_explicit) # error: Revealed type: `String` - AdvancedODM.new.const_explicit = 'b' # error: Method `const_explicit=` does not exist on `AdvancedODM` + AdvancedODM.new.const_explicit = 'b' # error: Setter method `const_explicit=` does not exist on `AdvancedODM` T.reveal_type(AdvancedODM.new.const) # error: Revealed type: `String` - AdvancedODM.new.const = 'b' # error: Method `const=` does not exist on `AdvancedODM` + AdvancedODM.new.const = 'b' # error: Setter method `const=` does not exist on `AdvancedODM` T.reveal_type(AdvancedODM.new.enum_prop) # error: Revealed type: `String` AdvancedODM.new.enum_prop = "hello" @@ -131,13 +131,13 @@ def main PropHelpers2.new.token = nil # error: does not match expected type T.reveal_type(PropHelpers2.new.created) # error: Revealed type: `Float` - PropHelpers2.new.created = 0.0 # error: Method `created=` does not exist + PropHelpers2.new.created = 0.0 # error: Setter method `created=` does not exist T.reveal_type(EncryptedProp.new.foo) # error: Revealed type: `T.nilable(String)` T.reveal_type(EncryptedProp.new.encrypted_foo) # error: Revealed type: `T.nilable(Opus::DB::Model::Mixins::Encryptable::EncryptedValue)` EncryptedProp.new.foo = "hello" EncryptedProp.new.foo = nil - EncryptedProp.new.bar = "hello" # error: Method `bar=` does not exist + EncryptedProp.new.bar = "hello" # error: Setter method `bar=` does not exist T.reveal_type(AdvancedODM.new.ifunset) # error: Revealed type: `String` T.reveal_type(AdvancedODM.new.ifunset_nilable) # error: Revealed type: `T.nilable(String)` diff --git a/test/testdata/rewriter/prop.rb.symbol-table-raw.exp b/test/testdata/rewriter/prop.rb.symbol-table-raw.exp index 656038a4b2..4b526efa55 100644 --- a/test/testdata/rewriter/prop.rb.symbol-table-raw.exp +++ b/test/testdata/rewriter/prop.rb.symbol-table-raw.exp @@ -32,7 +32,7 @@ class >> < > () method ># (foreign, ) -> String @ Loc {file=test/testdata/rewriter/prop.rb start=52:5 end=52:49} argument foreign<> -> String @ Loc {file=test/testdata/rewriter/prop.rb start=52:11 end=52:18} argument -> T.untyped @ Loc {file=test/testdata/rewriter/prop.rb start=??? end=???} - method ># (allow_direct_mutation, ) -> ForeignClass | NilClass @ Loc {file=test/testdata/rewriter/prop.rb start=52:5 end=52:49} + method ># (allow_direct_mutation, ) -> ForeignClass | NilClass @ Loc {file=test/testdata/rewriter/prop.rb start=52:28 end=52:35} argument allow_direct_mutation -> T.nilable(T::Boolean) @ Loc {file=test/testdata/rewriter/prop.rb start=52:11 end=52:18} argument -> T.untyped @ Loc {file=test/testdata/rewriter/prop.rb start=??? end=???} method ># (allow_direct_mutation, ) -> ForeignClass @ Loc {file=test/testdata/rewriter/prop.rb start=52:5 end=52:49} @@ -43,7 +43,7 @@ class >> < > () method ># (foreign_invalid, ) -> String @ Loc {file=test/testdata/rewriter/prop.rb start=55:5 end=55:65} argument foreign_invalid<> -> String @ Loc {file=test/testdata/rewriter/prop.rb start=55:11 end=55:26} argument -> T.untyped @ Loc {file=test/testdata/rewriter/prop.rb start=??? end=???} - method ># (allow_direct_mutation, ) -> T.untyped @ Loc {file=test/testdata/rewriter/prop.rb start=55:5 end=55:65} + method ># (allow_direct_mutation, ) -> T.untyped @ Loc {file=test/testdata/rewriter/prop.rb start=55:36 end=55:43} argument allow_direct_mutation -> T.nilable(T::Boolean) @ Loc {file=test/testdata/rewriter/prop.rb start=55:11 end=55:26} argument -> T.untyped @ Loc {file=test/testdata/rewriter/prop.rb start=??? end=???} method ># (allow_direct_mutation, ) -> T.untyped @ Loc {file=test/testdata/rewriter/prop.rb start=55:5 end=55:65} @@ -54,7 +54,7 @@ class >> < > () method ># (foreign_lazy, ) -> String @ Loc {file=test/testdata/rewriter/prop.rb start=53:5 end=53:59} argument foreign_lazy<> -> String @ Loc {file=test/testdata/rewriter/prop.rb start=53:11 end=53:23} argument -> T.untyped @ Loc {file=test/testdata/rewriter/prop.rb start=??? end=???} - method ># (allow_direct_mutation, ) -> ForeignClass | NilClass @ Loc {file=test/testdata/rewriter/prop.rb start=53:5 end=53:59} + method ># (allow_direct_mutation, ) -> ForeignClass | NilClass @ Loc {file=test/testdata/rewriter/prop.rb start=53:33 end=53:40} argument allow_direct_mutation -> T.nilable(T::Boolean) @ Loc {file=test/testdata/rewriter/prop.rb start=53:11 end=53:23} argument -> T.untyped @ Loc {file=test/testdata/rewriter/prop.rb start=??? end=???} method ># (allow_direct_mutation, ) -> ForeignClass @ Loc {file=test/testdata/rewriter/prop.rb start=53:5 end=53:59} @@ -65,7 +65,7 @@ class >> < > () method ># (foreign_proc, ) -> String @ Loc {file=test/testdata/rewriter/prop.rb start=54:5 end=54:61} argument foreign_proc<> -> String @ Loc {file=test/testdata/rewriter/prop.rb start=54:11 end=54:23} argument -> T.untyped @ Loc {file=test/testdata/rewriter/prop.rb start=??? end=???} - method ># (allow_direct_mutation, ) -> ForeignClass | NilClass @ Loc {file=test/testdata/rewriter/prop.rb start=54:5 end=54:61} + method ># (allow_direct_mutation, ) -> ForeignClass | NilClass @ Loc {file=test/testdata/rewriter/prop.rb start=54:33 end=54:40} argument allow_direct_mutation -> T.nilable(T::Boolean) @ Loc {file=test/testdata/rewriter/prop.rb start=54:11 end=54:23} argument -> T.untyped @ Loc {file=test/testdata/rewriter/prop.rb start=??? end=???} method ># (allow_direct_mutation, ) -> ForeignClass @ Loc {file=test/testdata/rewriter/prop.rb start=54:5 end=54:61} @@ -149,7 +149,7 @@ class >> < > () method > $1># (args, ) @ Loc {file=test/testdata/rewriter/prop.rb start=3:5 end=3:25} argument args @ Loc {file=test/testdata/rewriter/prop.rb start=3:20 end=3:24} argument @ Loc {file=test/testdata/rewriter/prop.rb start=??? end=???} - class > < > (>) @ Loc {file=https://github.com/sorbet/sorbet/tree/master/rbi/core/object.rbi start=removed end=removed} + class > < > (>, >) @ (Loc {file=https://github.com/sorbet/sorbet/tree/master/rbi/stdlib/json.rbi start=removed end=removed}, Loc {file=https://github.com/sorbet/sorbet/tree/master/rbi/core/object.rbi start=removed end=removed}) method ># : private () @ Loc {file=test/testdata/rewriter/prop.rb start=94:1 end=94:9} argument @ Loc {file=test/testdata/rewriter/prop.rb start=??? end=???} module > < >::>::>::> () @ Loc {file=test/testdata/rewriter/prop.rb start=84:7 end=84:11} diff --git a/test/testdata/rewriter/prop_compiled.rb b/test/testdata/rewriter/prop_compiled.rb index 06a5190335..d84c569432 100644 --- a/test/testdata/rewriter/prop_compiled.rb +++ b/test/testdata/rewriter/prop_compiled.rb @@ -105,9 +105,9 @@ def main T.reveal_type(AdvancedODM.new.hash_of) # error: Revealed type: `T::Hash[Symbol, String]` T.reveal_type(AdvancedODM.new.const_explicit) # error: Revealed type: `String` - AdvancedODM.new.const_explicit = 'b' # error: Method `const_explicit=` does not exist on `AdvancedODM` + AdvancedODM.new.const_explicit = 'b' # error: Setter method `const_explicit=` does not exist on `AdvancedODM` T.reveal_type(AdvancedODM.new.const) # error: Revealed type: `String` - AdvancedODM.new.const = 'b' # error: Method `const=` does not exist on `AdvancedODM` + AdvancedODM.new.const = 'b' # error: Setter method `const=` does not exist on `AdvancedODM` T.reveal_type(AdvancedODM.new.enum_prop) # error: Revealed type: `String` AdvancedODM.new.enum_prop = "hello" @@ -132,13 +132,13 @@ def main PropHelpers2.new.token = nil # error: does not match expected type T.reveal_type(PropHelpers2.new.created) # error: Revealed type: `Float` - PropHelpers2.new.created = 0.0 # error: Method `created=` does not exist + PropHelpers2.new.created = 0.0 # error: Setter method `created=` does not exist T.reveal_type(EncryptedProp.new.foo) # error: Revealed type: `T.nilable(String)` T.reveal_type(EncryptedProp.new.encrypted_foo) # error: Revealed type: `T.nilable(Opus::DB::Model::Mixins::Encryptable::EncryptedValue)` EncryptedProp.new.foo = "hello" EncryptedProp.new.foo = nil - EncryptedProp.new.bar = "hello" # error: Method `bar=` does not exist + EncryptedProp.new.bar = "hello" # error: Setter method `bar=` does not exist T.reveal_type(AdvancedODM.new.ifunset) # error: Revealed type: `String` T.reveal_type(AdvancedODM.new.ifunset_nilable) # error: Revealed type: `T.nilable(String)` diff --git a/test/testdata/rewriter/rails/cattr_accessor.rb b/test/testdata/rewriter/rails/cattr_accessor.rb index 4c4e0288ed..3cac7faf71 100644 --- a/test/testdata/rewriter/rails/cattr_accessor.rb +++ b/test/testdata/rewriter/rails/cattr_accessor.rb @@ -13,13 +13,13 @@ def usages self.both = 1 no_instance # error: Method `no_instance` does not exist - self.no_instance = 1 # error: Method `no_instance=` does not exist + self.no_instance = 1 # error: Setter method `no_instance=` does not exist no_instance_reader # error: Method `no_instance_reader` does not exist self.no_instance_reader= 1 no_instance_writer - self.no_instance_writer = 1 # error: Method `no_instance_writer=` does not exist + self.no_instance_writer = 1 # error: Setter method `no_instance_writer=` does not exist end both diff --git a/test/testdata/rewriter/rails/cattr_writer.rb b/test/testdata/rewriter/rails/cattr_writer.rb index 7664f3d7f1..781a5245f8 100644 --- a/test/testdata/rewriter/rails/cattr_writer.rb +++ b/test/testdata/rewriter/rails/cattr_writer.rb @@ -9,8 +9,8 @@ class GoodUsages sig {void} def usages self.both = 1 - self.no_instance = 1 # error: Method `no_instance=` does not exist - self.no_instance_writer = 1 # error: Method `no_instance_writer=` does not exist + self.no_instance = 1 # error: Setter method `no_instance=` does not exist + self.no_instance_writer = 1 # error: Setter method `no_instance_writer=` does not exist end self.both = 1 diff --git a/test/testdata/rewriter/rails/class_attribute.rb b/test/testdata/rewriter/rails/class_attribute.rb index 6da691e197..2f095fdad5 100644 --- a/test/testdata/rewriter/rails/class_attribute.rb +++ b/test/testdata/rewriter/rails/class_attribute.rb @@ -16,7 +16,7 @@ def usages no_instance # error: Method `no_instance` does not exist no_instance? # error: Method `no_instance?` does not exist - self.no_instance = 1 # error: Method `no_instance=` does not exist + self.no_instance = 1 # error: Setter method `no_instance=` does not exist no_instance_reader # error: Method `no_instance_reader` does not exist no_instance_reader? # error: Method `no_instance_reader?` does not exist @@ -24,7 +24,7 @@ def usages no_instance_writer no_instance_writer? - self.no_instance_writer = 1 # error: Method `no_instance_writer=` does not exist + self.no_instance_writer = 1 # error: Setter method `no_instance_writer=` does not exist no_predicate no_predicate? # error: Method `no_predicate?` does not exist diff --git a/test/testdata/rewriter/rails/mattr_accessor.rb b/test/testdata/rewriter/rails/mattr_accessor.rb index 40c60a4b69..5d6783aa77 100644 --- a/test/testdata/rewriter/rails/mattr_accessor.rb +++ b/test/testdata/rewriter/rails/mattr_accessor.rb @@ -13,13 +13,13 @@ def usages self.both = 1 no_instance # error: Method `no_instance` does not exist - self.no_instance = 1 # error: Method `no_instance=` does not exist + self.no_instance = 1 # error: Setter method `no_instance=` does not exist no_instance_reader # error: Method `no_instance_reader` does not exist self.no_instance_reader= 1 no_instance_writer - self.no_instance_writer = 1 # error: Method `no_instance_writer=` does not exist + self.no_instance_writer = 1 # error: Setter method `no_instance_writer=` does not exist end both diff --git a/test/testdata/rewriter/rails/mattr_writer.rb b/test/testdata/rewriter/rails/mattr_writer.rb index f3dde71339..20094cebcb 100644 --- a/test/testdata/rewriter/rails/mattr_writer.rb +++ b/test/testdata/rewriter/rails/mattr_writer.rb @@ -9,8 +9,8 @@ class GoodUsages sig {void} def usages self.both = 1 - self.no_instance = 1 # error: Method `no_instance=` does not exist - self.no_instance_writer = 1 # error: Method `no_instance_writer=` does not exist + self.no_instance = 1 # error: Setter method `no_instance=` does not exist + self.no_instance_writer = 1 # error: Setter method `no_instance_writer=` does not exist end self.both = 1 diff --git a/test/testdata/rewriter/rails/thread_cattr_accessor.rb b/test/testdata/rewriter/rails/thread_cattr_accessor.rb index a47d20cd32..a2e6c609a4 100644 --- a/test/testdata/rewriter/rails/thread_cattr_accessor.rb +++ b/test/testdata/rewriter/rails/thread_cattr_accessor.rb @@ -13,13 +13,13 @@ def usages self.both = 1 no_instance # error: Method `no_instance` does not exist - self.no_instance = 1 # error: Method `no_instance=` does not exist + self.no_instance = 1 # error: Setter method `no_instance=` does not exist no_instance_reader # error: Method `no_instance_reader` does not exist self.no_instance_reader= 1 no_instance_writer - self.no_instance_writer = 1 # error: Method `no_instance_writer=` does not exist + self.no_instance_writer = 1 # error: Setter method `no_instance_writer=` does not exist end both diff --git a/test/testdata/rewriter/rails/thread_mattr_accessor.rb b/test/testdata/rewriter/rails/thread_mattr_accessor.rb index a98f50ef0a..5ab2723b8e 100644 --- a/test/testdata/rewriter/rails/thread_mattr_accessor.rb +++ b/test/testdata/rewriter/rails/thread_mattr_accessor.rb @@ -13,13 +13,13 @@ def usages self.both = 1 no_instance # error: Method `no_instance` does not exist - self.no_instance = 1 # error: Method `no_instance=` does not exist + self.no_instance = 1 # error: Setter method `no_instance=` does not exist no_instance_reader # error: Method `no_instance_reader` does not exist self.no_instance_reader= 1 no_instance_writer - self.no_instance_writer = 1 # error: Method `no_instance_writer=` does not exist + self.no_instance_writer = 1 # error: Setter method `no_instance_writer=` does not exist end both diff --git a/test/testdata/rewriter/shard_by_merchant_prop.rb b/test/testdata/rewriter/shard_by_merchant_prop.rb index e9d8c9b0dd..e2ec9e6da4 100644 --- a/test/testdata/rewriter/shard_by_merchant_prop.rb +++ b/test/testdata/rewriter/shard_by_merchant_prop.rb @@ -39,8 +39,20 @@ class MerchantTokenPropModel merchant_token_prop end +class MerchantTokenPropModelCustomName + include T::Props + extend ShardByMerchantBase + + merchant_token_prop name: :linked_merchant +end + T.reveal_type(MerchantPropModel.new.merchant) # error: Revealed type: `String` -MerchantPropModel.new.merchant = "hi" # error: Method `merchant=` does not exist +MerchantPropModel.new.merchant = "hi" # error: Setter method `merchant=` does not exist T.reveal_type(MerchantTokenPropModel.new.merchant) # error: Revealed type: `Opus::Autogen::Tokens::AccountModelMerchant::Token` -MerchantTokenPropModel.new.merchant = nil # error: Method `merchant=` does not exist +MerchantTokenPropModel.new.merchant = nil # error: Setter method `merchant=` does not exist + +MerchantTokenPropModelCustomName.new.merchant +# ^^^^^^^^ error: does not exist + +T.reveal_type(MerchantTokenPropModelCustomName.new.linked_merchant) # error: `Opus::Autogen::Tokens::AccountModelMerchant::Token` diff --git a/test/testdata/rewriter/shard_by_merchant_prop.rb.rewrite-tree.exp b/test/testdata/rewriter/shard_by_merchant_prop.rb.rewrite-tree.exp index 9bd8e12235..1efabdcad8 100644 --- a/test/testdata/rewriter/shard_by_merchant_prop.rb.rewrite-tree.exp +++ b/test/testdata/rewriter/shard_by_merchant_prop.rb.rewrite-tree.exp @@ -81,6 +81,24 @@ class <>> < (::) end + class ::<>> < (::) + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .returns(::::::::::) + end + + def linked_merchant<>(&) + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") + end + + .include(::::) + + .extend(::) + + .merchant_token_prop(:name, :linked_merchant, :without_accessors, true) + + + end + ::.reveal_type(::.new().merchant()) ::.new().merchant=("hi") @@ -88,4 +106,8 @@ class <>> < (::) ::.reveal_type(::.new().merchant()) ::.new().merchant=(nil) + + ::.new().merchant() + + ::.reveal_type(::.new().linked_merchant()) end diff --git a/test/testdata/rewriter/shard_by_merchant_prop_compiled.rb b/test/testdata/rewriter/shard_by_merchant_prop_compiled.rb index ece176f037..4df49fc132 100644 --- a/test/testdata/rewriter/shard_by_merchant_prop_compiled.rb +++ b/test/testdata/rewriter/shard_by_merchant_prop_compiled.rb @@ -41,7 +41,7 @@ class MerchantTokenPropModel end T.reveal_type(MerchantPropModel.new.merchant) # error: Revealed type: `String` -MerchantPropModel.new.merchant = "hi" # error: Method `merchant=` does not exist +MerchantPropModel.new.merchant = "hi" # error: Setter method `merchant=` does not exist T.reveal_type(MerchantTokenPropModel.new.merchant) # error: Revealed type: `Opus::Autogen::Tokens::AccountModelMerchant::Token` -MerchantTokenPropModel.new.merchant = nil # error: Method `merchant=` does not exist +MerchantTokenPropModel.new.merchant = nil # error: Setter method `merchant=` does not exist diff --git a/test/testdata/rewriter/singleton.rb b/test/testdata/rewriter/singleton.rb index 90e6917b61..7f10a38f3e 100644 --- a/test/testdata/rewriter/singleton.rb +++ b/test/testdata/rewriter/singleton.rb @@ -4,7 +4,7 @@ class A include Singleton end -# Singleton supports inheritence, turning the sub-class into a singleton as well. +# Singleton supports inheritance, turning the sub-class into a singleton as well. class B < A; end T.reveal_type(A.instance) # error: Revealed type: `A` diff --git a/test/testdata/rewriter/struct.rb b/test/testdata/rewriter/struct.rb index 873e74180c..ecc1484e7e 100644 --- a/test/testdata/rewriter/struct.rb +++ b/test/testdata/rewriter/struct.rb @@ -64,13 +64,13 @@ def foo; end self.new(1, 2) # ^^^^ error: Too many positional arguments provided for method `MixinStruct::MyKeywordInitStruct#initialize`. Expected: `0`, got: `2` self.new(giberish: 1) - # ^^^^^^^^^^^^^^^^^^^^^ error: Unrecognized keyword argument `giberish` passed for method `MixinStruct::MyKeywordInitStruct#initialize` + # ^^^^^^^^^^^ error: Unrecognized keyword argument `giberish` passed for method `MixinStruct::MyKeywordInitStruct#initialize` end MyKeywordInitStruct.new(1, 2) # ^^^^ error: Too many positional arguments provided for method `MixinStruct::MyKeywordInitStruct#initialize`. Expected: `0`, got: `2` MyKeywordInitStruct.new(giberish: 1) -# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Unrecognized keyword argument `giberish` passed for method `MixinStruct::MyKeywordInitStruct#initialize` + # ^^^^^^^^^^^ error: Unrecognized keyword argument `giberish` passed for method `MixinStruct::MyKeywordInitStruct#initialize` MyStruct.new.x MyStruct.new.foo end @@ -130,9 +130,9 @@ class Immutable < T::ImmutableStruct class ImmutableTest Immutable.new(a: 1, b: "foo") -# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Unrecognized keyword argument `a` passed for method `Immutable#initialize` + # ^^^^ error: Unrecognized keyword argument `a` passed for method `Immutable#initialize` obj = Immutable.new(b: "foo") obj.b = "bar" - # ^^^ error: Method `b=` does not exist on `Immutable` + # ^^^ error: Setter method `b=` does not exist on `Immutable` end diff --git a/test/testdata/rewriter/struct.rb.rewrite-tree.exp b/test/testdata/rewriter/struct.rb.rewrite-tree.exp index 2535bab4af..abcfeded8d 100644 --- a/test/testdata/rewriter/struct.rb.rewrite-tree.exp +++ b/test/testdata/rewriter/struct.rb.rewrite-tree.exp @@ -13,21 +13,37 @@ class <>> < (::) end class ::<>> < (::) - class ::<>> < (::::) + class ::<>> < (::::) + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .returns(::T.untyped()) + end + def foo<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .params(:foo, ::T.untyped()).returns(::T.untyped()) end def foo=<>(foo, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .returns(::T.untyped()) end def bar<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .params(:bar, ::T.untyped()).returns(::T.untyped()) end def bar=<>(bar, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -35,7 +51,7 @@ class <>> < (::) end def initialize<>(foo = nil, bar = nil, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end @@ -53,21 +69,43 @@ class <>> < (::) end - class ::<>> < (::::) + class ::<>> < (::) + :: = .type_member() do || + {:fixed => ::T.untyped()} + end + end + + class ::<>> < (::::) + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .returns(::T.untyped()) + end + def foo<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .params(:foo, ::T.untyped()).returns(::T.untyped()) end def foo=<>(foo, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .returns(::T.untyped()) end def bar<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .params(:bar, ::T.untyped()).returns(::T.untyped()) end def bar=<>(bar, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -75,7 +113,7 @@ class <>> < (::) end def initialize<>(foo: = nil, bar: = nil, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end @@ -92,6 +130,12 @@ class <>> < (::) end + + class ::<>> < (::) + :: = .type_member() do || + {:fixed => ::T.untyped()} + end + end end class ::<>> < (::) @@ -135,13 +179,21 @@ class <>> < (::) end class ::<>> < (::) - class ::<>> < (::::) + class ::<>> < (::::) + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .returns(::T.untyped()) + end + def foo<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .params(:foo, ::T.untyped()).returns(::T.untyped()) end def foo=<>(foo, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -149,7 +201,7 @@ class <>> < (::) end def initialize<>(foo = nil, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end @@ -163,13 +215,27 @@ class <>> < (::) end - class ::<>> < (::::) + class ::<>> < (::) + :: = .type_member() do || + {:fixed => ::T.untyped()} + end + end + + class ::<>> < (::::) + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .returns(::T.untyped()) + end + def foo<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .params(:foo, ::T.untyped()).returns(::T.untyped()) end def foo=<>(foo, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -177,7 +243,7 @@ class <>> < (::) end def initialize<>(foo = nil, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end @@ -190,27 +256,49 @@ class <>> < (::) end + + class ::<>> < (::) + :: = .type_member() do || + {:fixed => ::T.untyped()} + end + end end class ::<>> < (::) class ::<>> < (::) end - class ::<>> < (::::) + class ::<>> < (::::) + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .returns(::T.untyped()) + end + def foo<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .params(:foo, ::T.untyped()).returns(::T.untyped()) end def foo=<>(foo, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .returns(::T.untyped()) end def bar<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .params(:bar, ::T.untyped()).returns(::T.untyped()) end def bar=<>(bar, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -218,7 +306,7 @@ class <>> < (::) end def initialize<>(foo = nil, bar = nil, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end @@ -235,16 +323,30 @@ class <>> < (::) end + + class ::<>> < (::) + :: = .type_member() do || + {:fixed => ::T.untyped()} + end + end end class ::<>> < (::) - class ::<>> < (::::) + class ::<>> < (::::) + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .returns(::T.untyped()) + end + def foo=<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .params(:foo=, ::T.untyped()).returns(::T.untyped()) end def foo==<>(foo=, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -252,7 +354,7 @@ class <>> < (::) end def initialize<>(foo= = nil, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end @@ -265,6 +367,12 @@ class <>> < (::) end + + class ::<>> < (::) + :: = .type_member() do || + {:fixed => ::T.untyped()} + end + end end class ::<>> < (::) @@ -276,13 +384,21 @@ class <>> < (::) end - class ::<>> < (::::) + class ::<>> < (::::) + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .returns(::T.untyped()) + end + def x<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .params(:x, ::T.untyped()).returns(::T.untyped()) end def x=<>(x, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -290,7 +406,7 @@ class <>> < (::) end def initialize<>(x = nil, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end @@ -302,6 +418,12 @@ class <>> < (::) end + end + + class ::<>> < (::) + :: = .type_member() do || + {:fixed => ::T.untyped()} + end .include(::) @@ -310,13 +432,21 @@ class <>> < (::) .new().foo() end - class ::<>> < (::::) + class ::<>> < (::::) + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .returns(::T.untyped()) + end + def x<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .params(:x, ::T.untyped()).returns(::T.untyped()) end def x=<>(x, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -324,7 +454,7 @@ class <>> < (::) end def initialize<>(x: = nil, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end @@ -336,6 +466,12 @@ class <>> < (::) end + end + + class ::<>> < (::) + :: = .type_member() do || + {:fixed => ::T.untyped()} + end .include(::) @@ -394,13 +530,21 @@ class <>> < (::) .puts(::.new().main()) class ::<>> < (::) - class ::<>> < (::::) + class ::<>> < (::::) + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .returns(::T.untyped()) + end + def a<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .params(:a, ::T.untyped()).returns(::T.untyped()) end def a=<>(a, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -408,7 +552,7 @@ class <>> < (::) end def initialize<>(a = nil, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end @@ -422,13 +566,27 @@ class <>> < (::) end - class ::<>> < (::::) + class ::<>> < (::) + :: = .type_member() do || + {:fixed => ::T.untyped()} + end + end + + class ::<>> < (::::) + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .returns(::T.untyped()) + end + def a<>(&) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .params(:a, ::T.untyped()).returns(::T.untyped()) end def a=<>(a, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || @@ -436,7 +594,7 @@ class <>> < (::) end def initialize<>(a = nil, &) - ::T.unsafe(::Kernel).raise("Sorbet rewriter pass partially unimplemented") + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") end @@ -450,6 +608,12 @@ class <>> < (::) end + class ::<>> < (::) + :: = .type_member() do || + {:fixed => ::T.untyped()} + end + end + :: = >(::::::.new(), , ::::::) ::.new().a() diff --git a/test/testdata/rewriter/struct.rb.symbol-table-raw.exp b/test/testdata/rewriter/struct.rb.symbol-table-raw.exp index 009822e6fb..fc48911163 100644 --- a/test/testdata/rewriter/struct.rb.symbol-table-raw.exp +++ b/test/testdata/rewriter/struct.rb.symbol-table-raw.exp @@ -3,23 +3,29 @@ class >> < > () method >> $1>#> $CENSORED> () @ Loc {file=test/testdata/rewriter/struct.rb start=2:1 end=138:4} argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} class > < > () @ Loc {file=test/testdata/rewriter/struct.rb start=37:1 end=37:25} - class >::> < > () @ Loc {file=test/testdata/rewriter/struct.rb start=42:5 end=42:31} - type-member(=) >::>::> -> LambdaParam(>::>::>, fixed=T.untyped) @ Loc {file=test/testdata/rewriter/struct.rb start=42:5 end=42:31} - method >::># () @ Loc {file=test/testdata/rewriter/struct.rb start=42:27 end=42:30} - argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - method >::># (bar, ) @ Loc {file=test/testdata/rewriter/struct.rb start=42:27 end=42:30} - argument bar<> @ Loc {file=test/testdata/rewriter/struct.rb start=42:27 end=42:30} - argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - method >::># () @ Loc {file=test/testdata/rewriter/struct.rb start=42:21 end=42:24} - argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - method >::># (foo, ) @ Loc {file=test/testdata/rewriter/struct.rb start=42:21 end=42:24} - argument foo<> @ Loc {file=test/testdata/rewriter/struct.rb start=42:21 end=42:24} - argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - method >::># : private (foo, bar, ) -> Sorbet::Private::Static::Void @ Loc {file=test/testdata/rewriter/struct.rb start=42:5 end=42:31} + class >::> $1>> < > () @ Loc {file=test/testdata/rewriter/struct.rb start=42:5 end=42:31} + type-member(=) >::> $1>>::> -> LambdaParam(>::> $1>>::>, fixed=T.untyped) @ Loc {file=test/testdata/rewriter/struct.rb start=42:5 end=42:31} + method >::> $1>># () -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=42:27 end=42:30} + argument -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} + method >::> $1>># (bar, ) -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=42:27 end=42:30} + argument bar<> -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=42:27 end=42:30} + argument -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} + method >::> $1>># () -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=42:21 end=42:24} + argument -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} + method >::> $1>># (foo, ) -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=42:21 end=42:24} + argument foo<> -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=42:21 end=42:24} + argument -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} + method >::> $1>># : private (foo, bar, ) -> Sorbet::Private::Static::Void @ Loc {file=test/testdata/rewriter/struct.rb start=42:5 end=42:31} argument foo -> BasicObject @ Loc {file=test/testdata/rewriter/struct.rb start=42:21 end=42:24} argument bar -> BasicObject @ Loc {file=test/testdata/rewriter/struct.rb start=42:27 end=42:30} argument -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - class >::> $1>[>>] < > $1> () @ Loc {file=test/testdata/rewriter/struct.rb start=42:5 end=42:31} + class >::> < >::> $1>> () @ Loc {file=test/testdata/rewriter/struct.rb start=42:5 end=42:31} + type-member(=) >::>::> -> LambdaParam(>::>::>, fixed=T.untyped) @ Loc {file=test/testdata/rewriter/struct.rb start=42:5 end=42:31} + class >::> $1>> $1>[>>] < > $1> () @ Loc {file=test/testdata/rewriter/struct.rb start=42:5 end=42:31} + type-member(+) >::> $1>> $1>::>> -> LambdaParam(>::> $1>> $1>::>>, lower=T.noreturn, upper=AppliedType { klass = >::> $1>> targs = [ > = T.untyped ] }) @ Loc {file=test/testdata/rewriter/struct.rb start=42:5 end=42:31} + method >::> $1>> $1>#> () @ Loc {file=test/testdata/rewriter/struct.rb start=42:5 end=42:31} + argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} + class >::> $1>[>>] < >::> $1>> $1> () @ Loc {file=test/testdata/rewriter/struct.rb start=42:5 end=42:31} type-member(+) >::> $1>::>> -> LambdaParam(>::> $1>::>>, lower=T.noreturn, upper=AppliedType { klass = >::> targs = [ > = T.untyped ] }) @ Loc {file=test/testdata/rewriter/struct.rb start=42:5 end=42:31} method >::> $1>#> () @ Loc {file=test/testdata/rewriter/struct.rb start=42:5 end=42:31} argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} @@ -52,32 +58,44 @@ class >> < > () method > $1>#> () @ Loc {file=test/testdata/rewriter/struct.rb start=4:1 end=7:4} argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} class > < > () @ Loc {file=test/testdata/rewriter/struct.rb start=115:1 end=115:33} - class >::> < > () @ Loc {file=test/testdata/rewriter/struct.rb start=117:3 end=117:25} - type-member(=) >::>::> -> LambdaParam(>::>::>, fixed=T.untyped) @ Loc {file=test/testdata/rewriter/struct.rb start=117:3 end=117:25} - method >::># () @ Loc {file=test/testdata/rewriter/struct.rb start=117:23 end=117:24} - argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - method >::># (a, ) @ Loc {file=test/testdata/rewriter/struct.rb start=117:23 end=117:24} - argument a<> @ Loc {file=test/testdata/rewriter/struct.rb start=117:23 end=117:24} - argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - method >::># : private (a, ) -> Sorbet::Private::Static::Void @ Loc {file=test/testdata/rewriter/struct.rb start=117:3 end=117:25} + class >::> $1>> < > () @ Loc {file=test/testdata/rewriter/struct.rb start=117:3 end=117:25} + type-member(=) >::> $1>>::> -> LambdaParam(>::> $1>>::>, fixed=T.untyped) @ Loc {file=test/testdata/rewriter/struct.rb start=117:3 end=117:25} + method >::> $1>># () -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=117:23 end=117:24} + argument -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} + method >::> $1>># (a, ) -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=117:23 end=117:24} + argument a<> -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=117:23 end=117:24} + argument -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} + method >::> $1>># : private (a, ) -> Sorbet::Private::Static::Void @ Loc {file=test/testdata/rewriter/struct.rb start=117:3 end=117:25} argument a -> BasicObject @ Loc {file=test/testdata/rewriter/struct.rb start=117:23 end=117:24} argument -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - class >::> $1>[>>] < > $1> () @ Loc {file=test/testdata/rewriter/struct.rb start=117:3 end=117:25} + class >::> < >::> $1>> () @ Loc {file=test/testdata/rewriter/struct.rb start=117:3 end=117:25} + type-member(=) >::>::> -> LambdaParam(>::>::>, fixed=T.untyped) @ Loc {file=test/testdata/rewriter/struct.rb start=117:3 end=117:25} + class >::> $1>> $1>[>>] < > $1> () @ Loc {file=test/testdata/rewriter/struct.rb start=117:3 end=117:25} + type-member(+) >::> $1>> $1>::>> -> LambdaParam(>::> $1>> $1>::>>, lower=T.noreturn, upper=AppliedType { klass = >::> $1>> targs = [ > = T.untyped ] }) @ Loc {file=test/testdata/rewriter/struct.rb start=117:3 end=117:25} + method >::> $1>> $1>#> () @ Loc {file=test/testdata/rewriter/struct.rb start=117:3 end=117:25} + argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} + class >::> $1>[>>] < >::> $1>> $1> () @ Loc {file=test/testdata/rewriter/struct.rb start=117:3 end=117:25} type-member(+) >::> $1>::>> -> LambdaParam(>::> $1>::>>, lower=T.noreturn, upper=AppliedType { klass = >::> targs = [ > = T.untyped ] }) @ Loc {file=test/testdata/rewriter/struct.rb start=117:3 end=117:25} method >::> $1>#> () @ Loc {file=test/testdata/rewriter/struct.rb start=117:3 end=117:25} argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} static-field >::> -> Foo::Struct @ Loc {file=test/testdata/rewriter/struct.rb start=118:3 end=118:6} - class >::> < > () @ Loc {file=test/testdata/rewriter/struct.rb start=116:3 end=116:23} - type-member(=) >::>::> -> LambdaParam(>::>::>, fixed=T.untyped) @ Loc {file=test/testdata/rewriter/struct.rb start=116:3 end=116:23} - method >::># () @ Loc {file=test/testdata/rewriter/struct.rb start=116:21 end=116:22} - argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - method >::># (a, ) @ Loc {file=test/testdata/rewriter/struct.rb start=116:21 end=116:22} - argument a<> @ Loc {file=test/testdata/rewriter/struct.rb start=116:21 end=116:22} - argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - method >::># : private (a, ) -> Sorbet::Private::Static::Void @ Loc {file=test/testdata/rewriter/struct.rb start=116:3 end=116:23} + class >::> $1>> < > () @ Loc {file=test/testdata/rewriter/struct.rb start=116:3 end=116:23} + type-member(=) >::> $1>>::> -> LambdaParam(>::> $1>>::>, fixed=T.untyped) @ Loc {file=test/testdata/rewriter/struct.rb start=116:3 end=116:23} + method >::> $1>># () -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=116:21 end=116:22} + argument -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} + method >::> $1>># (a, ) -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=116:21 end=116:22} + argument a<> -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=116:21 end=116:22} + argument -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} + method >::> $1>># : private (a, ) -> Sorbet::Private::Static::Void @ Loc {file=test/testdata/rewriter/struct.rb start=116:3 end=116:23} argument a -> BasicObject @ Loc {file=test/testdata/rewriter/struct.rb start=116:21 end=116:22} argument -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - class >::> $1>[>>] < > $1> () @ Loc {file=test/testdata/rewriter/struct.rb start=116:3 end=116:23} + class >::> < >::> $1>> () @ Loc {file=test/testdata/rewriter/struct.rb start=116:3 end=116:23} + type-member(=) >::>::> -> LambdaParam(>::>::>, fixed=T.untyped) @ Loc {file=test/testdata/rewriter/struct.rb start=116:3 end=116:23} + class >::> $1>> $1>[>>] < > $1> () @ Loc {file=test/testdata/rewriter/struct.rb start=116:3 end=116:23} + type-member(+) >::> $1>> $1>::>> -> LambdaParam(>::> $1>> $1>::>>, lower=T.noreturn, upper=AppliedType { klass = >::> $1>> targs = [ > = T.untyped ] }) @ Loc {file=test/testdata/rewriter/struct.rb start=116:3 end=116:23} + method >::> $1>> $1>#> () @ Loc {file=test/testdata/rewriter/struct.rb start=116:3 end=116:23} + argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} + class >::> $1>[>>] < >::> $1>> $1> () @ Loc {file=test/testdata/rewriter/struct.rb start=116:3 end=116:23} type-member(+) >::> $1>::>> -> LambdaParam(>::> $1>::>>, lower=T.noreturn, upper=AppliedType { klass = >::> targs = [ > = T.untyped ] }) @ Loc {file=test/testdata/rewriter/struct.rb start=116:3 end=116:23} method >::> $1>#> () @ Loc {file=test/testdata/rewriter/struct.rb start=116:3 end=116:23} argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} @@ -102,17 +120,23 @@ class >> < > () method > $1>#> () @ Loc {file=test/testdata/rewriter/struct.rb start=131:1 end=138:4} argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} class > < > () @ Loc {file=test/testdata/rewriter/struct.rb start=45:1 end=45:20} - class >::> < > () @ Loc {file=test/testdata/rewriter/struct.rb start=46:3 end=46:24} - type-member(=) >::>::> -> LambdaParam(>::>::>, fixed=T.untyped) @ Loc {file=test/testdata/rewriter/struct.rb start=46:3 end=46:24} - method >::># () @ Loc {file=test/testdata/rewriter/struct.rb start=46:19 end=46:23} - argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - method >::># (foo=, ) @ Loc {file=test/testdata/rewriter/struct.rb start=46:19 end=46:23} - argument foo=<> @ Loc {file=test/testdata/rewriter/struct.rb start=46:19 end=46:23} - argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - method >::># : private (foo=, ) -> Sorbet::Private::Static::Void @ Loc {file=test/testdata/rewriter/struct.rb start=46:3 end=46:24} + class >::> $1>> < > () @ Loc {file=test/testdata/rewriter/struct.rb start=46:3 end=46:24} + type-member(=) >::> $1>>::> -> LambdaParam(>::> $1>>::>, fixed=T.untyped) @ Loc {file=test/testdata/rewriter/struct.rb start=46:3 end=46:24} + method >::> $1>># () -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=46:19 end=46:23} + argument -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} + method >::> $1>># (foo=, ) -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=46:19 end=46:23} + argument foo=<> -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=46:19 end=46:23} + argument -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} + method >::> $1>># : private (foo=, ) -> Sorbet::Private::Static::Void @ Loc {file=test/testdata/rewriter/struct.rb start=46:3 end=46:24} argument foo= -> BasicObject @ Loc {file=test/testdata/rewriter/struct.rb start=46:19 end=46:23} argument -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - class >::> $1>[>>] < > $1> () @ Loc {file=test/testdata/rewriter/struct.rb start=46:3 end=46:24} + class >::> < >::> $1>> () @ Loc {file=test/testdata/rewriter/struct.rb start=46:3 end=46:24} + type-member(=) >::>::> -> LambdaParam(>::>::>, fixed=T.untyped) @ Loc {file=test/testdata/rewriter/struct.rb start=46:3 end=46:24} + class >::> $1>> $1>[>>] < > $1> () @ Loc {file=test/testdata/rewriter/struct.rb start=46:3 end=46:24} + type-member(+) >::> $1>> $1>::>> -> LambdaParam(>::> $1>> $1>::>>, lower=T.noreturn, upper=AppliedType { klass = >::> $1>> targs = [ > = T.untyped ] }) @ Loc {file=test/testdata/rewriter/struct.rb start=46:3 end=46:24} + method >::> $1>> $1>#> () @ Loc {file=test/testdata/rewriter/struct.rb start=46:3 end=46:24} + argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} + class >::> $1>[>>] < >::> $1>> $1> () @ Loc {file=test/testdata/rewriter/struct.rb start=46:3 end=46:24} type-member(+) >::> $1>::>> -> LambdaParam(>::> $1>::>>, lower=T.noreturn, upper=AppliedType { klass = >::> targs = [ > = T.untyped ] }) @ Loc {file=test/testdata/rewriter/struct.rb start=46:3 end=46:24} method >::> $1>#> () @ Loc {file=test/testdata/rewriter/struct.rb start=46:3 end=46:24} argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} @@ -128,17 +152,23 @@ class >> < > () method > $1>#> () @ Loc {file=test/testdata/rewriter/struct.rb start=90:1 end=112:4} argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} class > < > () @ Loc {file=test/testdata/rewriter/struct.rb start=49:1 end=49:18} - class >::> < > (>) @ Loc {file=test/testdata/rewriter/struct.rb start=60:3 end=68:6} - type-member(=) >::>::> -> LambdaParam(>::>::>, fixed=T.untyped) @ Loc {file=test/testdata/rewriter/struct.rb start=60:3 end=68:6} - method >::># : private (x, ) -> Sorbet::Private::Static::Void @ Loc {file=test/testdata/rewriter/struct.rb start=60:3 end=68:6} + class >::> $1>> < > () @ Loc {file=test/testdata/rewriter/struct.rb start=60:3 end=68:6} + type-member(=) >::> $1>>::> -> LambdaParam(>::> $1>>::>, fixed=T.untyped) @ Loc {file=test/testdata/rewriter/struct.rb start=60:3 end=68:6} + method >::> $1>># : private (x, ) -> Sorbet::Private::Static::Void @ Loc {file=test/testdata/rewriter/struct.rb start=60:3 end=68:6} argument x -> BasicObject @ Loc {file=test/testdata/rewriter/struct.rb start=60:37 end=60:38} argument -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - method >::># () @ Loc {file=test/testdata/rewriter/struct.rb start=60:37 end=60:38} - argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - method >::># (x, ) @ Loc {file=test/testdata/rewriter/struct.rb start=60:37 end=60:38} - argument x<> @ Loc {file=test/testdata/rewriter/struct.rb start=60:37 end=60:38} + method >::> $1>># () -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=60:37 end=60:38} + argument -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} + method >::> $1>># (x, ) -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=60:37 end=60:38} + argument x<> -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=60:37 end=60:38} + argument -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} + class >::> < >::> $1>> (>) @ Loc {file=test/testdata/rewriter/struct.rb start=60:3 end=68:6} + type-member(=) >::>::> -> LambdaParam(>::>::>, fixed=T.untyped) @ Loc {file=test/testdata/rewriter/struct.rb start=60:3 end=68:6} + class >::> $1>> $1>[>>] < > $1> () @ Loc {file=test/testdata/rewriter/struct.rb start=60:3 end=68:6} + type-member(+) >::> $1>> $1>::>> -> LambdaParam(>::> $1>> $1>::>>, lower=T.noreturn, upper=AppliedType { klass = >::> $1>> targs = [ > = T.untyped ] }) @ Loc {file=test/testdata/rewriter/struct.rb start=60:3 end=68:6} + method >::> $1>> $1>#> () @ Loc {file=test/testdata/rewriter/struct.rb start=60:3 end=68:6} argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - class >::> $1>[>>] < > $1> () @ Loc {file=test/testdata/rewriter/struct.rb start=60:3 end=68:6} + class >::> $1>[>>] < >::> $1>> $1> () @ Loc {file=test/testdata/rewriter/struct.rb start=60:3 end=68:6} type-member(+) >::> $1>::>> -> LambdaParam(>::> $1>::>>, lower=T.noreturn, upper=AppliedType { klass = >::> targs = [ > = T.untyped ] }) @ Loc {file=test/testdata/rewriter/struct.rb start=60:3 end=68:6} method >::> $1>#> () @ Loc {file=test/testdata/rewriter/struct.rb start=60:3 end=68:6} argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} @@ -148,17 +178,23 @@ class >> < > () class >::> $1> < > () @ Loc {file=test/testdata/rewriter/struct.rb start=50:3 end=50:17} method >::> $1>#> () @ Loc {file=test/testdata/rewriter/struct.rb start=50:3 end=52:6} argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - class >::> < > (>) @ Loc {file=test/testdata/rewriter/struct.rb start=54:3 end=58:6} - type-member(=) >::>::> -> LambdaParam(>::>::>, fixed=T.untyped) @ Loc {file=test/testdata/rewriter/struct.rb start=54:3 end=58:6} - method >::># : private (x, ) -> Sorbet::Private::Static::Void @ Loc {file=test/testdata/rewriter/struct.rb start=54:3 end=58:6} + class >::> $1>> < > () @ Loc {file=test/testdata/rewriter/struct.rb start=54:3 end=58:6} + type-member(=) >::> $1>>::> -> LambdaParam(>::> $1>>::>, fixed=T.untyped) @ Loc {file=test/testdata/rewriter/struct.rb start=54:3 end=58:6} + method >::> $1>># : private (x, ) -> Sorbet::Private::Static::Void @ Loc {file=test/testdata/rewriter/struct.rb start=54:3 end=58:6} argument x -> BasicObject @ Loc {file=test/testdata/rewriter/struct.rb start=54:26 end=54:27} argument -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - method >::># () @ Loc {file=test/testdata/rewriter/struct.rb start=54:26 end=54:27} - argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - method >::># (x, ) @ Loc {file=test/testdata/rewriter/struct.rb start=54:26 end=54:27} - argument x<> @ Loc {file=test/testdata/rewriter/struct.rb start=54:26 end=54:27} + method >::> $1>># () -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=54:26 end=54:27} + argument -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} + method >::> $1>># (x, ) -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=54:26 end=54:27} + argument x<> -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=54:26 end=54:27} + argument -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} + class >::> < >::> $1>> (>) @ Loc {file=test/testdata/rewriter/struct.rb start=54:3 end=58:6} + type-member(=) >::>::> -> LambdaParam(>::>::>, fixed=T.untyped) @ Loc {file=test/testdata/rewriter/struct.rb start=54:3 end=58:6} + class >::> $1>> $1>[>>] < > $1> () @ Loc {file=test/testdata/rewriter/struct.rb start=54:3 end=58:6} + type-member(+) >::> $1>> $1>::>> -> LambdaParam(>::> $1>> $1>::>>, lower=T.noreturn, upper=AppliedType { klass = >::> $1>> targs = [ > = T.untyped ] }) @ Loc {file=test/testdata/rewriter/struct.rb start=54:3 end=58:6} + method >::> $1>> $1>#> () @ Loc {file=test/testdata/rewriter/struct.rb start=54:3 end=58:6} argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - class >::> $1>[>>] < > $1> () @ Loc {file=test/testdata/rewriter/struct.rb start=54:3 end=58:6} + class >::> $1>[>>] < >::> $1>> $1> () @ Loc {file=test/testdata/rewriter/struct.rb start=54:3 end=58:6} type-member(+) >::> $1>::>> -> LambdaParam(>::> $1>::>>, lower=T.noreturn, upper=AppliedType { klass = >::> targs = [ > = T.untyped ] }) @ Loc {file=test/testdata/rewriter/struct.rb start=54:3 end=58:6} method >::> $1>#> () @ Loc {file=test/testdata/rewriter/struct.rb start=54:3 end=58:6} argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} @@ -173,43 +209,55 @@ class >> < > () method > $1>#> () @ Loc {file=test/testdata/rewriter/struct.rb start=9:1 end=12:4} argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} class > < > () @ Loc {file=test/testdata/rewriter/struct.rb start=14:1 end=14:17} - class >::> < > () @ Loc {file=test/testdata/rewriter/struct.rb start=15:5 end=15:31} - type-member(=) >::>::> -> LambdaParam(>::>::>, fixed=T.untyped) @ Loc {file=test/testdata/rewriter/struct.rb start=15:5 end=15:31} - method >::># () @ Loc {file=test/testdata/rewriter/struct.rb start=15:27 end=15:30} - argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - method >::># (bar, ) @ Loc {file=test/testdata/rewriter/struct.rb start=15:27 end=15:30} - argument bar<> @ Loc {file=test/testdata/rewriter/struct.rb start=15:27 end=15:30} - argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - method >::># () @ Loc {file=test/testdata/rewriter/struct.rb start=15:21 end=15:24} - argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - method >::># (foo, ) @ Loc {file=test/testdata/rewriter/struct.rb start=15:21 end=15:24} - argument foo<> @ Loc {file=test/testdata/rewriter/struct.rb start=15:21 end=15:24} - argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - method >::># : private (foo, bar, ) -> Sorbet::Private::Static::Void @ Loc {file=test/testdata/rewriter/struct.rb start=15:5 end=15:31} + class >::> $1>> < > () @ Loc {file=test/testdata/rewriter/struct.rb start=15:5 end=15:31} + type-member(=) >::> $1>>::> -> LambdaParam(>::> $1>>::>, fixed=T.untyped) @ Loc {file=test/testdata/rewriter/struct.rb start=15:5 end=15:31} + method >::> $1>># () -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=15:27 end=15:30} + argument -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} + method >::> $1>># (bar, ) -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=15:27 end=15:30} + argument bar<> -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=15:27 end=15:30} + argument -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} + method >::> $1>># () -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=15:21 end=15:24} + argument -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} + method >::> $1>># (foo, ) -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=15:21 end=15:24} + argument foo<> -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=15:21 end=15:24} + argument -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} + method >::> $1>># : private (foo, bar, ) -> Sorbet::Private::Static::Void @ Loc {file=test/testdata/rewriter/struct.rb start=15:5 end=15:31} argument foo -> BasicObject @ Loc {file=test/testdata/rewriter/struct.rb start=15:21 end=15:24} argument bar -> BasicObject @ Loc {file=test/testdata/rewriter/struct.rb start=15:27 end=15:30} argument -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - class >::> $1>[>>] < > $1> () @ Loc {file=test/testdata/rewriter/struct.rb start=15:5 end=15:31} + class >::> < >::> $1>> () @ Loc {file=test/testdata/rewriter/struct.rb start=15:5 end=15:31} + type-member(=) >::>::> -> LambdaParam(>::>::>, fixed=T.untyped) @ Loc {file=test/testdata/rewriter/struct.rb start=15:5 end=15:31} + class >::> $1>> $1>[>>] < > $1> () @ Loc {file=test/testdata/rewriter/struct.rb start=15:5 end=15:31} + type-member(+) >::> $1>> $1>::>> -> LambdaParam(>::> $1>> $1>::>>, lower=T.noreturn, upper=AppliedType { klass = >::> $1>> targs = [ > = T.untyped ] }) @ Loc {file=test/testdata/rewriter/struct.rb start=15:5 end=15:31} + method >::> $1>> $1>#> () @ Loc {file=test/testdata/rewriter/struct.rb start=15:5 end=15:31} + argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} + class >::> $1>[>>] < >::> $1>> $1> () @ Loc {file=test/testdata/rewriter/struct.rb start=15:5 end=15:31} type-member(+) >::> $1>::>> -> LambdaParam(>::> $1>::>>, lower=T.noreturn, upper=AppliedType { klass = >::> targs = [ > = T.untyped ] }) @ Loc {file=test/testdata/rewriter/struct.rb start=15:5 end=15:31} method >::> $1>#> () @ Loc {file=test/testdata/rewriter/struct.rb start=15:5 end=15:31} argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - class >::> < > () @ Loc {file=test/testdata/rewriter/struct.rb start=16:5 end=16:61} - type-member(=) >::>::> -> LambdaParam(>::>::>, fixed=T.untyped) @ Loc {file=test/testdata/rewriter/struct.rb start=16:5 end=16:61} - method >::># () @ Loc {file=test/testdata/rewriter/struct.rb start=16:37 end=16:40} - argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - method >::># (bar, ) @ Loc {file=test/testdata/rewriter/struct.rb start=16:37 end=16:40} - argument bar<> @ Loc {file=test/testdata/rewriter/struct.rb start=16:37 end=16:40} - argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - method >::># () @ Loc {file=test/testdata/rewriter/struct.rb start=16:31 end=16:34} - argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - method >::># (foo, ) @ Loc {file=test/testdata/rewriter/struct.rb start=16:31 end=16:34} - argument foo<> @ Loc {file=test/testdata/rewriter/struct.rb start=16:31 end=16:34} - argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - method >::># : private (foo, bar, ) -> Sorbet::Private::Static::Void @ Loc {file=test/testdata/rewriter/struct.rb start=16:5 end=16:61} + class >::> $1>> < > () @ Loc {file=test/testdata/rewriter/struct.rb start=16:5 end=16:61} + type-member(=) >::> $1>>::> -> LambdaParam(>::> $1>>::>, fixed=T.untyped) @ Loc {file=test/testdata/rewriter/struct.rb start=16:5 end=16:61} + method >::> $1>># () -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=16:37 end=16:40} + argument -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} + method >::> $1>># (bar, ) -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=16:37 end=16:40} + argument bar<> -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=16:37 end=16:40} + argument -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} + method >::> $1>># () -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=16:31 end=16:34} + argument -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} + method >::> $1>># (foo, ) -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=16:31 end=16:34} + argument foo<> -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=16:31 end=16:34} + argument -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} + method >::> $1>># : private (foo, bar, ) -> Sorbet::Private::Static::Void @ Loc {file=test/testdata/rewriter/struct.rb start=16:5 end=16:61} argument foo -> BasicObject @ Loc {file=test/testdata/rewriter/struct.rb start=16:31 end=16:34} argument bar -> BasicObject @ Loc {file=test/testdata/rewriter/struct.rb start=16:37 end=16:40} argument -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - class >::> $1>[>>] < > $1> () @ Loc {file=test/testdata/rewriter/struct.rb start=16:5 end=16:61} + class >::> < >::> $1>> () @ Loc {file=test/testdata/rewriter/struct.rb start=16:5 end=16:61} + type-member(=) >::>::> -> LambdaParam(>::>::>, fixed=T.untyped) @ Loc {file=test/testdata/rewriter/struct.rb start=16:5 end=16:61} + class >::> $1>> $1>[>>] < > $1> () @ Loc {file=test/testdata/rewriter/struct.rb start=16:5 end=16:61} + type-member(+) >::> $1>> $1>::>> -> LambdaParam(>::> $1>> $1>::>>, lower=T.noreturn, upper=AppliedType { klass = >::> $1>> targs = [ > = T.untyped ] }) @ Loc {file=test/testdata/rewriter/struct.rb start=16:5 end=16:61} + method >::> $1>> $1>#> () @ Loc {file=test/testdata/rewriter/struct.rb start=16:5 end=16:61} + argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} + class >::> $1>[>>] < >::> $1>> $1> () @ Loc {file=test/testdata/rewriter/struct.rb start=16:5 end=16:61} type-member(+) >::> $1>::>> -> LambdaParam(>::> $1>::>>, lower=T.noreturn, upper=AppliedType { klass = >::> targs = [ > = T.untyped ] }) @ Loc {file=test/testdata/rewriter/struct.rb start=16:5 end=16:61} method >::> $1>#> () @ Loc {file=test/testdata/rewriter/struct.rb start=16:5 end=16:61} argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} @@ -243,31 +291,43 @@ class >> < > () method > $1>#> () @ Loc {file=test/testdata/rewriter/struct.rb start=18:1 end=30:4} argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} class > < > () @ Loc {file=test/testdata/rewriter/struct.rb start=32:1 end=32:17} - class >::> < > () @ Loc {file=test/testdata/rewriter/struct.rb start=33:5 end=33:25} - type-member(=) >::>::> -> LambdaParam(>::>::>, fixed=T.untyped) @ Loc {file=test/testdata/rewriter/struct.rb start=33:5 end=33:25} - method >::># () @ Loc {file=test/testdata/rewriter/struct.rb start=33:21 end=33:24} - argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - method >::># (foo, ) @ Loc {file=test/testdata/rewriter/struct.rb start=33:21 end=33:24} - argument foo<> @ Loc {file=test/testdata/rewriter/struct.rb start=33:21 end=33:24} - argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - method >::># : private (foo, ) -> Sorbet::Private::Static::Void @ Loc {file=test/testdata/rewriter/struct.rb start=33:5 end=33:25} + class >::> $1>> < > () @ Loc {file=test/testdata/rewriter/struct.rb start=33:5 end=33:25} + type-member(=) >::> $1>>::> -> LambdaParam(>::> $1>>::>, fixed=T.untyped) @ Loc {file=test/testdata/rewriter/struct.rb start=33:5 end=33:25} + method >::> $1>># () -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=33:21 end=33:24} + argument -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} + method >::> $1>># (foo, ) -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=33:21 end=33:24} + argument foo<> -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=33:21 end=33:24} + argument -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} + method >::> $1>># : private (foo, ) -> Sorbet::Private::Static::Void @ Loc {file=test/testdata/rewriter/struct.rb start=33:5 end=33:25} argument foo -> BasicObject @ Loc {file=test/testdata/rewriter/struct.rb start=33:21 end=33:24} argument -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - class >::> $1>[>>] < > $1> () @ Loc {file=test/testdata/rewriter/struct.rb start=33:5 end=33:25} + class >::> < >::> $1>> () @ Loc {file=test/testdata/rewriter/struct.rb start=33:5 end=33:25} + type-member(=) >::>::> -> LambdaParam(>::>::>, fixed=T.untyped) @ Loc {file=test/testdata/rewriter/struct.rb start=33:5 end=33:25} + class >::> $1>> $1>[>>] < > $1> () @ Loc {file=test/testdata/rewriter/struct.rb start=33:5 end=33:25} + type-member(+) >::> $1>> $1>::>> -> LambdaParam(>::> $1>> $1>::>>, lower=T.noreturn, upper=AppliedType { klass = >::> $1>> targs = [ > = T.untyped ] }) @ Loc {file=test/testdata/rewriter/struct.rb start=33:5 end=33:25} + method >::> $1>> $1>#> () @ Loc {file=test/testdata/rewriter/struct.rb start=33:5 end=33:25} + argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} + class >::> $1>[>>] < >::> $1>> $1> () @ Loc {file=test/testdata/rewriter/struct.rb start=33:5 end=33:25} type-member(+) >::> $1>::>> -> LambdaParam(>::> $1>::>>, lower=T.noreturn, upper=AppliedType { klass = >::> targs = [ > = T.untyped ] }) @ Loc {file=test/testdata/rewriter/struct.rb start=33:5 end=33:25} method >::> $1>#> () @ Loc {file=test/testdata/rewriter/struct.rb start=33:5 end=33:25} argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - class >::> < > () @ Loc {file=test/testdata/rewriter/struct.rb start=34:5 end=34:25} - type-member(=) >::>::> -> LambdaParam(>::>::>, fixed=T.untyped) @ Loc {file=test/testdata/rewriter/struct.rb start=34:5 end=34:25} - method >::># () @ Loc {file=test/testdata/rewriter/struct.rb start=34:21 end=34:24} - argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - method >::># (foo, ) @ Loc {file=test/testdata/rewriter/struct.rb start=34:21 end=34:24} - argument foo<> @ Loc {file=test/testdata/rewriter/struct.rb start=34:21 end=34:24} - argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - method >::># : private (foo, ) -> Sorbet::Private::Static::Void @ Loc {file=test/testdata/rewriter/struct.rb start=34:5 end=34:25} + class >::> $1>> < > () @ Loc {file=test/testdata/rewriter/struct.rb start=34:5 end=34:25} + type-member(=) >::> $1>>::> -> LambdaParam(>::> $1>>::>, fixed=T.untyped) @ Loc {file=test/testdata/rewriter/struct.rb start=34:5 end=34:25} + method >::> $1>># () -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=34:21 end=34:24} + argument -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} + method >::> $1>># (foo, ) -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=34:21 end=34:24} + argument foo<> -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=34:21 end=34:24} + argument -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} + method >::> $1>># : private (foo, ) -> Sorbet::Private::Static::Void @ Loc {file=test/testdata/rewriter/struct.rb start=34:5 end=34:25} argument foo -> BasicObject @ Loc {file=test/testdata/rewriter/struct.rb start=34:21 end=34:24} argument -> T.untyped @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} - class >::> $1>[>>] < > $1> () @ Loc {file=test/testdata/rewriter/struct.rb start=34:5 end=34:25} + class >::> < >::> $1>> () @ Loc {file=test/testdata/rewriter/struct.rb start=34:5 end=34:25} + type-member(=) >::>::> -> LambdaParam(>::>::>, fixed=T.untyped) @ Loc {file=test/testdata/rewriter/struct.rb start=34:5 end=34:25} + class >::> $1>> $1>[>>] < > $1> () @ Loc {file=test/testdata/rewriter/struct.rb start=34:5 end=34:25} + type-member(+) >::> $1>> $1>::>> -> LambdaParam(>::> $1>> $1>::>>, lower=T.noreturn, upper=AppliedType { klass = >::> $1>> targs = [ > = T.untyped ] }) @ Loc {file=test/testdata/rewriter/struct.rb start=34:5 end=34:25} + method >::> $1>> $1>#> () @ Loc {file=test/testdata/rewriter/struct.rb start=34:5 end=34:25} + argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} + class >::> $1>[>>] < >::> $1>> $1> () @ Loc {file=test/testdata/rewriter/struct.rb start=34:5 end=34:25} type-member(+) >::> $1>::>> -> LambdaParam(>::> $1>::>>, lower=T.noreturn, upper=AppliedType { klass = >::> targs = [ > = T.untyped ] }) @ Loc {file=test/testdata/rewriter/struct.rb start=34:5 end=34:25} method >::> $1>#> () @ Loc {file=test/testdata/rewriter/struct.rb start=34:5 end=34:25} argument @ Loc {file=test/testdata/rewriter/struct.rb start=??? end=???} diff --git a/test/testdata/rewriter/struct_self.rb b/test/testdata/rewriter/struct_self.rb index 854e41b3df..e2f8548e09 100644 --- a/test/testdata/rewriter/struct_self.rb +++ b/test/testdata/rewriter/struct_self.rb @@ -5,4 +5,4 @@ class Foo a = Foo::Bar.new(1) a.x = 2 -a.y = 3 # error: Method `y=` does not exist on `Foo::Bar` +a.y = 3 # error: Setter method `y=` does not exist on `Foo::Bar` diff --git a/test/testdata/rewriter/struct_super.rb b/test/testdata/rewriter/struct_super.rb new file mode 100644 index 0000000000..5c9f0442ac --- /dev/null +++ b/test/testdata/rewriter/struct_super.rb @@ -0,0 +1,21 @@ +# typed: true + +CompatibleOverride = Struct.new(:foo) do + def initialize(foo) + super + end +end + +MismatchKeywordInit = Struct.new(:foo, keyword_init: true) do + def initialize(foo) + super + end +end + +DifferentArity = Struct.new(:a, :b, keyword_init: true) do + def initialize(a) + super(a: a, b: "b") + end +end + +foo = DifferentArity.new("a") diff --git a/test/testdata/rewriter/struct_super.rb.rewrite-tree.exp b/test/testdata/rewriter/struct_super.rb.rewrite-tree.exp new file mode 100644 index 0000000000..6db8fb1617 --- /dev/null +++ b/test/testdata/rewriter/struct_super.rb.rewrite-tree.exp @@ -0,0 +1,167 @@ +class <>> < (::) + class ::<>> < (::::) + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .returns(::T.untyped()) + end + + def foo<>(&) + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .params(:foo, ::T.untyped()).returns(::T.untyped()) + end + + def foo=<>(foo, &) + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .params(:foo, ::BasicObject).void() + end + + def initialize<>(foo = nil, &) + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") + end + + + + + + :: = .type_member() do || + {:fixed => ::T.untyped()} + end + + + end + + class ::<>> < (::) + def initialize<>(foo, &) + .(ZSuperArgs) + end + + :: = .type_member() do || + {:fixed => ::T.untyped()} + end + + + end + + class ::<>> < (::::) + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .returns(::T.untyped()) + end + + def foo<>(&) + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .params(:foo, ::T.untyped()).returns(::T.untyped()) + end + + def foo=<>(foo, &) + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .params(:foo, ::BasicObject).void() + end + + def initialize<>(foo: = nil, &) + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") + end + + + + + + :: = .type_member() do || + {:fixed => ::T.untyped()} + end + + + end + + class ::<>> < (::) + def initialize<>(foo, &) + .(ZSuperArgs) + end + + :: = .type_member() do || + {:fixed => ::T.untyped()} + end + + + end + + class ::<>> < (::::) + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .returns(::T.untyped()) + end + + def a<>(&) + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .params(:a, ::T.untyped()).returns(::T.untyped()) + end + + def a=<>(a, &) + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .returns(::T.untyped()) + end + + def b<>(&) + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .params(:b, ::T.untyped()).returns(::T.untyped()) + end + + def b=<>(b, &) + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .params(:a, ::BasicObject, :b, ::BasicObject).void() + end + + def initialize<>(a: = nil, b: = nil, &) + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") + end + + + + + + + + + + :: = .type_member() do || + {:fixed => ::T.untyped()} + end + + + end + + class ::<>> < (::) + def initialize<>(a, &) + .(:a, a, :b, "b") + end + + :: = .type_member() do || + {:fixed => ::T.untyped()} + end + + + end + + foo = ::.new("a") +end diff --git a/test/testdata/rewriter/struct_super_strict.rb b/test/testdata/rewriter/struct_super_strict.rb new file mode 100644 index 0000000000..5b9d7b02bc --- /dev/null +++ b/test/testdata/rewriter/struct_super_strict.rb @@ -0,0 +1,23 @@ +# typed: strict +# highlight-untyped-values: true +class Module; include T::Sig; end + +WithoutAnyOverride = Struct.new(:foo) do + # We did not always generate `T.untyped` signatures for Struct members, which + # prevented using `Struct.new` in `# typed: strict` files unless you went and + # defined an override for every method. +end + +ExplicitOverride = Struct.new(:foo) do + sig {returns(String)} + def foo + super + # ^^^^^ untyped: Value returned from method is `T.untyped` + end + + sig {params(foo: String).returns(String)} + def foo=(foo); + super + # ^^^^^ untyped: Value returned from method is `T.untyped` + end +end diff --git a/test/testdata/rewriter/suggest_initialize_sig.rb b/test/testdata/rewriter/suggest_initialize_sig.rb new file mode 100644 index 0000000000..2f59411545 --- /dev/null +++ b/test/testdata/rewriter/suggest_initialize_sig.rb @@ -0,0 +1,111 @@ +# typed: true + +class SimpleReturns + extend T::Sig + + sig {params(x: Integer).returns(Integer).on_failure(:soft, notify: 'sorbet')} + # ^^^^^^^ error: The initialize method should always return void + def initialize(x) + @x = x + end +end + +class SimpleMultiLineReturns + extend T::Sig + + sig do + params( + x: Integer + ) + .returns(Integer) + #^^^^^^^ error: The initialize method should always return void + end + def initialize(x) + @x = x + end +end + +class MultiLineReturnsWithCombinators + extend T::Sig + + sig do + params( + x: T::Array[T.any(String, T::Enum)] + ) + .returns(T::Array[T.any(String, T::Enum)]) + #^^^^^^^ error: The initialize method should always return void + end + def initialize(x) + @x = x + end +end + +class SingleLineReturnsWithCombinators + extend T::Sig + + sig {params(x: T.nilable(Integer)).returns(T.nilable(Integer)).on_failure(:soft, notify: 'sorbet')} + # ^^^^^^^ error: The initialize method should always return void + def initialize(x) + @x = x + end +end + +class SingleLineNoAfterStatements + extend T::Sig + + sig {params(x: T.any(Integer, String)).returns(T.any(Integer, String))} + # ^^^^^^^ error: The initialize method should always return void + def initialize(x) + @x = x + end +end + +class ClassMethodInitializeIsIgnored + extend T::Sig + + sig {returns(ClassMethodInitializeIsIgnored)} + def self.initialize + new + end +end + +class LineBreakAfterReturns + extend T::Sig + + sig do + params( + path: String, + key: String + ) + .returns(T.self_type) + #^^^^^^^ error: The initialize method should always return void + .checked(:tests) + end + def initialize(path, key) + self + end +end + +class LineBreakOnlyAtEnd + extend T::Sig + + sig do + params( + path: String, + key: String + ) + .returns(T.self_type).checked(:tests) + #^^^^^^^ error: The initialize method should always return void + end + def initialize(path, key) + self + end +end + +class TProcReturnsInInitializeSig + extend T::Sig + sig { params(blk: T.proc.returns(T.untyped)).returns(T.anything) } + # ^^^^^^^ error: The initialize method should always return void + def initialize(&blk) + end +end diff --git a/test/testdata/rewriter/suggest_initialize_sig.rb.autocorrects.exp b/test/testdata/rewriter/suggest_initialize_sig.rb.autocorrects.exp new file mode 100644 index 0000000000..70f2a68741 --- /dev/null +++ b/test/testdata/rewriter/suggest_initialize_sig.rb.autocorrects.exp @@ -0,0 +1,113 @@ +# -- test/testdata/rewriter/suggest_initialize_sig.rb -- +# typed: true + +class SimpleReturns + extend T::Sig + + sig {params(x: Integer).void.on_failure(:soft, notify: 'sorbet')} + # ^^^^^^^ error: The initialize method should always return void + def initialize(x) + @x = x + end +end + +class SimpleMultiLineReturns + extend T::Sig + + sig do + params( + x: Integer + ) + .void + #^^^^^^^ error: The initialize method should always return void + end + def initialize(x) + @x = x + end +end + +class MultiLineReturnsWithCombinators + extend T::Sig + + sig do + params( + x: T::Array[T.any(String, T::Enum)] + ) + .void + #^^^^^^^ error: The initialize method should always return void + end + def initialize(x) + @x = x + end +end + +class SingleLineReturnsWithCombinators + extend T::Sig + + sig {params(x: T.nilable(Integer)).void.on_failure(:soft, notify: 'sorbet')} + # ^^^^^^^ error: The initialize method should always return void + def initialize(x) + @x = x + end +end + +class SingleLineNoAfterStatements + extend T::Sig + + sig {params(x: T.any(Integer, String)).void} + # ^^^^^^^ error: The initialize method should always return void + def initialize(x) + @x = x + end +end + +class ClassMethodInitializeIsIgnored + extend T::Sig + + sig {returns(ClassMethodInitializeIsIgnored)} + def self.initialize + new + end +end + +class LineBreakAfterReturns + extend T::Sig + + sig do + params( + path: String, + key: String + ) + .void + #^^^^^^^ error: The initialize method should always return void + .checked(:tests) + end + def initialize(path, key) + self + end +end + +class LineBreakOnlyAtEnd + extend T::Sig + + sig do + params( + path: String, + key: String + ) + .void.checked(:tests) + #^^^^^^^ error: The initialize method should always return void + end + def initialize(path, key) + self + end +end + +class TProcReturnsInInitializeSig + extend T::Sig + sig { params(blk: T.proc.returns(T.untyped)).void } + # ^^^^^^^ error: The initialize method should always return void + def initialize(&blk) + end +end +# ------------------------------ diff --git a/test/testdata/rewriter/t_enum.rb b/test/testdata/rewriter/t_enum.rb index 0d8864159d..f50a94b064 100644 --- a/test/testdata/rewriter/t_enum.rb +++ b/test/testdata/rewriter/t_enum.rb @@ -1,5 +1,7 @@ # typed: strict +extend T::Sig + class MyEnum < T::Enum enums do X = new @@ -21,3 +23,66 @@ class NotAnEnum T.reveal_type(NotAnEnum::X) # error: Revealed type: `T.untyped` T.reveal_type(NotAnEnum::Y) # error: Revealed type: `NotAnEnum` + +class EnumWithStrings < T::Enum + enums do + X = new + Y = new("y") + Z = new + end +end + +T.reveal_type(EnumWithStrings::X.serialize) # error: Revealed type: `String` + +class EnumWithSymbols < T::Enum + enums do + X = new(:x) + Y = new(:y) + Z = new(:z) + end +end + +T.reveal_type(EnumWithSymbols::X.serialize) # error: Revealed type: `Symbol` + +class EnumWithNonLiterals < T::Enum + enums do + X = new(:x.to_s) + Y = new("y") + Z = new(:z) + end +end + +T.reveal_type(EnumWithNonLiterals::X.serialize) # error: Revealed type: `T.untyped` + +class EnumWithKwargs < T::Enum + enums do + X = new(a: 1, b: 2) + Y = new(a: 3, b: 4) + end +end + +T.reveal_type(EnumWithKwargs::X.serialize) # error: Revealed type: `T.untyped` + +class EnumWithNil < T::Enum + enums do + X = new(nil) + end +end + +T.reveal_type(EnumWithNil::X.serialize) # error: Revealed type: `NilClass` + + +class EnumWithPrivate < T::Enum + extend T::Sig + + enums do + X = new(nil) + end + + private + + sig { void } + def unrelated_method; end +end + +EnumWithPrivate::X.serialize diff --git a/test/testdata/rewriter/t_enum_kwargs.rb b/test/testdata/rewriter/t_enum_kwargs.rb index 95d0de32a8..547fcc1c3c 100644 --- a/test/testdata/rewriter/t_enum_kwargs.rb +++ b/test/testdata/rewriter/t_enum_kwargs.rb @@ -14,7 +14,7 @@ def initialize(a, b="hi", c:, d: 20) D = new(10, "bar", c: 20, d: 30) E = new(10, "bar", c: 20, d: 30, f: "error") - # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Unrecognized keyword argument + # ^^^^^^^^^^ error: Unrecognized keyword argument F = new(1, "bar", 3, 4, c: 20) # ^^^^ error: Too many positional arguments end diff --git a/test/testdata/rewriter/t_enum_snapshot.rb.cfg-text.exp b/test/testdata/rewriter/t_enum_snapshot.rb.cfg-text.exp index 029ac65cb4..d8014d4d70 100644 --- a/test/testdata/rewriter/t_enum_snapshot.rb.cfg-text.exp +++ b/test/testdata/rewriter/t_enum_snapshot.rb.cfg-text.exp @@ -1,31 +1,7 @@ method ::># { -bb0[rubyRegionId=0, firstDead=26](): +bb0[rubyRegionId=0, firstDead=2](): : T.class_of() = cast(: NilClass, T.class_of()); - $7: T.class_of(Sorbet::Private::Static) = alias - $9: T.class_of(MyEnum) = alias - $5: Sorbet::Private::Static::Void = $7: T.class_of(Sorbet::Private::Static).keep_for_ide($9: T.class_of(MyEnum)) - $12: T.class_of(Sorbet::Private::Static) = alias - $14: T.class_of(T::Enum) = alias - $16: T.class_of(T) = alias - $10: Sorbet::Private::Static::Void = $12: T.class_of(Sorbet::Private::Static).keep_for_ide($14: T.class_of(T::Enum)) - $21: T.class_of(Sorbet::Private::Static) = alias - $23: T.class_of(NotAnEnum) = alias - $19: Sorbet::Private::Static::Void = $21: T.class_of(Sorbet::Private::Static).keep_for_ide($23: T.class_of(NotAnEnum)) - $28: T.class_of(Sorbet::Private::Static) = alias - $30: T.class_of(EnumsDoEnum) = alias - $26: Sorbet::Private::Static::Void = $28: T.class_of(Sorbet::Private::Static).keep_for_ide($30: T.class_of(EnumsDoEnum)) - $33: T.class_of(Sorbet::Private::Static) = alias - $35: T.class_of(T::Enum) = alias - $37: T.class_of(T) = alias - $31: Sorbet::Private::Static::Void = $33: T.class_of(Sorbet::Private::Static).keep_for_ide($35: T.class_of(T::Enum)) - $42: T.class_of(Sorbet::Private::Static) = alias - $44: T.class_of(BadConsts) = alias - $40: Sorbet::Private::Static::Void = $42: T.class_of(Sorbet::Private::Static).keep_for_ide($44: T.class_of(BadConsts)) - $47: T.class_of(Sorbet::Private::Static) = alias - $49: T.class_of(T::Enum) = alias - $51: T.class_of(T) = alias - $45: Sorbet::Private::Static::Void = $47: T.class_of(Sorbet::Private::Static).keep_for_ide($49: T.class_of(T::Enum)) : T.noreturn = return $2: NilClass -> bb1 @@ -36,85 +12,114 @@ bb1[rubyRegionId=0, firstDead=-1](): } +method ::MyEnum#serialize { + +bb0[rubyRegionId=0, firstDead=4](): + : MyEnum = cast(: NilClass, MyEnum); + $4: T.class_of(Kernel) = alias + $5: String("Sorbet rewriter pass partially unimplemented") = "Sorbet rewriter pass partially unimplemented" + $2: T.noreturn = $4: T.class_of(Kernel).raise($5: String("Sorbet rewriter pass partially unimplemented")) + = return $2 + -> bb1 + +# backedges +# - bb0(rubyRegionId=0) +bb1[rubyRegionId=0, firstDead=-1](): + -> bb1 + +} + method ::# { bb0[rubyRegionId=0, firstDead=-1](): - $29: MyEnum::X = alias - $48: MyEnum::Y = alias - $68: MyEnum::Z = alias + $30: MyEnum::X = alias + $38: MyEnum::Y = alias + $47: MyEnum::Z = alias : T.class_of(MyEnum) = cast(: NilClass, T.class_of(MyEnum)); - $6: T.class_of(T::Helpers) = alias - $3: T.class_of(MyEnum) = : T.class_of(MyEnum).extend($6: T.class_of(T::Helpers)) - $7: Sorbet::Private::Static::Void = : T.class_of(MyEnum).abstract!() - $9: Sorbet::Private::Static::Void = : T.class_of(MyEnum).sealed!() - $13: Sorbet::Private::Static::Void = : T.class_of(MyEnum).enums() - $14: T.class_of(MyEnum) = + $5: T.class_of(Sorbet::Private::Static) = alias + $7: T.class_of(T::Sig::WithoutRuntime) = alias + $8: Sorbet::Private::Static::Void = $5: T.class_of(Sorbet::Private::Static).sig($7: T.class_of(T::Sig::WithoutRuntime)) + $9: T.class_of(MyEnum) = -> bb2 # backedges -# - bb3(rubyRegionId=0) +# - bb7(rubyRegionId=0) bb1[rubyRegionId=0, firstDead=-1](): -> bb1 # backedges # - bb0(rubyRegionId=0) # - bb5(rubyRegionId=1) -bb2[rubyRegionId=1, firstDead=-1](: T.class_of(MyEnum), $13: Sorbet::Private::Static::Void, $14: T.class_of(MyEnum), $29: MyEnum::X, $48: MyEnum::Y, $68: MyEnum::Z): +bb2[rubyRegionId=1, firstDead=-1](: T.class_of(MyEnum), $8: Sorbet::Private::Static::Void, $9: T.class_of(MyEnum), $30: MyEnum::X, $38: MyEnum::Y, $47: MyEnum::Z): # outerLoops: 1 -> (NilClass ? bb5 : bb3) # backedges # - bb2(rubyRegionId=1) -bb3[rubyRegionId=0, firstDead=2]($13: Sorbet::Private::Static::Void, $14: T.class_of(MyEnum)): - $11: Sorbet::Private::Static::Void = Solve<$13, enums> +bb3[rubyRegionId=0, firstDead=-1]($8: Sorbet::Private::Static::Void, $9: T.class_of(MyEnum), $30: MyEnum::X, $38: MyEnum::Y, $47: MyEnum::Z): + $3: Sorbet::Private::Static::Void = Solve<$8, sig> + : T.class_of(MyEnum) = $9 + $18: T.class_of(T::Helpers) = alias + $15: T.class_of(MyEnum) = : T.class_of(MyEnum).extend($18: T.class_of(T::Helpers)) + $19: Sorbet::Private::Static::Void = : T.class_of(MyEnum).abstract!() + $21: Sorbet::Private::Static::Void = : T.class_of(MyEnum).sealed!() + $25: Sorbet::Private::Static::Void = : T.class_of(MyEnum).enums() + $26: T.class_of(MyEnum) = + -> bb6 + +# backedges +# - bb2(rubyRegionId=1) +bb5[rubyRegionId=1, firstDead=4](: T.class_of(MyEnum), $8: Sorbet::Private::Static::Void, $9: T.class_of(MyEnum), $30: MyEnum::X, $38: MyEnum::Y, $47: MyEnum::Z): + # outerLoops: 1 + : T::Private::Methods::DeclBuilder = loadSelf(sig) + $13: T.class_of(String) = alias + $10: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.returns($13: T.class_of(String)) + $14: T.noreturn = blockreturn $10: T::Private::Methods::DeclBuilder + -> bb2 + +# backedges +# - bb3(rubyRegionId=0) +# - bb9(rubyRegionId=2) +bb6[rubyRegionId=2, firstDead=-1](: T.class_of(MyEnum), $25: Sorbet::Private::Static::Void, $26: T.class_of(MyEnum), $30: MyEnum::X, $38: MyEnum::Y, $47: MyEnum::Z): + # outerLoops: 1 + -> (NilClass ? bb9 : bb7) + +# backedges +# - bb6(rubyRegionId=2) +bb7[rubyRegionId=0, firstDead=4]($25: Sorbet::Private::Static::Void, $26: T.class_of(MyEnum)): + $23: Sorbet::Private::Static::Void = Solve<$25, enums> + : T.class_of(MyEnum) = $26 + $54: T.class_of(MyEnum) = : T.class_of(MyEnum).public() : T.noreturn = return $2: NilClass -> bb1 # backedges -# - bb2(rubyRegionId=1) -bb5[rubyRegionId=1, firstDead=40](: T.class_of(MyEnum), $13: Sorbet::Private::Static::Void, $14: T.class_of(MyEnum), $29: MyEnum::X, $48: MyEnum::Y, $68: MyEnum::Z): +# - bb6(rubyRegionId=2) +bb9[rubyRegionId=2, firstDead=22](: T.class_of(MyEnum), $25: Sorbet::Private::Static::Void, $26: T.class_of(MyEnum), $30: MyEnum::X, $38: MyEnum::Y, $47: MyEnum::Z): # outerLoops: 1 : T.class_of(MyEnum) = loadSelf(enums) - $20: T.class_of(Sorbet::Private::Static) = alias - $22: T.class_of(MyEnum::X) = alias - $18: Sorbet::Private::Static::Void = $20: T.class_of(Sorbet::Private::Static).keep_for_ide($22: T.class_of(MyEnum::X)) - $25: T.class_of(Sorbet::Private::Static) = alias - $27: T.class_of(MyEnum) = alias - $23: Sorbet::Private::Static::Void = $25: T.class_of(Sorbet::Private::Static).keep_for_ide($27: T.class_of(MyEnum)) - $31: T.class_of(MyEnum::X) = alias - keep_for_ide$30: T.class_of(MyEnum::X) = $31 - keep_for_ide$30: T.untyped = keep_for_ide$30 - $34: T.class_of(MyEnum::X) = alias - $32: MyEnum::X = $34: T.class_of(MyEnum::X).new() - $29: MyEnum::X = $32 - $39: T.class_of(Sorbet::Private::Static) = alias - $41: T.class_of(MyEnum::Y) = alias - $37: Sorbet::Private::Static::Void = $39: T.class_of(Sorbet::Private::Static).keep_for_ide($41: T.class_of(MyEnum::Y)) - $44: T.class_of(Sorbet::Private::Static) = alias - $46: T.class_of(MyEnum) = alias - $42: Sorbet::Private::Static::Void = $44: T.class_of(Sorbet::Private::Static).keep_for_ide($46: T.class_of(MyEnum)) - $50: T.class_of(MyEnum::Y) = alias - keep_for_ide$49: T.class_of(MyEnum::Y) = $50 - keep_for_ide$49: T.untyped = keep_for_ide$49 - $53: T.class_of(MyEnum::Y) = alias - $54: String("y") = "y" - $51: MyEnum::Y = $53: T.class_of(MyEnum::Y).new($54: String("y")) - $48: MyEnum::Y = $51 - $59: T.class_of(Sorbet::Private::Static) = alias - $61: T.class_of(MyEnum::Z) = alias - $57: Sorbet::Private::Static::Void = $59: T.class_of(Sorbet::Private::Static).keep_for_ide($61: T.class_of(MyEnum::Z)) - $64: T.class_of(Sorbet::Private::Static) = alias - $66: T.class_of(MyEnum) = alias - $62: Sorbet::Private::Static::Void = $64: T.class_of(Sorbet::Private::Static).keep_for_ide($66: T.class_of(MyEnum)) - $70: T.class_of(MyEnum::Z) = alias - keep_for_ide$69: T.class_of(MyEnum::Z) = $70 - keep_for_ide$69: T.untyped = keep_for_ide$69 - $73: T.class_of(MyEnum::Z) = alias - $71: MyEnum::Z = $73: T.class_of(MyEnum::Z).new() - $68: MyEnum::Z = $71 - $15: NilClass = nil - $74: T.noreturn = blockreturn $15: NilClass - -> bb2 + $32: T.class_of(MyEnum::X) = alias + keep_for_ide$31: T.class_of(MyEnum::X) = $32 + keep_for_ide$31: T.untyped = keep_for_ide$31 + $35: T.class_of(MyEnum::X) = alias + $33: MyEnum::X = $35: T.class_of(MyEnum::X).new() + $30: MyEnum::X = $33 + $40: T.class_of(MyEnum::Y) = alias + keep_for_ide$39: T.class_of(MyEnum::Y) = $40 + keep_for_ide$39: T.untyped = keep_for_ide$39 + $43: T.class_of(MyEnum::Y) = alias + $44: String("y") = "y" + $41: MyEnum::Y = $43: T.class_of(MyEnum::Y).new($44: String("y")) + $38: MyEnum::Y = $41 + $49: T.class_of(MyEnum::Z) = alias + keep_for_ide$48: T.class_of(MyEnum::Z) = $49 + keep_for_ide$48: T.untyped = keep_for_ide$48 + $52: T.class_of(MyEnum::Z) = alias + $50: MyEnum::Z = $52: T.class_of(MyEnum::Z).new() + $47: MyEnum::Z = $50 + $27: NilClass = nil + $53: T.noreturn = blockreturn $27: NilClass + -> bb6 } @@ -221,85 +226,114 @@ bb1[rubyRegionId=0, firstDead=-1](): } +method ::EnumsDoEnum#serialize { + +bb0[rubyRegionId=0, firstDead=4](): + : EnumsDoEnum = cast(: NilClass, EnumsDoEnum); + $4: T.class_of(Kernel) = alias + $5: String("Sorbet rewriter pass partially unimplemented") = "Sorbet rewriter pass partially unimplemented" + $2: T.noreturn = $4: T.class_of(Kernel).raise($5: String("Sorbet rewriter pass partially unimplemented")) + = return $2 + -> bb1 + +# backedges +# - bb0(rubyRegionId=0) +bb1[rubyRegionId=0, firstDead=-1](): + -> bb1 + +} + method ::# { bb0[rubyRegionId=0, firstDead=-1](): - $29: EnumsDoEnum::X = alias - $48: EnumsDoEnum::Y = alias - $68: EnumsDoEnum::Z = alias + $30: EnumsDoEnum::X = alias + $38: EnumsDoEnum::Y = alias + $47: EnumsDoEnum::Z = alias : T.class_of(EnumsDoEnum) = cast(: NilClass, T.class_of(EnumsDoEnum)); - $6: T.class_of(T::Helpers) = alias - $3: T.class_of(EnumsDoEnum) = : T.class_of(EnumsDoEnum).extend($6: T.class_of(T::Helpers)) - $7: Sorbet::Private::Static::Void = : T.class_of(EnumsDoEnum).abstract!() - $9: Sorbet::Private::Static::Void = : T.class_of(EnumsDoEnum).sealed!() - $13: Sorbet::Private::Static::Void = : T.class_of(EnumsDoEnum).enums() - $14: T.class_of(EnumsDoEnum) = + $5: T.class_of(Sorbet::Private::Static) = alias + $7: T.class_of(T::Sig::WithoutRuntime) = alias + $8: Sorbet::Private::Static::Void = $5: T.class_of(Sorbet::Private::Static).sig($7: T.class_of(T::Sig::WithoutRuntime)) + $9: T.class_of(EnumsDoEnum) = -> bb2 # backedges -# - bb3(rubyRegionId=0) +# - bb7(rubyRegionId=0) bb1[rubyRegionId=0, firstDead=-1](): -> bb1 # backedges # - bb0(rubyRegionId=0) # - bb5(rubyRegionId=1) -bb2[rubyRegionId=1, firstDead=-1](: T.class_of(EnumsDoEnum), $13: Sorbet::Private::Static::Void, $14: T.class_of(EnumsDoEnum), $29: EnumsDoEnum::X, $48: EnumsDoEnum::Y, $68: EnumsDoEnum::Z): +bb2[rubyRegionId=1, firstDead=-1](: T.class_of(EnumsDoEnum), $8: Sorbet::Private::Static::Void, $9: T.class_of(EnumsDoEnum), $30: EnumsDoEnum::X, $38: EnumsDoEnum::Y, $47: EnumsDoEnum::Z): # outerLoops: 1 -> (NilClass ? bb5 : bb3) # backedges # - bb2(rubyRegionId=1) -bb3[rubyRegionId=0, firstDead=2]($13: Sorbet::Private::Static::Void, $14: T.class_of(EnumsDoEnum)): - $11: Sorbet::Private::Static::Void = Solve<$13, enums> +bb3[rubyRegionId=0, firstDead=-1]($8: Sorbet::Private::Static::Void, $9: T.class_of(EnumsDoEnum), $30: EnumsDoEnum::X, $38: EnumsDoEnum::Y, $47: EnumsDoEnum::Z): + $3: Sorbet::Private::Static::Void = Solve<$8, sig> + : T.class_of(EnumsDoEnum) = $9 + $18: T.class_of(T::Helpers) = alias + $15: T.class_of(EnumsDoEnum) = : T.class_of(EnumsDoEnum).extend($18: T.class_of(T::Helpers)) + $19: Sorbet::Private::Static::Void = : T.class_of(EnumsDoEnum).abstract!() + $21: Sorbet::Private::Static::Void = : T.class_of(EnumsDoEnum).sealed!() + $25: Sorbet::Private::Static::Void = : T.class_of(EnumsDoEnum).enums() + $26: T.class_of(EnumsDoEnum) = + -> bb6 + +# backedges +# - bb2(rubyRegionId=1) +bb5[rubyRegionId=1, firstDead=4](: T.class_of(EnumsDoEnum), $8: Sorbet::Private::Static::Void, $9: T.class_of(EnumsDoEnum), $30: EnumsDoEnum::X, $38: EnumsDoEnum::Y, $47: EnumsDoEnum::Z): + # outerLoops: 1 + : T::Private::Methods::DeclBuilder = loadSelf(sig) + $13: T.class_of(String) = alias + $10: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.returns($13: T.class_of(String)) + $14: T.noreturn = blockreturn $10: T::Private::Methods::DeclBuilder + -> bb2 + +# backedges +# - bb3(rubyRegionId=0) +# - bb9(rubyRegionId=2) +bb6[rubyRegionId=2, firstDead=-1](: T.class_of(EnumsDoEnum), $25: Sorbet::Private::Static::Void, $26: T.class_of(EnumsDoEnum), $30: EnumsDoEnum::X, $38: EnumsDoEnum::Y, $47: EnumsDoEnum::Z): + # outerLoops: 1 + -> (NilClass ? bb9 : bb7) + +# backedges +# - bb6(rubyRegionId=2) +bb7[rubyRegionId=0, firstDead=4]($25: Sorbet::Private::Static::Void, $26: T.class_of(EnumsDoEnum)): + $23: Sorbet::Private::Static::Void = Solve<$25, enums> + : T.class_of(EnumsDoEnum) = $26 + $55: T.class_of(EnumsDoEnum) = : T.class_of(EnumsDoEnum).public() : T.noreturn = return $2: NilClass -> bb1 # backedges -# - bb2(rubyRegionId=1) -bb5[rubyRegionId=1, firstDead=40](: T.class_of(EnumsDoEnum), $13: Sorbet::Private::Static::Void, $14: T.class_of(EnumsDoEnum), $29: EnumsDoEnum::X, $48: EnumsDoEnum::Y, $68: EnumsDoEnum::Z): +# - bb6(rubyRegionId=2) +bb9[rubyRegionId=2, firstDead=22](: T.class_of(EnumsDoEnum), $25: Sorbet::Private::Static::Void, $26: T.class_of(EnumsDoEnum), $30: EnumsDoEnum::X, $38: EnumsDoEnum::Y, $47: EnumsDoEnum::Z): # outerLoops: 1 : T.class_of(EnumsDoEnum) = loadSelf(enums) - $20: T.class_of(Sorbet::Private::Static) = alias - $22: T.class_of(EnumsDoEnum::X) = alias - $18: Sorbet::Private::Static::Void = $20: T.class_of(Sorbet::Private::Static).keep_for_ide($22: T.class_of(EnumsDoEnum::X)) - $25: T.class_of(Sorbet::Private::Static) = alias - $27: T.class_of(EnumsDoEnum) = alias - $23: Sorbet::Private::Static::Void = $25: T.class_of(Sorbet::Private::Static).keep_for_ide($27: T.class_of(EnumsDoEnum)) - $31: T.class_of(EnumsDoEnum::X) = alias - keep_for_ide$30: T.class_of(EnumsDoEnum::X) = $31 - keep_for_ide$30: T.untyped = keep_for_ide$30 - $34: T.class_of(EnumsDoEnum::X) = alias - $32: EnumsDoEnum::X = $34: T.class_of(EnumsDoEnum::X).new() - $29: EnumsDoEnum::X = $32 - $39: T.class_of(Sorbet::Private::Static) = alias - $41: T.class_of(EnumsDoEnum::Y) = alias - $37: Sorbet::Private::Static::Void = $39: T.class_of(Sorbet::Private::Static).keep_for_ide($41: T.class_of(EnumsDoEnum::Y)) - $44: T.class_of(Sorbet::Private::Static) = alias - $46: T.class_of(EnumsDoEnum) = alias - $42: Sorbet::Private::Static::Void = $44: T.class_of(Sorbet::Private::Static).keep_for_ide($46: T.class_of(EnumsDoEnum)) - $50: T.class_of(EnumsDoEnum::Y) = alias - keep_for_ide$49: T.class_of(EnumsDoEnum::Y) = $50 - keep_for_ide$49: T.untyped = keep_for_ide$49 - $53: T.class_of(EnumsDoEnum::Y) = alias - $54: String("y") = "y" - $51: EnumsDoEnum::Y = $53: T.class_of(EnumsDoEnum::Y).new($54: String("y")) - $48: EnumsDoEnum::Y = $51 - $59: T.class_of(Sorbet::Private::Static) = alias - $61: T.class_of(EnumsDoEnum::Z) = alias - $57: Sorbet::Private::Static::Void = $59: T.class_of(Sorbet::Private::Static).keep_for_ide($61: T.class_of(EnumsDoEnum::Z)) - $64: T.class_of(Sorbet::Private::Static) = alias - $66: T.class_of(EnumsDoEnum) = alias - $62: Sorbet::Private::Static::Void = $64: T.class_of(Sorbet::Private::Static).keep_for_ide($66: T.class_of(EnumsDoEnum)) - $70: T.class_of(EnumsDoEnum::Z) = alias - keep_for_ide$69: T.class_of(EnumsDoEnum::Z) = $70 - keep_for_ide$69: T.untyped = keep_for_ide$69 - $73: T.class_of(EnumsDoEnum::Z) = alias - $71: EnumsDoEnum::Z = $73: T.class_of(EnumsDoEnum::Z).new() - $68: EnumsDoEnum::Z = $71 - $15: NilClass = nil - $74: T.noreturn = blockreturn $15: NilClass - -> bb2 + $32: T.class_of(EnumsDoEnum::X) = alias + keep_for_ide$31: T.class_of(EnumsDoEnum::X) = $32 + keep_for_ide$31: T.untyped = keep_for_ide$31 + $35: T.class_of(EnumsDoEnum::X) = alias + $33: EnumsDoEnum::X = $35: T.class_of(EnumsDoEnum::X).new() + $30: EnumsDoEnum::X = $33 + $40: T.class_of(EnumsDoEnum::Y) = alias + keep_for_ide$39: T.class_of(EnumsDoEnum::Y) = $40 + keep_for_ide$39: T.untyped = keep_for_ide$39 + $43: T.class_of(EnumsDoEnum::Y) = alias + $44: String("y") = "y" + $41: EnumsDoEnum::Y = $43: T.class_of(EnumsDoEnum::Y).new($44: String("y")) + $38: EnumsDoEnum::Y = $41 + $49: T.class_of(EnumsDoEnum::Z) = alias + keep_for_ide$48: T.class_of(EnumsDoEnum::Z) = $49 + keep_for_ide$48: T.untyped = keep_for_ide$48 + $52: T.class_of(EnumsDoEnum::Z) = alias + $50: EnumsDoEnum::Z = $52: T.class_of(EnumsDoEnum::Z).new() + $47: EnumsDoEnum::Z = $50 + $27: NilClass = nil + $53: T.noreturn = blockreturn $27: NilClass + -> bb6 } @@ -345,27 +379,61 @@ bb1[rubyRegionId=0, firstDead=-1](): } +method ::BadConsts#serialize { + +bb0[rubyRegionId=0, firstDead=4](): + : BadConsts = cast(: NilClass, BadConsts); + $4: T.class_of(Kernel) = alias + $5: String("Sorbet rewriter pass partially unimplemented") = "Sorbet rewriter pass partially unimplemented" + $2: T.noreturn = $4: T.class_of(Kernel).raise($5: String("Sorbet rewriter pass partially unimplemented")) + = return $2 + -> bb1 + +# backedges +# - bb0(rubyRegionId=0) +bb1[rubyRegionId=0, firstDead=-1](): + -> bb1 + +} + method ::# { bb0[rubyRegionId=0, firstDead=-1](): $24: BadConsts::Before = alias $31: Integer = alias - $50: BadConsts::Inside = alias - $57: Integer = alias - $72: BadConsts::After = alias - $79: Integer = alias - $81: Integer = alias + $39: BadConsts::Inside = alias + $46: Integer = alias + $49: BadConsts::After = alias + $56: Integer = alias + $58: Integer = alias : T.class_of(BadConsts) = cast(: NilClass, T.class_of(BadConsts)); - $6: T.class_of(T::Helpers) = alias - $3: T.class_of(BadConsts) = : T.class_of(BadConsts).extend($6: T.class_of(T::Helpers)) - $7: Sorbet::Private::Static::Void = : T.class_of(BadConsts).abstract!() - $9: Sorbet::Private::Static::Void = : T.class_of(BadConsts).sealed!() - $15: T.class_of(Sorbet::Private::Static) = alias - $17: T.class_of(BadConsts::Before) = alias - $13: Sorbet::Private::Static::Void = $15: T.class_of(Sorbet::Private::Static).keep_for_ide($17: T.class_of(BadConsts::Before)) - $20: T.class_of(Sorbet::Private::Static) = alias - $22: T.class_of(BadConsts) = alias - $18: Sorbet::Private::Static::Void = $20: T.class_of(Sorbet::Private::Static).keep_for_ide($22: T.class_of(BadConsts)) + $5: T.class_of(Sorbet::Private::Static) = alias + $7: T.class_of(T::Sig::WithoutRuntime) = alias + $8: Sorbet::Private::Static::Void = $5: T.class_of(Sorbet::Private::Static).sig($7: T.class_of(T::Sig::WithoutRuntime)) + $9: T.class_of(BadConsts) = + -> bb2 + +# backedges +# - bb7(rubyRegionId=0) +bb1[rubyRegionId=0, firstDead=-1](): + -> bb1 + +# backedges +# - bb0(rubyRegionId=0) +# - bb5(rubyRegionId=1) +bb2[rubyRegionId=1, firstDead=-1](: T.class_of(BadConsts), $8: Sorbet::Private::Static::Void, $9: T.class_of(BadConsts), $24: BadConsts::Before, $31: Integer, $39: BadConsts::Inside, $46: Integer, $49: BadConsts::After, $56: Integer, $58: Integer): + # outerLoops: 1 + -> (NilClass ? bb5 : bb3) + +# backedges +# - bb2(rubyRegionId=1) +bb3[rubyRegionId=0, firstDead=-1]($8: Sorbet::Private::Static::Void, $9: T.class_of(BadConsts), $24: BadConsts::Before, $31: Integer, $39: BadConsts::Inside, $46: Integer, $49: BadConsts::After, $56: Integer, $58: Integer): + $3: Sorbet::Private::Static::Void = Solve<$8, sig> + : T.class_of(BadConsts) = $9 + $18: T.class_of(T::Helpers) = alias + $15: T.class_of(BadConsts) = : T.class_of(BadConsts).extend($18: T.class_of(T::Helpers)) + $19: Sorbet::Private::Static::Void = : T.class_of(BadConsts).abstract!() + $21: Sorbet::Private::Static::Void = : T.class_of(BadConsts).sealed!() $26: T.class_of(BadConsts::Before) = alias keep_for_ide$25: T.class_of(BadConsts::Before) = $26 keep_for_ide$25: T.untyped = keep_for_ide$25 @@ -375,66 +443,61 @@ bb0[rubyRegionId=0, firstDead=-1](): $31: Integer(1) = 1 $34: Sorbet::Private::Static::Void = : T.class_of(BadConsts).enums() $35: T.class_of(BadConsts) = - -> bb2 + -> bb6 # backedges -# - bb3(rubyRegionId=0) -bb1[rubyRegionId=0, firstDead=-1](): - -> bb1 +# - bb2(rubyRegionId=1) +bb5[rubyRegionId=1, firstDead=4](: T.class_of(BadConsts), $8: Sorbet::Private::Static::Void, $9: T.class_of(BadConsts), $24: BadConsts::Before, $31: Integer, $39: BadConsts::Inside, $46: Integer, $49: BadConsts::After, $56: Integer, $58: Integer): + # outerLoops: 1 + : T::Private::Methods::DeclBuilder = loadSelf(sig) + $13: T.class_of(String) = alias + $10: T::Private::Methods::DeclBuilder = : T::Private::Methods::DeclBuilder.returns($13: T.class_of(String)) + $14: T.noreturn = blockreturn $10: T::Private::Methods::DeclBuilder + -> bb2 # backedges -# - bb0(rubyRegionId=0) -# - bb5(rubyRegionId=1) -bb2[rubyRegionId=1, firstDead=-1](: T.class_of(BadConsts), $34: Sorbet::Private::Static::Void, $35: T.class_of(BadConsts), $50: BadConsts::Inside, $57: Integer, $72: BadConsts::After, $79: Integer, $81: Integer): +# - bb3(rubyRegionId=0) +# - bb9(rubyRegionId=2) +bb6[rubyRegionId=2, firstDead=-1](: T.class_of(BadConsts), $34: Sorbet::Private::Static::Void, $35: T.class_of(BadConsts), $39: BadConsts::Inside, $46: Integer, $49: BadConsts::After, $56: Integer, $58: Integer): # outerLoops: 1 - -> (NilClass ? bb5 : bb3) + -> (NilClass ? bb9 : bb7) # backedges -# - bb2(rubyRegionId=1) -bb3[rubyRegionId=0, firstDead=20]($34: Sorbet::Private::Static::Void, $35: T.class_of(BadConsts), $72: BadConsts::After, $79: Integer, $81: Integer): +# - bb6(rubyRegionId=2) +bb7[rubyRegionId=0, firstDead=16]($34: Sorbet::Private::Static::Void, $35: T.class_of(BadConsts), $49: BadConsts::After, $56: Integer, $58: Integer): $32: Sorbet::Private::Static::Void = Solve<$34, enums> - $63: T.class_of(Sorbet::Private::Static) = alias - $65: T.class_of(BadConsts::After) = alias - $61: Sorbet::Private::Static::Void = $63: T.class_of(Sorbet::Private::Static).keep_for_ide($65: T.class_of(BadConsts::After)) - $68: T.class_of(Sorbet::Private::Static) = alias - $70: T.class_of(BadConsts) = alias - $66: Sorbet::Private::Static::Void = $68: T.class_of(Sorbet::Private::Static).keep_for_ide($70: T.class_of(BadConsts)) - $74: T.class_of(BadConsts::After) = alias - keep_for_ide$73: T.class_of(BadConsts::After) = $74 - keep_for_ide$73: T.untyped = keep_for_ide$73 - $77: T.class_of(BadConsts::After) = alias - $75: BadConsts::After = $77: T.class_of(BadConsts::After).new() - $72: BadConsts::After = $75 - $79: Integer(3) = 3 - $83: T.class_of(Integer) = alias - keep_for_ide$82: T.class_of(Integer) = $83 - keep_for_ide$82: T.untyped = keep_for_ide$82 - $84: Integer(1) = 1 - $81: Integer = cast($84: Integer(1), Integer); + : T.class_of(BadConsts) = $35 + $51: T.class_of(BadConsts::After) = alias + keep_for_ide$50: T.class_of(BadConsts::After) = $51 + keep_for_ide$50: T.untyped = keep_for_ide$50 + $54: T.class_of(BadConsts::After) = alias + $52: BadConsts::After = $54: T.class_of(BadConsts::After).new() + $49: BadConsts::After = $52 + $56: Integer(3) = 3 + $60: T.class_of(Integer) = alias + keep_for_ide$59: T.class_of(Integer) = $60 + keep_for_ide$59: T.untyped = keep_for_ide$59 + $61: Integer(1) = 1 + $58: Integer = cast($61: Integer(1), Integer); + $62: T.class_of(BadConsts) = : T.class_of(BadConsts).public() : T.noreturn = return $2: NilClass -> bb1 # backedges -# - bb2(rubyRegionId=1) -bb5[rubyRegionId=1, firstDead=16](: T.class_of(BadConsts), $34: Sorbet::Private::Static::Void, $35: T.class_of(BadConsts), $50: BadConsts::Inside, $57: Integer, $72: BadConsts::After, $79: Integer, $81: Integer): +# - bb6(rubyRegionId=2) +bb9[rubyRegionId=2, firstDead=10](: T.class_of(BadConsts), $34: Sorbet::Private::Static::Void, $35: T.class_of(BadConsts), $39: BadConsts::Inside, $46: Integer, $49: BadConsts::After, $56: Integer, $58: Integer): # outerLoops: 1 : T.class_of(BadConsts) = loadSelf(enums) - $41: T.class_of(Sorbet::Private::Static) = alias - $43: T.class_of(BadConsts::Inside) = alias - $39: Sorbet::Private::Static::Void = $41: T.class_of(Sorbet::Private::Static).keep_for_ide($43: T.class_of(BadConsts::Inside)) - $46: T.class_of(Sorbet::Private::Static) = alias - $48: T.class_of(BadConsts) = alias - $44: Sorbet::Private::Static::Void = $46: T.class_of(Sorbet::Private::Static).keep_for_ide($48: T.class_of(BadConsts)) - $52: T.class_of(BadConsts::Inside) = alias - keep_for_ide$51: T.class_of(BadConsts::Inside) = $52 - keep_for_ide$51: T.untyped = keep_for_ide$51 - $55: T.class_of(BadConsts::Inside) = alias - $53: BadConsts::Inside = $55: T.class_of(BadConsts::Inside).new() - $50: BadConsts::Inside = $53 - $57: Integer(2) = 2 + $41: T.class_of(BadConsts::Inside) = alias + keep_for_ide$40: T.class_of(BadConsts::Inside) = $41 + keep_for_ide$40: T.untyped = keep_for_ide$40 + $44: T.class_of(BadConsts::Inside) = alias + $42: BadConsts::Inside = $44: T.class_of(BadConsts::Inside).new() + $39: BadConsts::Inside = $42 + $46: Integer(2) = 2 $36: NilClass = nil - $58: T.noreturn = blockreturn $36: NilClass - -> bb2 + $47: T.noreturn = blockreturn $36: NilClass + -> bb6 } diff --git a/test/testdata/rewriter/t_enum_snapshot.rb.rewrite-tree.exp b/test/testdata/rewriter/t_enum_snapshot.rb.rewrite-tree.exp index 7d6edfbb32..e9d5422cda 100644 --- a/test/testdata/rewriter/t_enum_snapshot.rb.rewrite-tree.exp +++ b/test/testdata/rewriter/t_enum_snapshot.rb.rewrite-tree.exp @@ -1,5 +1,13 @@ class <>> < (::) class ::<>> < (::::) + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .returns(::String) + end + + def serialize<>(&) + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") + end + .extend(::T::Helpers) .abstract!() @@ -20,6 +28,10 @@ class <>> < (::) nil end end + + .public() + + end class ::<>> < (::) @@ -36,6 +48,14 @@ class <>> < (::) end + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .returns(::String) + end + + def serialize<>(&) + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") + end + .extend(::T::Helpers) .abstract!() @@ -58,9 +78,21 @@ class <>> < (::) end + + .public() + + end class ::<>> < (::::) + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .returns(::String) + end + + def serialize<>(&) + ::Kernel.raise("Sorbet rewriter pass partially unimplemented") + end + .extend(::T::Helpers) .abstract!() @@ -92,5 +124,9 @@ class <>> < (::) :: = 3 :: = (1, , ::) + + .public() + + end end diff --git a/test/testdata/rewriter/t_enum_strong.rb b/test/testdata/rewriter/t_enum_strong.rb new file mode 100644 index 0000000000..1d94798e61 --- /dev/null +++ b/test/testdata/rewriter/t_enum_strong.rb @@ -0,0 +1,10 @@ +# typed: strong + +extend T::Sig + +class MyEnum < T::Enum + enums do + X = new + Y = new + end +end diff --git a/test/testdata/rewriter/t_struct/default.rb b/test/testdata/rewriter/t_struct/default.rb index 87fe595c76..5ce8227fba 100644 --- a/test/testdata/rewriter/t_struct/default.rb +++ b/test/testdata/rewriter/t_struct/default.rb @@ -7,8 +7,8 @@ class Default < T::Struct T.reveal_type(Default.new.foo) # error: Revealed type: `Integer` Default.new(foo: "no") # ^^^^ error: Expected `Integer` but found `String("no")` for argument `foo` - Default.new(foo: 3, bar: 4) -# ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Unrecognized keyword argument `bar` passed for method `Default#initialize` +Default.new(foo: 3, bar: 4) +# ^^^^^^ error: Unrecognized keyword argument `bar` passed for method `Default#initialize` T.reveal_type(Default.new(foo: 3).foo) # error: Revealed type: `Integer` class Bad < T::Struct diff --git a/test/testdata/rewriter/t_struct/default_compiled.rb b/test/testdata/rewriter/t_struct/default_compiled.rb index 218921d473..4c6f9a3c39 100644 --- a/test/testdata/rewriter/t_struct/default_compiled.rb +++ b/test/testdata/rewriter/t_struct/default_compiled.rb @@ -8,8 +8,8 @@ class Default < T::Struct T.reveal_type(Default.new.foo) # error: Revealed type: `Integer` Default.new(foo: "no") # ^^^^ error: Expected `Integer` but found `String("no")` for argument `foo` - Default.new(foo: 3, bar: 4) -# ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Unrecognized keyword argument `bar` passed for method `Default#initialize` +Default.new(foo: 3, bar: 4) +# ^^^^^^ error: Unrecognized keyword argument `bar` passed for method `Default#initialize` T.reveal_type(Default.new(foo: 3).foo) # error: Revealed type: `Integer` class Bad < T::Struct diff --git a/test/testdata/rewriter/t_struct/nilable.rb b/test/testdata/rewriter/t_struct/nilable.rb index 000e48640b..90ab38aef2 100644 --- a/test/testdata/rewriter/t_struct/nilable.rb +++ b/test/testdata/rewriter/t_struct/nilable.rb @@ -7,7 +7,7 @@ class Nilable < T::Struct T.reveal_type(Nilable.new.foo) # error: Revealed type: `T.nilable(Integer)` Nilable.new(foo: "no") # ^^^^ error: Expected `T.nilable(Integer)` but found `String("no")` for argument `foo` - Nilable.new(foo: 3, bar: 4) -# ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Unrecognized keyword argument `bar` passed for method `Nilable#initialize` +Nilable.new(foo: 3, bar: 4) +# ^^^^^^ error: Unrecognized keyword argument `bar` passed for method `Nilable#initialize` T.reveal_type(Nilable.new(foo: 3).foo) # error: Revealed type: `T.nilable(Integer)` T.reveal_type(Nilable.new(foo: nil).foo) # error: Revealed type: `T.nilable(Integer)` diff --git a/test/testdata/rewriter/t_struct/nilable_compiled.rb b/test/testdata/rewriter/t_struct/nilable_compiled.rb index 7cfa177cd5..edc005cc21 100644 --- a/test/testdata/rewriter/t_struct/nilable_compiled.rb +++ b/test/testdata/rewriter/t_struct/nilable_compiled.rb @@ -9,6 +9,6 @@ class Nilable < T::Struct Nilable.new(foo: "no") # ^^^^ error: Expected `T.nilable(Integer)` but found `String("no")` for argument `foo` Nilable.new(foo: 3, bar: 4) -# ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Unrecognized keyword argument `bar` passed for method `Nilable#initialize` + # ^^^^^^ error: Unrecognized keyword argument `bar` passed for method `Nilable#initialize` T.reveal_type(Nilable.new(foo: 3).foo) # error: Revealed type: `T.nilable(Integer)` T.reveal_type(Nilable.new(foo: nil).foo) # error: Revealed type: `T.nilable(Integer)` diff --git a/test/testdata/rewriter/t_struct/normal.rb b/test/testdata/rewriter/t_struct/normal.rb index a63bae32e1..9ff4a65700 100644 --- a/test/testdata/rewriter/t_struct/normal.rb +++ b/test/testdata/rewriter/t_struct/normal.rb @@ -8,6 +8,6 @@ class Normal < T::Struct # ^ error: Missing required keyword argument `foo` for method `Normal#initialize` Normal.new(foo: "no") # ^^^^ error: Expected `Integer` but found `String("no")` for argument `foo` - Normal.new(foo: 3, bar: 4) -# ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Unrecognized keyword argument `bar` passed for method `Normal#initialize` +Normal.new(foo: 3, bar: 4) +# ^^^^^^ error: Unrecognized keyword argument `bar` passed for method `Normal#initialize` T.reveal_type(Normal.new(foo: 3).foo) # error: Revealed type: `Integer` diff --git a/test/testdata/rewriter/t_struct/normal_compiled.rb b/test/testdata/rewriter/t_struct/normal_compiled.rb index e188333395..395f8d00bb 100644 --- a/test/testdata/rewriter/t_struct/normal_compiled.rb +++ b/test/testdata/rewriter/t_struct/normal_compiled.rb @@ -8,6 +8,6 @@ class Normal < T::Struct Normal.new # error: Missing required keyword argument `foo` for method `Normal#initialize` Normal.new(foo: "no") # ^^^^ error: Expected `Integer` but found `String("no")` for argument `foo` - Normal.new(foo: 3, bar: 4) -# ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Unrecognized keyword argument `bar` passed for method `Normal#initialize` +Normal.new(foo: 3, bar: 4) +# ^^^^^^ error: Unrecognized keyword argument `bar` passed for method `Normal#initialize` T.reveal_type(Normal.new(foo: 3).foo) # error: Revealed type: `Integer` diff --git a/test/testdata/rewriter/t_struct/override.rb b/test/testdata/rewriter/t_struct/override.rb index bcf69f47be..d0c9abd29c 100644 --- a/test/testdata/rewriter/t_struct/override.rb +++ b/test/testdata/rewriter/t_struct/override.rb @@ -18,6 +18,6 @@ def initialize(foo:) # ^ error: Missing required keyword argument `foo` for method `Override#initialize` Override.new(foo: "no") # ^^^^ error: Expected `Integer` but found `String("no")` for argument `foo` - Override.new(foo: 3, bar: 4) -# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Unrecognized keyword argument `bar` passed for method `Override#initialize` +Override.new(foo: 3, bar: 4) +# ^^^^^^ error: Unrecognized keyword argument `bar` passed for method `Override#initialize` T.reveal_type(Override.new(foo: 3).foo) # error: Revealed type: `String` diff --git a/test/testdata/rewriter/t_struct/override_compiled.rb b/test/testdata/rewriter/t_struct/override_compiled.rb index f9463d7d61..9bfab83a88 100644 --- a/test/testdata/rewriter/t_struct/override_compiled.rb +++ b/test/testdata/rewriter/t_struct/override_compiled.rb @@ -18,6 +18,6 @@ def initialize(foo:) Override.new # error: Missing required keyword argument `foo` for method `Override#initialize` Override.new(foo: "no") # ^^^^ error: Expected `Integer` but found `String("no")` for argument `foo` - Override.new(foo: 3, bar: 4) -# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Unrecognized keyword argument `bar` passed for method `Override#initialize` +Override.new(foo: 3, bar: 4) +# ^^^^^^ error: Unrecognized keyword argument `bar` passed for method `Override#initialize` T.reveal_type(Override.new(foo: 3).foo) # error: Revealed type: `String` diff --git a/test/testdata/rewriter/t_struct/param_order.rb b/test/testdata/rewriter/t_struct/param_order.rb index a7423914d8..d2381d778c 100644 --- a/test/testdata/rewriter/t_struct/param_order.rb +++ b/test/testdata/rewriter/t_struct/param_order.rb @@ -1,7 +1,7 @@ # typed: strict # We run checks to ensure that all optional arguments to a method appear after all required arguments. This means that -# when we synthesize the initalize method for this struct, the optional foo parameter must be synthesized to come after +# when we synthesize the initialize method for this struct, the optional foo parameter must be synthesized to come after # the required bar parameter, even though the order we wrote in the code was the opposite. class ParamOrder < T::Struct prop :foo, Integer, default: 3 diff --git a/test/testdata/rewriter/t_struct/param_order_compiled.rb b/test/testdata/rewriter/t_struct/param_order_compiled.rb index ed43ad10ed..693b15f6a4 100644 --- a/test/testdata/rewriter/t_struct/param_order_compiled.rb +++ b/test/testdata/rewriter/t_struct/param_order_compiled.rb @@ -2,7 +2,7 @@ # typed: strict # We run checks to ensure that all optional arguments to a method appear after all required arguments. This means that -# when we synthesize the initalize method for this struct, the optional foo parameter must be synthesized to come after +# when we synthesize the initialize method for this struct, the optional foo parameter must be synthesized to come after # the required bar parameter, even though the order we wrote in the code was the opposite. class ParamOrder < T::Struct prop :foo, Integer, default: 3 diff --git a/test/testdata/rewriter/t_struct/root.rb b/test/testdata/rewriter/t_struct/root.rb index f1d7dda0fc..605670cce5 100644 --- a/test/testdata/rewriter/t_struct/root.rb +++ b/test/testdata/rewriter/t_struct/root.rb @@ -8,6 +8,6 @@ class Root < ::T::Struct # ^ error: Missing required keyword argument `foo` for method `Root#initialize` Root.new(foo: "no") # ^^^^ error: Expected `Integer` but found `String("no")` for argument `foo` - Root.new(foo: 3, bar: 4) -# ^^^^^^^^^^^^^^^^^^^^^^^^ error: Unrecognized keyword argument `bar` passed for method `Root#initialize` +Root.new(foo: 3, bar: 4) +# ^^^^^^ error: Unrecognized keyword argument `bar` passed for method `Root#initialize` T.reveal_type(Root.new(foo: 3).foo) # error: Revealed type: `Integer` diff --git a/test/testdata/rewriter/t_struct/root_compiled.rb b/test/testdata/rewriter/t_struct/root_compiled.rb index 17804dcdda..c0e263f250 100644 --- a/test/testdata/rewriter/t_struct/root_compiled.rb +++ b/test/testdata/rewriter/t_struct/root_compiled.rb @@ -8,6 +8,6 @@ class Root < ::T::Struct Root.new # error: Missing required keyword argument `foo` for method `Root#initialize` Root.new(foo: "no") # ^^^^ error: Expected `Integer` but found `String("no")` for argument `foo` - Root.new(foo: 3, bar: 4) -# ^^^^^^^^^^^^^^^^^^^^^^^^ error: Unrecognized keyword argument `bar` passed for method `Root#initialize` +Root.new(foo: 3, bar: 4) +# ^^^^^^ error: Unrecognized keyword argument `bar` passed for method `Root#initialize` T.reveal_type(Root.new(foo: 3).foo) # error: Revealed type: `Integer` diff --git a/test/testdata/rewriter/test_case.rb.rewrite-tree.exp b/test/testdata/rewriter/test_case.rb.rewrite-tree.exp index aef0bb96db..07a0baa387 100644 --- a/test/testdata/rewriter/test_case.rb.rewrite-tree.exp +++ b/test/testdata/rewriter/test_case.rb.rewrite-tree.exp @@ -43,7 +43,7 @@ class <>> < (::) .void() end - def initialize<>(&) + def <>(&) begin @a = (1, , ::) .foo() @@ -54,7 +54,7 @@ class <>> < (::) .void() end - def initialize<>(&) + def <>(&) .bar() end @@ -102,9 +102,9 @@ class <>> < (::) - + > - + > @@ -180,7 +180,7 @@ class <>> < (::) .void() end - def initialize<>(&) + def <>(&) @a = (1, , ::) end @@ -202,7 +202,7 @@ class <>> < (::) - + > end diff --git a/test/testdata/rewriter/test_each_describe.rb b/test/testdata/rewriter/test_each_describe.rb new file mode 100644 index 0000000000..ca0a75d802 --- /dev/null +++ b/test/testdata/rewriter/test_each_describe.rb @@ -0,0 +1,81 @@ +# typed: true +require 'minitest/autorun' +require_relative 'gems/sorbet-runtime/lib/sorbet-runtime' +extend T::Sig + +class Minitest::Spec + def self.test_each(arg, &blk) + arg.each(&blk) + end +end + +class Flag + def self.enable(arg) + p arg + end +end + +class MyTest < Minitest::Spec + test_each([true, false]) do |flag_enabled| + describe 'foo' do + before do + T.reveal_type(flag_enabled) # error: `T::Boolean` + Flag.enable(flag_enabled) + @enabled = T.let(flag_enabled, T::Boolean) + end + + after do + @enabled = false + end + + it "do this thing" do + T.reveal_type(@enabled) # error: `T::Boolean` + T.reveal_type(flag_enabled) # error: `T::Boolean` + end + end + end +end + +class MyTestBad1 < Minitest::Spec + test_each([true, false]) do |flag_enabled| + describe 'foo' do + def nope; end + # ^^^^^^^^^^^^^ error: Only valid `it`, `before`, `after`, and `describe` blocks can appear within `test_each` + end + end +end + +class MyTestBad2 < Minitest::Spec + test_each([true, false]) do |flag_enabled| + describe 'foo' do + it '' do + end + def nope; end + # ^^^^^^^^^^^^^ error: Only valid `it`, `before`, `after`, and `describe` blocks can appear within `test_each` + end + end +end + +class MyTestBad3 < Minitest::Spec + test_each([true, false]) do |flag_enabled| + describe 'foo' do + it '' do + end + def nope; end + # ^^^^^^^^^^^^^ error: Only valid `it`, `before`, `after`, and `describe` blocks can appear within `test_each` + end + end +end + +class MyTestBad4 < Minitest::Spec + test_each([true, false]) do |flag_enabled| + x = flag_enabled + # ^^^^^^^^^^^^^^^^ error: Only valid `it`, `before`, `after`, and `describe` blocks can appear within `test_each` + puts(x) + # ^^^^^^^ error: Only valid `it`, `before`, `after`, and `describe` blocks can appear within `test_each` + describe 'foo' do + it '' do + end + end + end +end diff --git a/test/testdata/rewriter/test_each_describe.rb.rewrite-tree.exp b/test/testdata/rewriter/test_each_describe.rb.rewrite-tree.exp new file mode 100644 index 0000000000..2e2fb58033 --- /dev/null +++ b/test/testdata/rewriter/test_each_describe.rb.rewrite-tree.exp @@ -0,0 +1,146 @@ +class <>> < (::) + .require("minitest/autorun") + + .require_relative("gems/sorbet-runtime/lib/sorbet-runtime") + + .extend(::::) + + class ::::<>> < (::) + def self.test_each<>(arg, &blk) + ::.(arg, :each, blk) + end + + + end + + class ::<>> < (::) + def self.enable<>(arg, &) + .p(arg) + end + + + end + + class ::<>> < (::::) + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .void() + end + + def <>(&) + [true, false].each() do |flag_enabled| + begin + ::.reveal_type(flag_enabled) + ::.enable(flag_enabled) + @enabled = (flag_enabled, , ::::) + end + end + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .void() + end + + def <>(&) + [true, false].each() do |flag_enabled| + @enabled = false + end + end + + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .void() + end + + def <>(&) + [true, false].each() do |flag_enabled| + begin + ::.reveal_type(@enabled) + ::.reveal_type(flag_enabled) + end + end + end + + .test_each([true, false]) do |flag_enabled| + begin + > + > + > + end + end + end + + class ::<>> < (::::) + def nope<>(&) + + end + + .test_each([true, false]) do |flag_enabled| + + end + end + + class ::<>> < (::::) + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .void() + end + + def <>(&) + [true, false].each() do |flag_enabled| + + end + end + + def nope<>(&) + + end + + .test_each([true, false]) do |flag_enabled| + begin + > + + end + end + end + + class ::<>> < (::::) + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .void() + end + + def <>(&) + [true, false].each() do |flag_enabled| + + end + end + + def nope<>(&) + + end + + .test_each([true, false]) do |flag_enabled| + begin + > + + end + end + end + + class ::<>> < (::::) + ::Sorbet::Private::Static.sig(::T::Sig::WithoutRuntime) do || + .void() + end + + def <>(&) + [true, false].each() do |flag_enabled| + + end + end + + .test_each([true, false]) do |flag_enabled| + begin + x = flag_enabled + .puts(x) + > + end + end + end +end diff --git a/test/testdata/ruby_benchmark/.rubocop.yml b/test/testdata/ruby_benchmark/.rubocop.yml deleted file mode 100644 index 9a24e6a2ad..0000000000 --- a/test/testdata/ruby_benchmark/.rubocop.yml +++ /dev/null @@ -1,2 +0,0 @@ -AllCops: - Exclude: * diff --git a/test/testdata/ruby_benchmark/README.md b/test/testdata/ruby_benchmark/README.md deleted file mode 100644 index 24a2669143..0000000000 --- a/test/testdata/ruby_benchmark/README.md +++ /dev/null @@ -1,72 +0,0 @@ -# ruby/benchmark - -This directory has benchmark definitions to be run with -[benchmark\_driver.gem](https://github.com/benchmark-driver/benchmark-driver). - -## Normal usage - -Execute `gem install benchmark_driver` and run a command like: - -```bash -# Run a benchmark script with the ruby in the $PATH -benchmark-driver benchmark/app_fib.rb - -# Run benchmark scripts with multiple Ruby executables or options -benchmark-driver benchmark/*.rb -e /path/to/ruby -e '/path/to/ruby --jit' - -# Or compare Ruby versions managed by rbenv -benchmark-driver benchmark/*.rb --rbenv '2.5.1;2.6.0-preview2 --jit' - -# You can collect many metrics in many ways -benchmark-driver benchmark/*.rb --runner memory --output markdown - -# Some are defined with YAML for complex setup or accurate measurement -benchmark-driver benchmark/*.yml -``` - -See also: - -```console -Usage: benchmark-driver [options] RUBY|YAML... - -r, --runner TYPE Specify runner type: ips, time, memory, once (default: ips) - -o, --output TYPE Specify output type: compare, simple, markdown, record (default: compare) - -e, --executables EXECS Ruby executables (e1::path1 arg1; e2::path2 arg2;...) - --rbenv VERSIONS Ruby executables in rbenv (x.x.x arg1;y.y.y arg2;...) - --repeat-count NUM Try benchmark NUM times and use the fastest result or the worst memory usage - --repeat-result TYPE Yield "best", "average" or "worst" result with --repeat-count (default: best) - --bundler Install and use gems specified in Gemfile - --filter REGEXP Filter out benchmarks with given regexp - --run-duration SECONDS Warmup estimates loop_count to run for this duration (default: 3) - -v, --verbose Verbose mode. Multiple -v options increase visilibity (max: 2) -``` - -## make benchmark - -Using `make benchmark`, `make update-benchmark-driver` automatically downloads -the supported version of benchmark\_driver, and it runs benchmarks with the downloaded -benchmark\_driver. - -```bash -# Run all benchmarks with the ruby in the $PATH and the built ruby -make benchmark - -# Or compare with specific ruby binary -make benchmark COMPARE_RUBY="/path/to/ruby --jit" - -# Run vm1 benchmarks -make benchmark ITEM=vm1 - -# Run some limited benchmarks in ITEM-matched files -make benchmark ITEM=vm1 OPTS=--filter=block - -# You can specify the benchmark by an exact filename instead of using the default argument: -# ARGS = $$(find $(srcdir)/benchmark -maxdepth 1 -name '*$(ITEM)*.yml' -o -name '*$(ITEM)*.rb') -make benchmark ARGS=../benchmark/erb_render.yml - -# You can specify any option via $OPTS -make benchmark OPTS="--help" - -# With `make benchmark`, some special runner plugins are available: -# -r peak, -r size, -r total, -r utime, -r stime, -r cutime, -r cstime -make benchmark ITEM=vm2_bigarray OPTS="-r peak" -``` diff --git a/test/testdata/ruby_benchmark/app_answer.rb b/test/testdata/ruby_benchmark/app_answer.rb deleted file mode 100644 index fdfb19c0b9..0000000000 --- a/test/testdata/ruby_benchmark/app_answer.rb +++ /dev/null @@ -1,18 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -def ack(m, n) - if m == 0 then - n + 1 - elsif n == 0 then - ack(m - 1, 1) - else - ack(m - 1, ack(m, n - 1)) - end -end - -def the_answer_to_life_the_universe_and_everything - (ack(3,7).to_s.split(//).inject(0){|s,x| s+x.to_i}.to_s + "2" ).to_i -end - -answer = the_answer_to_life_the_universe_and_everything diff --git a/test/testdata/ruby_benchmark/app_erb.yml b/test/testdata/ruby_benchmark/app_erb.yml deleted file mode 100644 index 31e29b7644..0000000000 --- a/test/testdata/ruby_benchmark/app_erb.yml +++ /dev/null @@ -1,23 +0,0 @@ -# -# Create many HTML strings with ERB. -# -prelude: | - require 'erb' - - data = < - <%= title %> - -

<%= title %>

-

- <%= content %> -

- - - erb - - title = "hello world!" - content = "hello world!\n" * 10 -benchmark: - app_erb: ERB.new(data).result(binding) -loop_count: 15000 diff --git a/test/testdata/ruby_benchmark/app_fib.llo.exp b/test/testdata/ruby_benchmark/app_fib.llo.exp deleted file mode 100644 index f1ff221fbe..0000000000 --- a/test/testdata/ruby_benchmark/app_fib.llo.exp +++ /dev/null @@ -1,980 +0,0 @@ -; ModuleID = 'payload' -source_filename = "llvm-link" -target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } -%struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } -%struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } -%struct.anon.0 = type { i16, [2 x i8] } -%struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } -%struct.rb_iseq_location_struct = type { i64, i64, i64, i64, i32, %struct.rb_code_location_struct } -%struct.rb_code_location_struct = type { %struct.rb_code_position_struct, %struct.rb_code_position_struct } -%struct.rb_code_position_struct = type { i32, i32 } -%struct.iseq_insn_info = type { %struct.iseq_insn_info_entry*, i32*, i32, %struct.succ_index_table* } -%struct.iseq_insn_info_entry = type opaque -%struct.succ_index_table = type opaque -%struct.iseq_catch_table = type opaque -%union.iseq_inline_storage_entry = type { %struct.iseq_inline_cache_entry } -%struct.iseq_inline_cache_entry = type { i64, %struct.rb_cref_struct*, i64 } -%struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } -%struct.rb_scope_visi_struct = type { i8, [3 x i8] } -%struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } -%struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } -%union.anon.13 = type { %struct.rb_method_cfunc_struct } -%struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%struct.rb_calling_info = type { i64, i64, i32, i32 } -%union.anon.15 = type { i32 } -%struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.16 = type { i64, i64, i64, i64* } -%union.anon.17 = type { %struct.anon.18 } -%struct.anon.18 = type { i64, i32 } -%struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } -%struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } -%struct.rb_fiber_struct = type opaque -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } -%struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } -%struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.__pthread_cond_s } -%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } -%union.anon = type { i64 } -%struct.anon.5 = type { [65 x i64] } -%struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } -%struct.rb_event_hook_struct = type opaque -%struct.rb_postponed_job_struct = type opaque -%struct.list_head = type { %struct.list_node } -%struct.rb_objspace = type opaque -%struct.rb_at_exit_list = type { void (%struct.rb_vm_struct*)*, %struct.rb_at_exit_list* } -%struct.rb_builtin_function = type opaque -%struct.anon.6 = type { i64, i64, i64, i64 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } -%union.anon.9 = type { %union.pthread_cond_t } -%union.pthread_mutex_t = type { %struct.__pthread_mutex_s } -%struct.__pthread_mutex_s = type { i32, i32, i32, i32, i32, i16, i16, %struct.__pthread_internal_list } -%struct.__pthread_internal_list = type { %struct.__pthread_internal_list*, %struct.__pthread_internal_list* } -%struct.rb_unblock_callback = type { void (i8*)*, i8* } -%struct.rb_mutex_struct = type opaque -%struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.10 = type { %struct.anon.11 } -%struct.anon.11 = type { i64, i64, i32 } -%struct.st_table = type { i8, i8, i8, i32, %struct.st_hash_type*, i64, i64*, i64, i64, %struct.st_table_entry* } -%struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } -%struct.st_table_entry = type opaque -%struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } -%struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } -%struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } -%struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } -%struct.__sigset_t = type { [16 x i64] } -%struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } -%struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } -%struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } -%struct.rb_call_info_with_kwarg = type { %struct.rb_call_info, %struct.rb_call_info_kw_arg* } -%struct.rb_call_info_kw_arg = type { i32, [1 x i64] } -%struct.iseq_inline_iv_cache_entry = type { i64, i64 } - -@ruby_current_execution_context_ptr = external local_unnamed_addr global %struct.rb_execution_context_struct*, align 8 -@rb_cModule = external local_unnamed_addr constant i64, align 8 -@ruby_vm_global_constant_state = external local_unnamed_addr global i64, align 8 -@rb_eRuntimeError = external local_unnamed_addr global i64, align 8 -@.str.9 = private unnamed_addr constant [95 x i8] c"sorbet_getBuildSCMRevision: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@.str.10 = private unnamed_addr constant [93 x i8] c"sorbet_getIsReleaseBuild: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@"stackFramePrecomputed_func_.17$152" = unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"rubyIdPrecomputed_" = internal unnamed_addr global i64 0, align 8 -@"str_" = private unnamed_addr constant [17 x i8] c"\00", align 1 -@"rubyStrFrozen_" = internal unnamed_addr global i64 0, align 8 -@"rubyStrFrozen_test/testdata/ruby_benchmark/app_fib.rb" = internal unnamed_addr global i64 0, align 8 -@"str_test/testdata/ruby_benchmark/app_fib.rb" = private unnamed_addr constant [40 x i8] c"test/testdata/ruby_benchmark/app_fib.rb\00", align 1 -@iseqEncodedArray = internal global [18 x i64] zeroinitializer -@fileLineNumberInfo = internal global %struct.SorbetLineNumberInfo zeroinitializer -@str_HasFib = private unnamed_addr constant [7 x i8] c"HasFib\00", align 1 -@rubyIdPrecomputed_fib = internal unnamed_addr global i64 0, align 8 -@str_fib = private unnamed_addr constant [4 x i8] c"fib\00", align 1 -@ic_fib.1 = internal global %struct.FunctionInlineCache zeroinitializer -@stackFramePrecomputed_func_HasFib.3fib = unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@ic_fib.2 = internal global %struct.FunctionInlineCache zeroinitializer -@str_sig = private unnamed_addr constant [4 x i8] c"sig\00", align 1 -@str_Integer = private unnamed_addr constant [8 x i8] c"Integer\00", align 1 -@"str_<" = private unnamed_addr constant [2 x i8] c"<\00", align 1 -@str_- = private unnamed_addr constant [2 x i8] c"-\00", align 1 -@ic_fib.3 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_fib.4 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_fib.6 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_fib.7 = internal global %struct.FunctionInlineCache zeroinitializer -@"rubyIdPrecomputed_+" = internal unnamed_addr global i64 0, align 8 -@"str_+" = private unnamed_addr constant [2 x i8] c"+\00", align 1 -@"ic_+" = internal global %struct.FunctionInlineCache zeroinitializer -@"str_Return value" = private unnamed_addr constant [13 x i8] c"Return value\00", align 1 -@"stackFramePrecomputed_func_HasFib.13" = unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"rubyIdPrecomputed_" = internal unnamed_addr global i64 0, align 8 -@"str_" = private unnamed_addr constant [15 x i8] c"\00", align 1 -@"stackFramePrecomputed_func_HasFib.13$block_1" = unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"rubyIdPrecomputed_block in " = internal unnamed_addr global i64 0, align 8 -@"str_block in " = private unnamed_addr constant [24 x i8] c"block in \00", align 1 -@str_final = private unnamed_addr constant [6 x i8] c"final\00", align 1 -@rubyIdPrecomputed_n = internal unnamed_addr global i64 0, align 8 -@str_n = private unnamed_addr constant [2 x i8] c"n\00", align 1 -@ic_params = internal global %struct.FunctionInlineCache zeroinitializer -@rubyIdPrecomputed_params = internal unnamed_addr global i64 0, align 8 -@str_params = private unnamed_addr constant [7 x i8] c"params\00", align 1 -@ic_returns = internal global %struct.FunctionInlineCache zeroinitializer -@rubyIdPrecomputed_returns = internal unnamed_addr global i64 0, align 8 -@str_returns = private unnamed_addr constant [8 x i8] c"returns\00", align 1 -@str_normal = private unnamed_addr constant [7 x i8] c"normal\00", align 1 -@guard_epoch_HasFib = linkonce local_unnamed_addr global i64 0 -@guarded_const_HasFib = linkonce local_unnamed_addr global i64 0 -@rb_cObject = external local_unnamed_addr constant i64 - -; Function Attrs: nounwind readnone willreturn -declare i64 @rb_id2sym(i64) local_unnamed_addr #0 - -; Function Attrs: nounwind readnone willreturn -declare i64 @rb_obj_is_kind_of(i64, i64) local_unnamed_addr #0 - -; Function Attrs: cold noreturn -declare void @sorbet_cast_failure(i64, i8*, i8*) local_unnamed_addr #1 - -; Function Attrs: noreturn -declare void @sorbet_raiseArity(i32, i32, i32) local_unnamed_addr #2 - -declare %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64, i64, i64, i64, %struct.rb_iseq_struct*, i32, i32, %struct.SorbetLineNumberInfo*, i64*, i32, i32) local_unnamed_addr #3 - -declare void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo*, i64*, i32) local_unnamed_addr #3 - -declare i64 @sorbet_getConstant(i8*, i64) local_unnamed_addr #3 - -declare i64 @sorbet_readRealpath() local_unnamed_addr #3 - -declare %struct.rb_control_frame_struct* @sorbet_pushCfuncFrame(%struct.FunctionInlineCache*, i64, %struct.rb_iseq_struct*) local_unnamed_addr #3 - -declare %struct.rb_control_frame_struct* @sorbet_pushStaticInitFrame(i64) local_unnamed_addr #3 - -declare void @sorbet_popFrame() local_unnamed_addr #3 - -declare void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache*, i64, i32, i32, i32, i64*) local_unnamed_addr #3 - -declare i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache*, i64) local_unnamed_addr #3 - -declare void @sorbet_vmMethodSearch(%struct.FunctionInlineCache*, i64) local_unnamed_addr #3 - -declare void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_iseq_struct*) local_unnamed_addr #3 - -declare i64 @sorbet_rb_int_plus_slowpath(i64, i64) local_unnamed_addr #3 - -declare i64 @sorbet_rb_int_minus_slowpath(i64, i64) local_unnamed_addr #3 - -declare i64 @sorbet_rb_int_lt_slowpath(i64, i64) local_unnamed_addr #3 - -declare void @sorbet_vm_define_method(i64, i8*, i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*)*, i8*, %struct.rb_iseq_struct*, i1 zeroext) local_unnamed_addr #3 - -declare i64 @sorbet_vm_fstring_new(i8*, i64) local_unnamed_addr #3 - -; Function Attrs: nounwind readnone willreturn -declare i64 @rb_class_inherited_p(i64, i64) local_unnamed_addr #0 - -declare i64 @rb_define_class(i8*, i64) local_unnamed_addr #3 - -declare i64 @rb_intern2(i8*, i64) local_unnamed_addr #3 - -declare void @rb_gc_register_mark_object(i64) local_unnamed_addr #3 - -; Function Attrs: noreturn -declare void @rb_raise(i64, i8*, ...) local_unnamed_addr #2 - -declare i64 @rb_int2big(i64) local_unnamed_addr #3 - -; Function Attrs: nofree nosync nounwind readnone speculatable willreturn -declare { i64, i1 } @llvm.sadd.with.overflow.i64(i64, i64) #4 - -; Function Attrs: nofree nosync nounwind readnone speculatable willreturn -declare { i64, i1 } @llvm.ssub.with.overflow.i64(i64, i64) #4 - -declare i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct*, i32) local_unnamed_addr #3 - -; Function Attrs: allocsize(0,1) -declare noalias nonnull i8* @ruby_xcalloc(i64, i64) local_unnamed_addr #5 - -; Function Attrs: allocsize(0,1) -declare noalias nonnull i8* @ruby_xmalloc2(i64, i64) local_unnamed_addr #5 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) #6 - -; Function Attrs: nounwind ssp uwtable -define weak i32 @sorbet_getIsReleaseBuild() local_unnamed_addr #7 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([93 x i8], [93 x i8]* @.str.10, i64 0, i64 0)) #15 - unreachable -} - -; Function Attrs: nounwind ssp uwtable -define weak i8* @sorbet_getBuildSCMRevision() local_unnamed_addr #7 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([95 x i8], [95 x i8]* @.str.9, i64 0, i64 0)) #15 - unreachable -} - -; Function Attrs: nounwind sspreq uwtable -define internal fastcc void @"func_HasFib.13L64"(%struct.rb_control_frame_struct* nocapture writeonly %cfp) unnamed_addr #8 !dbg !10 { -functionEntryInitializers: - %stackFrame = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_HasFib.13", align 8 - %0 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !14 - %1 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %0, i64 0, i32 2 - %2 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %1, align 8, !tbaa !16 - %3 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame, %struct.rb_iseq_struct** %3, align 8, !tbaa !20 - %4 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 4 - %5 = load i64*, i64** %4, align 8, !tbaa !22 - %6 = load i64, i64* %5, align 8, !tbaa !6 - %7 = and i64 %6, -33 - store i64 %7, i64* %5, align 8, !tbaa !6 - tail call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %0, %struct.rb_control_frame_struct* %2, %struct.rb_iseq_struct* %stackFrame) #16 - %8 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 0 - store i64* getelementptr inbounds ([18 x i64], [18 x i64]* @iseqEncodedArray, i64 0, i64 6), i64** %8, align 8, !dbg !23, !tbaa !14 - %9 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !24, !tbaa !14 - %10 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %9, i64 0, i32 5, !dbg !24 - %11 = load i32, i32* %10, align 8, !dbg !24, !tbaa !25 - %12 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %9, i64 0, i32 6, !dbg !24 - %13 = load i32, i32* %12, align 4, !dbg !24, !tbaa !26 - %14 = xor i32 %13, -1, !dbg !24 - %15 = and i32 %14, %11, !dbg !24 - %16 = icmp eq i32 %15, 0, !dbg !24 - br i1 %16, label %rb_vm_check_ints.exit1, label %17, !dbg !24, !prof !27 - -17: ; preds = %functionEntryInitializers - %18 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %9, i64 0, i32 8, !dbg !24 - %19 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %18, align 8, !dbg !24, !tbaa !28 - %20 = tail call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %19, i32 noundef 0) #16, !dbg !24 - br label %rb_vm_check_ints.exit1, !dbg !24 - -rb_vm_check_ints.exit1: ; preds = %functionEntryInitializers, %17 - store i64* getelementptr inbounds ([18 x i64], [18 x i64]* @iseqEncodedArray, i64 0, i64 7), i64** %8, align 8, !dbg !24, !tbaa !14 - %21 = load i64, i64* @guard_epoch_HasFib, align 8, !dbg !29 - %22 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !29, !tbaa !30 - %needTakeSlowPath = icmp ne i64 %21, %22, !dbg !29 - br i1 %needTakeSlowPath, label %23, label %24, !dbg !29, !prof !32 - -23: ; preds = %rb_vm_check_ints.exit1 - tail call void @const_recompute_HasFib(), !dbg !29 - br label %24, !dbg !29 - -24: ; preds = %rb_vm_check_ints.exit1, %23 - %25 = load i64, i64* @guarded_const_HasFib, align 8, !dbg !29 - %26 = load i64, i64* @guard_epoch_HasFib, align 8, !dbg !29 - %27 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !29, !tbaa !30 - %guardUpdated = icmp eq i64 %26, %27, !dbg !29 - tail call void @llvm.assume(i1 %guardUpdated), !dbg !29 - %stackFrame32 = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @stackFramePrecomputed_func_HasFib.3fib, align 8, !dbg !29 - %28 = tail call noalias nonnull i8* @ruby_xcalloc(i64 noundef 1, i64 noundef 64) #17, !dbg !29 - %29 = bitcast i8* %28 to i16*, !dbg !29 - %30 = load i16, i16* %29, align 8, !dbg !29 - %31 = and i16 %30, -384, !dbg !29 - %32 = or i16 %31, 1, !dbg !29 - store i16 %32, i16* %29, align 8, !dbg !29 - %33 = getelementptr inbounds i8, i8* %28, i64 8, !dbg !29 - %34 = bitcast i8* %33 to i32*, !dbg !29 - store i32 1, i32* %34, align 8, !dbg !29, !tbaa !33 - %35 = getelementptr inbounds i8, i8* %28, i64 12, !dbg !29 - %36 = bitcast i8* %35 to i32*, !dbg !29 - %37 = getelementptr inbounds i8, i8* %28, i64 4, !dbg !29 - %38 = bitcast i8* %37 to i32*, !dbg !29 - tail call void @llvm.memset.p0i8.i64(i8* nonnull align 4 %35, i8 0, i64 20, i1 false), !dbg !29 - store i32 1, i32* %38, align 4, !dbg !29, !tbaa !36 - %positional_table = alloca i64, align 8, !dbg !29 - %rubyId_n = load i64, i64* @rubyIdPrecomputed_n, align 8, !dbg !29 - store i64 %rubyId_n, i64* %positional_table, align 8, !dbg !29 - %39 = tail call noalias nonnull i8* @ruby_xmalloc2(i64 noundef 1, i64 noundef 8) #17, !dbg !29 - %40 = bitcast i64* %positional_table to i8*, !dbg !29 - call void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture nonnull writeonly align 1 %39, i8* nocapture noundef nonnull readonly align 8 dereferenceable(8) %40, i64 noundef 8, i1 noundef false) #16, !dbg !29 - %41 = getelementptr inbounds i8, i8* %28, i64 32, !dbg !29 - %42 = bitcast i8* %41 to i8**, !dbg !29 - store i8* %39, i8** %42, align 8, !dbg !29, !tbaa !37 - tail call void @sorbet_vm_define_method(i64 %25, i8* noundef getelementptr inbounds ([4 x i8], [4 x i8]* @str_fib, i64 0, i64 0), i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*)* noundef @func_HasFib.3fib, i8* nonnull %28, %struct.rb_iseq_struct* %stackFrame32, i1 noundef zeroext true) #16, !dbg !29 - %43 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !29, !tbaa !14 - %44 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %43, i64 0, i32 5, !dbg !29 - %45 = load i32, i32* %44, align 8, !dbg !29, !tbaa !25 - %46 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %43, i64 0, i32 6, !dbg !29 - %47 = load i32, i32* %46, align 4, !dbg !29, !tbaa !26 - %48 = xor i32 %47, -1, !dbg !29 - %49 = and i32 %48, %45, !dbg !29 - %50 = icmp eq i32 %49, 0, !dbg !29 - br i1 %50, label %rb_vm_check_ints.exit, label %51, !dbg !29, !prof !27 - -51: ; preds = %24 - %52 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %43, i64 0, i32 8, !dbg !29 - %53 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %52, align 8, !dbg !29, !tbaa !28 - %54 = tail call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %53, i32 noundef 0) #16, !dbg !29 - br label %rb_vm_check_ints.exit, !dbg !29 - -rb_vm_check_ints.exit: ; preds = %24, %51 - ret void -} - -; Function Attrs: nounwind sspreq uwtable -define i64 @func_HasFib.3fib(i32 %argc, i64* nocapture readonly %argArray, i64 %selfRaw, %struct.rb_control_frame_struct* nonnull align 8 dereferenceable(8) %cfp, i8* nocapture nofree readnone %callData) #8 !dbg !38 { -functionEntryInitializers: - %callArgs = alloca [2 x i64], align 8 - %0 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 0 - store i64* getelementptr inbounds ([18 x i64], [18 x i64]* @iseqEncodedArray, i64 0, i64 7), i64** %0, align 8, !tbaa !14 - %tooManyArgs = icmp ugt i32 %argc, 1, !dbg !39 - %tooFewArgs = icmp ult i32 %argc, 1, !dbg !39 - %or.cond = or i1 %tooManyArgs, %tooFewArgs, !dbg !39 - br i1 %or.cond, label %argCountFailBlock, label %fillRequiredArgs, !dbg !39, !prof !40 - -BB3: ; preds = %rb_vm_check_ints.exit98 - store i64* getelementptr inbounds ([18 x i64], [18 x i64]* @iseqEncodedArray, i64 0, i64 11), i64** %0, align 8, !tbaa !14 - store i64 3, i64* %callArgs0Addr, align 8, !dbg !41 - tail call void @llvm.experimental.noalias.scope.decl(metadata !42), !dbg !41 - %1 = load i64, i64* %68, align 8, !dbg !41, !tbaa !6, !alias.scope !42 - %2 = and i64 %1, %70, !dbg !41 - %3 = icmp eq i64 %2, 0, !dbg !41 - br i1 %3, label %13, label %4, !dbg !41, !prof !40 - -4: ; preds = %BB3 - %5 = add nsw i64 %1, -1, !dbg !41 - %6 = tail call { i64, i1 } @llvm.ssub.with.overflow.i64(i64 %rawArg_n, i64 %5) #18, !dbg !41 - %7 = extractvalue { i64, i1 } %6, 1, !dbg !41 - %8 = extractvalue { i64, i1 } %6, 0, !dbg !41 - br i1 %7, label %9, label %sorbet_rb_int_minus.exit, !dbg !41 - -9: ; preds = %4 - %10 = ashr i64 %8, 1, !dbg !41 - %11 = xor i64 %10, -9223372036854775808, !dbg !41 - %12 = tail call i64 @rb_int2big(i64 %11) #16, !dbg !41, !noalias !42 - br label %sorbet_rb_int_minus.exit, !dbg !41 - -13: ; preds = %BB3 - %14 = tail call i64 @sorbet_rb_int_minus_slowpath(i64 %rawArg_n, i64 %1) #16, !dbg !41, !noalias !42 - br label %sorbet_rb_int_minus.exit, !dbg !41 - -sorbet_rb_int_minus.exit: ; preds = %9, %4, %13 - %15 = phi i64 [ %14, %13 ], [ %12, %9 ], [ %8, %4 ], !dbg !41 - %16 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !41, !tbaa !14 - %17 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %16, i64 0, i32 5, !dbg !41 - %18 = load i32, i32* %17, align 8, !dbg !41, !tbaa !25 - %19 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %16, i64 0, i32 6, !dbg !41 - %20 = load i32, i32* %19, align 4, !dbg !41, !tbaa !26 - %21 = xor i32 %20, -1, !dbg !41 - %22 = and i32 %21, %18, !dbg !41 - %23 = icmp eq i32 %22, 0, !dbg !41 - br i1 %23, label %rb_vm_check_ints.exit, label %24, !dbg !41, !prof !27 - -24: ; preds = %sorbet_rb_int_minus.exit - %25 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %16, i64 0, i32 8, !dbg !41 - %26 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %25, align 8, !dbg !41, !tbaa !28 - %27 = tail call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %26, i32 noundef 0) #16, !dbg !41 - br label %rb_vm_check_ints.exit, !dbg !41 - -rb_vm_check_ints.exit: ; preds = %sorbet_rb_int_minus.exit, %24 - %28 = load i64, i64* @guard_epoch_HasFib, align 8, !dbg !45 - %29 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !45, !tbaa !30 - %needTakeSlowPath = icmp ne i64 %28, %29, !dbg !45 - br i1 %needTakeSlowPath, label %30, label %31, !dbg !45, !prof !32 - -30: ; preds = %rb_vm_check_ints.exit - tail call void @const_recompute_HasFib(), !dbg !45 - br label %31, !dbg !45 - -31: ; preds = %rb_vm_check_ints.exit, %30 - %32 = load i64, i64* @guarded_const_HasFib, align 8, !dbg !45 - %33 = load i64, i64* @guard_epoch_HasFib, align 8, !dbg !45 - %34 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !45, !tbaa !30 - %guardUpdated = icmp eq i64 %33, %34, !dbg !45 - tail call void @llvm.assume(i1 %guardUpdated), !dbg !45 - %35 = icmp eq i64 %selfRaw, %32, !dbg !45 - br i1 %35, label %fastFinalCall_fib, label %36, !dbg !45 - -36: ; preds = %31 - %37 = load i64, i64* @rb_cModule, align 8, !dbg !45, !tbaa !6 - %38 = tail call i64 @rb_obj_is_kind_of(i64 %selfRaw, i64 %37), !dbg !45 - %39 = icmp eq i64 %38, 0, !dbg !45 - br i1 %39, label %slowFinalCall_fib, label %sorbet_isa_class_of.exit, !dbg !45, !prof !46 - -sorbet_isa_class_of.exit: ; preds = %36 - %40 = tail call i64 @rb_class_inherited_p(i64 %selfRaw, i64 %32) #19, !dbg !45 - %41 = icmp ne i64 %40, 0, !dbg !45 - br i1 %41, label %fastFinalCall_fib, label %slowFinalCall_fib, !dbg !45, !prof !27 - -BB4: ; preds = %183, %sorbet_rb_int_plus.exit, %"alternativeCallIntrinsic_Integer_+" - %".sroa.0.0" = phi i64 [ %send107, %"alternativeCallIntrinsic_Integer_+" ], [ %174, %sorbet_rb_int_plus.exit ], [ %174, %183 ], !dbg !47 - store i64* getelementptr inbounds ([18 x i64], [18 x i64]* @iseqEncodedArray, i64 0, i64 13), i64** %0, align 8, !tbaa !14 - %42 = and i64 %".sroa.0.0", 1 - %43 = icmp eq i64 %42, 0 - br i1 %43, label %44, label %typeTestSuccess77, !prof !48 - -44: ; preds = %BB4 - %45 = and i64 %".sroa.0.0", 7 - %46 = icmp ne i64 %45, 0 - %47 = and i64 %".sroa.0.0", -9 - %48 = icmp eq i64 %47, 0 - %49 = or i1 %46, %48 - br i1 %49, label %codeRepl, label %sorbet_isa_Integer.exit - -sorbet_isa_Integer.exit: ; preds = %44 - %50 = inttoptr i64 %".sroa.0.0" to %struct.iseq_inline_iv_cache_entry* - %51 = getelementptr inbounds %struct.iseq_inline_iv_cache_entry, %struct.iseq_inline_iv_cache_entry* %50, i64 0, i32 0 - %52 = load i64, i64* %51, align 8, !tbaa !49 - %53 = and i64 %52, 31 - %54 = icmp eq i64 %53, 10 - br i1 %54, label %typeTestSuccess77, label %codeRepl, !prof !27 - -argCountFailBlock: ; preds = %functionEntryInitializers - tail call void @sorbet_raiseArity(i32 %argc, i32 noundef 1, i32 noundef 1) #20, !dbg !39 - unreachable, !dbg !39 - -fillRequiredArgs: ; preds = %functionEntryInitializers - %rawArg_n = load i64, i64* %argArray, align 8, !dbg !39 - %55 = and i64 %rawArg_n, 1, !dbg !51 - %56 = icmp eq i64 %55, 0, !dbg !51 - br i1 %56, label %57, label %typeTestSuccess, !dbg !51, !prof !48 - -57: ; preds = %fillRequiredArgs - %58 = and i64 %rawArg_n, 7, !dbg !51 - %59 = icmp ne i64 %58, 0, !dbg !51 - %60 = and i64 %rawArg_n, -9, !dbg !51 - %61 = icmp eq i64 %60, 0, !dbg !51 - %62 = or i1 %59, %61, !dbg !51 - br i1 %62, label %codeRepl103, label %sorbet_isa_Integer.exit109, !dbg !51 - -sorbet_isa_Integer.exit109: ; preds = %57 - %63 = inttoptr i64 %rawArg_n to %struct.iseq_inline_iv_cache_entry*, !dbg !51 - %64 = getelementptr inbounds %struct.iseq_inline_iv_cache_entry, %struct.iseq_inline_iv_cache_entry* %63, i64 0, i32 0, !dbg !51 - %65 = load i64, i64* %64, align 8, !dbg !51, !tbaa !49 - %66 = and i64 %65, 31, !dbg !51 - %67 = icmp eq i64 %66, 10, !dbg !51 - br i1 %67, label %typeTestSuccess, label %codeRepl103, !dbg !51, !prof !27 - -typeTestSuccess: ; preds = %fillRequiredArgs, %sorbet_isa_Integer.exit109 - store i64* getelementptr inbounds ([18 x i64], [18 x i64]* @iseqEncodedArray, i64 0, i64 8), i64** %0, align 8, !dbg !51, !tbaa !14 - %callArgs0Addr = getelementptr [2 x i64], [2 x i64]* %callArgs, i32 0, i64 0, !dbg !52 - store i64 7, i64* %callArgs0Addr, align 8, !dbg !52 - %68 = getelementptr [2 x i64], [2 x i64]* %callArgs, i64 0, i64 0, !dbg !52 - tail call void @llvm.experimental.noalias.scope.decl(metadata !53), !dbg !52 - %69 = load i64, i64* %68, align 8, !dbg !52, !tbaa !6, !alias.scope !53 - %70 = and i64 %rawArg_n, 1, !dbg !52 - %71 = and i64 %69, %70, !dbg !52 - %72 = icmp eq i64 %71, 0, !dbg !52 - br i1 %72, label %78, label %73, !dbg !52, !prof !40 - -73: ; preds = %typeTestSuccess - %74 = ashr i64 %rawArg_n, 1, !dbg !52 - %75 = ashr i64 %69, 1, !dbg !52 - %76 = icmp slt i64 %74, %75, !dbg !52 - %77 = select i1 %76, i64 20, i64 0, !dbg !52 - br label %sorbet_rb_int_lt.exit, !dbg !52 - -78: ; preds = %typeTestSuccess - %79 = tail call i64 @sorbet_rb_int_lt_slowpath(i64 %rawArg_n, i64 %69) #16, !dbg !52, !noalias !53 - br label %sorbet_rb_int_lt.exit, !dbg !52 - -sorbet_rb_int_lt.exit: ; preds = %78, %73 - %rawSendResult95 = phi i64 [ %77, %73 ], [ %79, %78 ] - %80 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !52, !tbaa !14 - %81 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %80, i64 0, i32 5, !dbg !52 - %82 = load i32, i32* %81, align 8, !dbg !52, !tbaa !25 - %83 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %80, i64 0, i32 6, !dbg !52 - %84 = load i32, i32* %83, align 4, !dbg !52, !tbaa !26 - %85 = xor i32 %84, -1, !dbg !52 - %86 = and i32 %85, %82, !dbg !52 - %87 = icmp eq i32 %86, 0, !dbg !52 - br i1 %87, label %rb_vm_check_ints.exit98, label %88, !dbg !52, !prof !27 - -88: ; preds = %sorbet_rb_int_lt.exit - %89 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %80, i64 0, i32 8, !dbg !52 - %90 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %89, align 8, !dbg !52, !tbaa !28 - %91 = tail call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %90, i32 noundef 0) #16, !dbg !52 - br label %rb_vm_check_ints.exit98, !dbg !52 - -rb_vm_check_ints.exit98: ; preds = %sorbet_rb_int_lt.exit, %88 - %92 = and i64 %rawSendResult95, -9, !dbg !52 - %93 = icmp ne i64 %92, 0, !dbg !52 - br i1 %93, label %BB4.thread, label %BB3, !dbg !52 - -BB4.thread: ; preds = %rb_vm_check_ints.exit98 - store i64* getelementptr inbounds ([18 x i64], [18 x i64]* @iseqEncodedArray, i64 0, i64 13), i64** %0, align 8, !tbaa !14 - br label %typeTestSuccess77 - -codeRepl103: ; preds = %57, %sorbet_isa_Integer.exit109 - tail call fastcc void @func_HasFib.3fib.cold.2(i64 %rawArg_n) #21, !dbg !51 - unreachable - -fastFinalCall_fib: ; preds = %31, %sorbet_isa_class_of.exit - store i64 %15, i64* %callArgs0Addr, align 8, !dbg !45 - %94 = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @stackFramePrecomputed_func_HasFib.3fib, align 8, !dbg !45 - %95 = load %struct.rb_callable_method_entry_struct*, %struct.rb_callable_method_entry_struct** getelementptr inbounds (%struct.FunctionInlineCache, %struct.FunctionInlineCache* @ic_fib.3, i64 0, i32 0, i32 0, i32 2), align 16, !dbg !45, !tbaa !56 - %96 = icmp eq %struct.rb_callable_method_entry_struct* %95, null, !dbg !45 - br i1 %96, label %97, label %sorbet_callFuncDirect.exit96, !dbg !45, !prof !48 - -97: ; preds = %fastFinalCall_fib - tail call void @sorbet_vmMethodSearch(%struct.FunctionInlineCache* noundef @ic_fib.3, i64 %selfRaw) #16, !dbg !45 - br label %sorbet_callFuncDirect.exit96, !dbg !45 - -sorbet_callFuncDirect.exit96: ; preds = %fastFinalCall_fib, %97 - %98 = tail call %struct.rb_control_frame_struct* @sorbet_pushCfuncFrame(%struct.FunctionInlineCache* noundef @ic_fib.3, i64 %selfRaw, %struct.rb_iseq_struct* %94) #16, !dbg !45 - %99 = call i64 @func_HasFib.3fib(i32 noundef 1, i64* nocapture noundef nonnull readonly align 8 dereferenceable(16) %68, i64 %selfRaw, %struct.rb_control_frame_struct* align 8 %98, i8* noalias nocapture nofree nonnull readnone align 16 dereferenceable(88) undef) #16, !dbg !45 - tail call void @sorbet_popFrame() #16, !dbg !45 - br label %afterFinalCall_fib, !dbg !45 - -slowFinalCall_fib: ; preds = %36, %sorbet_isa_class_of.exit - %100 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 1, !dbg !45 - %101 = load i64*, i64** %100, align 8, !dbg !45 - store i64 %selfRaw, i64* %101, align 8, !dbg !45, !tbaa !6 - %102 = getelementptr inbounds i64, i64* %101, i64 1, !dbg !45 - store i64 %15, i64* %102, align 8, !dbg !45, !tbaa !6 - %103 = getelementptr inbounds i64, i64* %102, i64 1, !dbg !45 - store i64* %103, i64** %100, align 8, !dbg !45 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_fib.4, i64 0), !dbg !45 - br label %afterFinalCall_fib, !dbg !45 - -afterFinalCall_fib: ; preds = %slowFinalCall_fib, %sorbet_callFuncDirect.exit96 - %104 = phi i1 [ true, %sorbet_callFuncDirect.exit96 ], [ false, %slowFinalCall_fib ] - %finalCallResult_fib = phi i64 [ %99, %sorbet_callFuncDirect.exit96 ], [ %send, %slowFinalCall_fib ], !dbg !45 - store i64 5, i64* %callArgs0Addr, align 8, !dbg !62 - tail call void @llvm.experimental.noalias.scope.decl(metadata !63), !dbg !62 - %105 = load i64, i64* %68, align 8, !dbg !62, !tbaa !6, !alias.scope !63 - %106 = and i64 %105, %70, !dbg !62 - %107 = icmp eq i64 %106, 0, !dbg !62 - br i1 %107, label %117, label %108, !dbg !62, !prof !40 - -108: ; preds = %afterFinalCall_fib - %109 = add nsw i64 %105, -1, !dbg !62 - %110 = tail call { i64, i1 } @llvm.ssub.with.overflow.i64(i64 %rawArg_n, i64 %109) #18, !dbg !62 - %111 = extractvalue { i64, i1 } %110, 1, !dbg !62 - %112 = extractvalue { i64, i1 } %110, 0, !dbg !62 - br i1 %111, label %113, label %sorbet_rb_int_minus.exit97, !dbg !62 - -113: ; preds = %108 - %114 = ashr i64 %112, 1, !dbg !62 - %115 = xor i64 %114, -9223372036854775808, !dbg !62 - %116 = tail call i64 @rb_int2big(i64 %115) #16, !dbg !62, !noalias !63 - br label %sorbet_rb_int_minus.exit97, !dbg !62 - -117: ; preds = %afterFinalCall_fib - %118 = tail call i64 @sorbet_rb_int_minus_slowpath(i64 %rawArg_n, i64 %105) #16, !dbg !62, !noalias !63 - br label %sorbet_rb_int_minus.exit97, !dbg !62 - -sorbet_rb_int_minus.exit97: ; preds = %113, %108, %117 - %119 = phi i64 [ %118, %117 ], [ %116, %113 ], [ %112, %108 ], !dbg !62 - %120 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !62, !tbaa !14 - %121 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %120, i64 0, i32 5, !dbg !62 - %122 = load i32, i32* %121, align 8, !dbg !62, !tbaa !25 - %123 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %120, i64 0, i32 6, !dbg !62 - %124 = load i32, i32* %123, align 4, !dbg !62, !tbaa !26 - %125 = xor i32 %124, -1, !dbg !62 - %126 = and i32 %125, %122, !dbg !62 - %127 = icmp eq i32 %126, 0, !dbg !62 - br i1 %127, label %rb_vm_check_ints.exit101, label %128, !dbg !62, !prof !27 - -128: ; preds = %sorbet_rb_int_minus.exit97 - %129 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %120, i64 0, i32 8, !dbg !62 - %130 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %129, align 8, !dbg !62, !tbaa !28 - %131 = tail call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %130, i32 noundef 0) #16, !dbg !62 - br label %rb_vm_check_ints.exit101, !dbg !62 - -rb_vm_check_ints.exit101: ; preds = %sorbet_rb_int_minus.exit97, %128 - br i1 %104, label %fastFinalCall_fib59, label %slowFinalCall_fib60, !dbg !66 - -fastFinalCall_fib59: ; preds = %rb_vm_check_ints.exit101 - store i64 %119, i64* %callArgs0Addr, align 8, !dbg !66 - %132 = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @stackFramePrecomputed_func_HasFib.3fib, align 8, !dbg !66 - %133 = load %struct.rb_callable_method_entry_struct*, %struct.rb_callable_method_entry_struct** getelementptr inbounds (%struct.FunctionInlineCache, %struct.FunctionInlineCache* @ic_fib.6, i64 0, i32 0, i32 0, i32 2), align 16, !dbg !66, !tbaa !56 - %134 = icmp eq %struct.rb_callable_method_entry_struct* %133, null, !dbg !66 - br i1 %134, label %135, label %sorbet_callFuncDirect.exit, !dbg !66, !prof !48 - -135: ; preds = %fastFinalCall_fib59 - tail call void @sorbet_vmMethodSearch(%struct.FunctionInlineCache* noundef @ic_fib.6, i64 %selfRaw) #16, !dbg !66 - br label %sorbet_callFuncDirect.exit, !dbg !66 - -sorbet_callFuncDirect.exit: ; preds = %fastFinalCall_fib59, %135 - %136 = tail call %struct.rb_control_frame_struct* @sorbet_pushCfuncFrame(%struct.FunctionInlineCache* noundef @ic_fib.6, i64 %selfRaw, %struct.rb_iseq_struct* %132) #16, !dbg !66 - %137 = call i64 @func_HasFib.3fib(i32 noundef 1, i64* nocapture noundef nonnull readonly align 8 dereferenceable(16) %68, i64 %selfRaw, %struct.rb_control_frame_struct* align 8 %136, i8* noalias nocapture nofree nonnull readnone align 16 dereferenceable(88) undef) #16, !dbg !66 - tail call void @sorbet_popFrame() #16, !dbg !66 - br label %afterFinalCall_fib61, !dbg !66 - -slowFinalCall_fib60: ; preds = %rb_vm_check_ints.exit101 - %138 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 1, !dbg !66 - %139 = load i64*, i64** %138, align 8, !dbg !66 - store i64 %selfRaw, i64* %139, align 8, !dbg !66, !tbaa !6 - %140 = getelementptr inbounds i64, i64* %139, i64 1, !dbg !66 - store i64 %119, i64* %140, align 8, !dbg !66, !tbaa !6 - %141 = getelementptr inbounds i64, i64* %140, i64 1, !dbg !66 - store i64* %141, i64** %138, align 8, !dbg !66 - %send105 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_fib.7, i64 0), !dbg !66 - br label %afterFinalCall_fib61, !dbg !66 - -afterFinalCall_fib61: ; preds = %slowFinalCall_fib60, %sorbet_callFuncDirect.exit - %finalCallResult_fib66 = phi i64 [ %137, %sorbet_callFuncDirect.exit ], [ %send105, %slowFinalCall_fib60 ], !dbg !66 - %142 = and i64 %finalCallResult_fib, 1, !dbg !45 - %143 = icmp eq i64 %142, 0, !dbg !45 - br i1 %143, label %144, label %"fastSymCallIntrinsic_Integer_+", !dbg !45, !prof !48 - -144: ; preds = %afterFinalCall_fib61 - %145 = and i64 %finalCallResult_fib, 7, !dbg !45 - %146 = icmp ne i64 %145, 0, !dbg !45 - %147 = and i64 %finalCallResult_fib, -9, !dbg !45 - %148 = icmp eq i64 %147, 0, !dbg !45 - %149 = or i1 %146, %148, !dbg !45 - br i1 %149, label %"alternativeCallIntrinsic_Integer_+", label %sorbet_isa_Integer.exit108, !dbg !45 - -sorbet_isa_Integer.exit108: ; preds = %144 - %150 = inttoptr i64 %finalCallResult_fib to %struct.iseq_inline_iv_cache_entry*, !dbg !45 - %151 = getelementptr inbounds %struct.iseq_inline_iv_cache_entry, %struct.iseq_inline_iv_cache_entry* %150, i64 0, i32 0, !dbg !45 - %152 = load i64, i64* %151, align 8, !dbg !45, !tbaa !49 - %153 = and i64 %152, 31, !dbg !45 - %154 = icmp eq i64 %153, 10, !dbg !45 - br i1 %154, label %"fastSymCallIntrinsic_Integer_+", label %"alternativeCallIntrinsic_Integer_+", !dbg !45, !prof !27 - -"alternativeCallIntrinsic_Integer_+": ; preds = %144, %sorbet_isa_Integer.exit108 - %155 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 1, !dbg !45 - %156 = load i64*, i64** %155, align 8, !dbg !45 - store i64 %finalCallResult_fib, i64* %156, align 8, !dbg !45, !tbaa !6 - %157 = getelementptr inbounds i64, i64* %156, i64 1, !dbg !45 - store i64 %finalCallResult_fib66, i64* %157, align 8, !dbg !45, !tbaa !6 - %158 = getelementptr inbounds i64, i64* %157, i64 1, !dbg !45 - store i64* %158, i64** %155, align 8, !dbg !45 - %send107 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @"ic_+", i64 0), !dbg !45 - br label %BB4, !dbg !45 - -"fastSymCallIntrinsic_Integer_+": ; preds = %afterFinalCall_fib61, %sorbet_isa_Integer.exit108 - store i64 %finalCallResult_fib66, i64* %callArgs0Addr, align 8, !dbg !45 - tail call void @llvm.experimental.noalias.scope.decl(metadata !67), !dbg !45 - %159 = load i64, i64* %68, align 8, !dbg !45, !tbaa !6, !alias.scope !67 - %160 = and i64 %finalCallResult_fib, 1, !dbg !45 - %161 = and i64 %160, %159, !dbg !45 - %162 = icmp eq i64 %161, 0, !dbg !45 - br i1 %162, label %172, label %163, !dbg !45, !prof !40 - -163: ; preds = %"fastSymCallIntrinsic_Integer_+" - %164 = add nsw i64 %159, -1, !dbg !45 - %165 = tail call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %finalCallResult_fib, i64 %164) #18, !dbg !45 - %166 = extractvalue { i64, i1 } %165, 1, !dbg !45 - %167 = extractvalue { i64, i1 } %165, 0, !dbg !45 - br i1 %166, label %168, label %sorbet_rb_int_plus.exit, !dbg !45 - -168: ; preds = %163 - %169 = ashr i64 %167, 1, !dbg !45 - %170 = xor i64 %169, -9223372036854775808, !dbg !45 - %171 = tail call i64 @rb_int2big(i64 %170) #16, !dbg !45, !noalias !67 - br label %sorbet_rb_int_plus.exit, !dbg !45 - -172: ; preds = %"fastSymCallIntrinsic_Integer_+" - %173 = tail call i64 @sorbet_rb_int_plus_slowpath(i64 %finalCallResult_fib, i64 %159) #16, !dbg !45, !noalias !67 - br label %sorbet_rb_int_plus.exit, !dbg !45 - -sorbet_rb_int_plus.exit: ; preds = %168, %163, %172 - %174 = phi i64 [ %173, %172 ], [ %171, %168 ], [ %167, %163 ], !dbg !45 - %175 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !45, !tbaa !14 - %176 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %175, i64 0, i32 5, !dbg !45 - %177 = load i32, i32* %176, align 8, !dbg !45, !tbaa !25 - %178 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %175, i64 0, i32 6, !dbg !45 - %179 = load i32, i32* %178, align 4, !dbg !45, !tbaa !26 - %180 = xor i32 %179, -1, !dbg !45 - %181 = and i32 %180, %177, !dbg !45 - %182 = icmp eq i32 %181, 0, !dbg !45 - br i1 %182, label %BB4, label %183, !dbg !45, !prof !27 - -183: ; preds = %sorbet_rb_int_plus.exit - %184 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %175, i64 0, i32 8, !dbg !45 - %185 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %184, align 8, !dbg !45, !tbaa !28 - %186 = tail call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %185, i32 noundef 0) #16, !dbg !45 - br label %BB4, !dbg !45 - -typeTestSuccess77: ; preds = %BB4.thread, %BB4, %sorbet_isa_Integer.exit - %".sroa.0.0111" = phi i64 [ 3, %BB4.thread ], [ %".sroa.0.0", %BB4 ], [ %".sroa.0.0", %sorbet_isa_Integer.exit ] - ret i64 %".sroa.0.0111" - -codeRepl: ; preds = %44, %sorbet_isa_Integer.exit - %".sroa.0.0112" = phi i64 [ %".sroa.0.0", %44 ], [ %".sroa.0.0", %sorbet_isa_Integer.exit ] - tail call fastcc void @func_HasFib.3fib.cold.1(i64 %".sroa.0.0112") #21, !dbg !47 - unreachable -} - -; Function Attrs: sspreq -define void @Init_app_fib() local_unnamed_addr #9 { -entry: - %callArgs.i = alloca [2 x i64], align 8 - %locals.i23.i = alloca i64, i32 0, align 8 - %locals.i21.i = alloca i64, i32 0, align 8 - %locals.i.i = alloca i64, i32 0, align 8 - %keywords.i = alloca i64, align 8, !dbg !70 - %realpath = tail call i64 @sorbet_readRealpath() - %0 = bitcast i64* %keywords.i to i8* - call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %0) - %1 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([17 x i8], [17 x i8]* @"str_", i64 0, i64 0), i64 noundef 16) #16 - store i64 %1, i64* @"rubyIdPrecomputed_", align 8 - %2 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([4 x i8], [4 x i8]* @str_fib, i64 0, i64 0), i64 noundef 3) #16 - store i64 %2, i64* @rubyIdPrecomputed_fib, align 8 - %3 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([2 x i8], [2 x i8]* @"str_<", i64 0, i64 0), i64 noundef 1) #16 - %4 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([2 x i8], [2 x i8]* @str_-, i64 0, i64 0), i64 noundef 1) #16 - %5 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([2 x i8], [2 x i8]* @"str_+", i64 0, i64 0), i64 noundef 1) #16 - store i64 %5, i64* @"rubyIdPrecomputed_+", align 8 - %6 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([15 x i8], [15 x i8]* @"str_", i64 0, i64 0), i64 noundef 14) #16 - store i64 %6, i64* @"rubyIdPrecomputed_", align 8 - %7 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([24 x i8], [24 x i8]* @"str_block in ", i64 0, i64 0), i64 noundef 23) #16 - store i64 %7, i64* @"rubyIdPrecomputed_block in ", align 8 - %8 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([6 x i8], [6 x i8]* @str_final, i64 0, i64 0), i64 noundef 5) #16 - %9 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([2 x i8], [2 x i8]* @str_n, i64 0, i64 0), i64 noundef 1) #16 - store i64 %9, i64* @rubyIdPrecomputed_n, align 8 - %10 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([7 x i8], [7 x i8]* @str_params, i64 0, i64 0), i64 noundef 6) #16 - store i64 %10, i64* @rubyIdPrecomputed_params, align 8 - %11 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([8 x i8], [8 x i8]* @str_returns, i64 0, i64 0), i64 noundef 7) #16 - store i64 %11, i64* @rubyIdPrecomputed_returns, align 8 - %12 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([7 x i8], [7 x i8]* @str_normal, i64 0, i64 0), i64 noundef 6) #16 - %13 = tail call i64 @sorbet_vm_fstring_new(i8* noundef getelementptr inbounds ([17 x i8], [17 x i8]* @"str_", i64 0, i64 0), i64 noundef 16) #16 - tail call void @rb_gc_register_mark_object(i64 %13) #16 - store i64 %13, i64* @"rubyStrFrozen_", align 8 - %14 = tail call i64 @sorbet_vm_fstring_new(i8* noundef getelementptr inbounds ([40 x i8], [40 x i8]* @"str_test/testdata/ruby_benchmark/app_fib.rb", i64 0, i64 0), i64 noundef 39) #16 - tail call void @rb_gc_register_mark_object(i64 %14) #16 - store i64 %14, i64* @"rubyStrFrozen_test/testdata/ruby_benchmark/app_fib.rb", align 8 - tail call void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef getelementptr inbounds ([18 x i64], [18 x i64]* @iseqEncodedArray, i32 0, i32 0), i32 noundef 18) - %"rubyId_.i.i" = load i64, i64* @"rubyIdPrecomputed_", align 8 - %"rubyStr_.i.i" = load i64, i64* @"rubyStrFrozen_", align 8 - %"rubyStr_test/testdata/ruby_benchmark/app_fib.rb.i.i" = load i64, i64* @"rubyStrFrozen_test/testdata/ruby_benchmark/app_fib.rb", align 8 - %15 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_.i.i", i64 %"rubyId_.i.i", i64 %"rubyStr_test/testdata/ruby_benchmark/app_fib.rb.i.i", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 0, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals.i.i, i32 noundef 0, i32 noundef 2) - store %struct.rb_iseq_struct* %15, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.17$152", align 8 - %rubyId_fib1.i = load i64, i64* @rubyIdPrecomputed_fib, align 8, !dbg !72 - call void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_fib.1, i64 %rubyId_fib1.i, i32 noundef 16, i32 noundef 0, i32 noundef 0, i64* noundef null), !dbg !72 - %rubyId_fib2.i = load i64, i64* @rubyIdPrecomputed_fib, align 8, !dbg !72 - call void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_fib.2, i64 %rubyId_fib2.i, i32 noundef 16, i32 noundef 1, i32 noundef 0, i64* noundef null), !dbg !72 - %16 = call i64 @sorbet_vm_fstring_new(i8* noundef getelementptr inbounds ([4 x i8], [4 x i8]* @str_fib, i64 0, i64 0), i64 noundef 3) #16 - call void @rb_gc_register_mark_object(i64 %16) #16 - %rubyId_fib.i.i = load i64, i64* @rubyIdPrecomputed_fib, align 8 - %"rubyStr_test/testdata/ruby_benchmark/app_fib.rb.i20.i" = load i64, i64* @"rubyStrFrozen_test/testdata/ruby_benchmark/app_fib.rb", align 8 - %17 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %16, i64 %rubyId_fib.i.i, i64 %"rubyStr_test/testdata/ruby_benchmark/app_fib.rb.i20.i", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 1, i32 noundef 7, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals.i21.i, i32 noundef 0, i32 noundef 2) - store %struct.rb_iseq_struct* %17, %struct.rb_iseq_struct** @stackFramePrecomputed_func_HasFib.3fib, align 8 - %rubyId_fib6.i = load i64, i64* @rubyIdPrecomputed_fib, align 8, !dbg !45 - call void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_fib.3, i64 %rubyId_fib6.i, i32 noundef 4, i32 noundef 0, i32 noundef 0, i64* noundef null), !dbg !45 - %rubyId_fib7.i = load i64, i64* @rubyIdPrecomputed_fib, align 8, !dbg !45 - call void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_fib.4, i64 %rubyId_fib7.i, i32 noundef 20, i32 noundef 1, i32 noundef 0, i64* noundef null), !dbg !45 - %rubyId_fib12.i = load i64, i64* @rubyIdPrecomputed_fib, align 8, !dbg !66 - call void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_fib.6, i64 %rubyId_fib12.i, i32 noundef 4, i32 noundef 0, i32 noundef 0, i64* noundef null), !dbg !66 - %rubyId_fib14.i = load i64, i64* @rubyIdPrecomputed_fib, align 8, !dbg !66 - call void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_fib.7, i64 %rubyId_fib14.i, i32 noundef 20, i32 noundef 1, i32 noundef 0, i64* noundef null), !dbg !66 - %"rubyId_+.i" = load i64, i64* @"rubyIdPrecomputed_+", align 8, !dbg !45 - call void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @"ic_+", i64 %"rubyId_+.i", i32 noundef 16, i32 noundef 1, i32 noundef 0, i64* noundef null), !dbg !45 - %18 = call i64 @sorbet_vm_fstring_new(i8* noundef getelementptr inbounds ([15 x i8], [15 x i8]* @"str_", i64 0, i64 0), i64 noundef 14) #16 - call void @rb_gc_register_mark_object(i64 %18) #16 - %"rubyId_.i.i" = load i64, i64* @"rubyIdPrecomputed_", align 8 - %"rubyStr_test/testdata/ruby_benchmark/app_fib.rb.i22.i" = load i64, i64* @"rubyStrFrozen_test/testdata/ruby_benchmark/app_fib.rb", align 8 - %19 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %18, i64 %"rubyId_.i.i", i64 %"rubyStr_test/testdata/ruby_benchmark/app_fib.rb.i22.i", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 3, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals.i23.i, i32 noundef 0, i32 noundef 4) - store %struct.rb_iseq_struct* %19, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_HasFib.13", align 8 - %20 = call i64 @sorbet_vm_fstring_new(i8* noundef getelementptr inbounds ([24 x i8], [24 x i8]* @"str_block in ", i64 0, i64 0), i64 noundef 23) #16 - call void @rb_gc_register_mark_object(i64 %20) #16 - %stackFrame.i.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_HasFib.13", align 8 - %"rubyId_block in .i.i" = load i64, i64* @"rubyIdPrecomputed_block in ", align 8 - %"rubyStr_test/testdata/ruby_benchmark/app_fib.rb.i24.i" = load i64, i64* @"rubyStrFrozen_test/testdata/ruby_benchmark/app_fib.rb", align 8 - %21 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %20, i64 %"rubyId_block in .i.i", i64 %"rubyStr_test/testdata/ruby_benchmark/app_fib.rb.i24.i", i64 %realpath, %struct.rb_iseq_struct* %stackFrame.i.i, i32 noundef 2, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef null, i32 noundef 0, i32 noundef 4) - store %struct.rb_iseq_struct* %21, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_HasFib.13$block_1", align 8 - %rubyId_params.i = load i64, i64* @rubyIdPrecomputed_params, align 8, !dbg !70 - %rubyId_n.i = load i64, i64* @rubyIdPrecomputed_n, align 8, !dbg !70 - %22 = call i64 @rb_id2sym(i64 %rubyId_n.i) #19, !dbg !70 - store i64 %22, i64* %keywords.i, align 8, !dbg !70 - call void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_params, i64 %rubyId_params.i, i32 noundef 68, i32 noundef 1, i32 noundef 1, i64* noundef nonnull align 8 %keywords.i), !dbg !70 - %rubyId_returns.i = load i64, i64* @rubyIdPrecomputed_returns, align 8, !dbg !70 - call void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_returns, i64 %rubyId_returns.i, i32 noundef 16, i32 noundef 1, i32 noundef 0, i64* noundef null), !dbg !70 - call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %0) - %23 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !14 - %24 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %23, i64 0, i32 2 - %25 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %24, align 8, !tbaa !16 - %26 = bitcast [2 x i64]* %callArgs.i to i8* - call void @llvm.lifetime.start.p0i8(i64 16, i8* nonnull %26) - %stackFrame.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.17$152", align 8 - %27 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %25, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame.i, %struct.rb_iseq_struct** %27, align 8, !tbaa !20 - %28 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %25, i64 0, i32 4 - %29 = load i64*, i64** %28, align 8, !tbaa !22 - %30 = load i64, i64* %29, align 8, !tbaa !6 - %31 = and i64 %30, -33 - store i64 %31, i64* %29, align 8, !tbaa !6 - call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %23, %struct.rb_control_frame_struct* %25, %struct.rb_iseq_struct* %stackFrame.i) #16 - %32 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %25, i64 0, i32 0 - store i64* getelementptr inbounds ([18 x i64], [18 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %32, align 8, !dbg !74, !tbaa !14 - %33 = load i64, i64* @rb_cObject, align 8, !dbg !75 - %34 = call i64 @rb_define_class(i8* noundef getelementptr inbounds ([7 x i8], [7 x i8]* @str_HasFib, i64 0, i64 0), i64 %33) #16, !dbg !75 - %35 = call %struct.rb_control_frame_struct* @sorbet_pushStaticInitFrame(i64 %34) #16, !dbg !75 - call fastcc void @"func_HasFib.13L64"(%struct.rb_control_frame_struct* nocapture writeonly %35) #16, !dbg !75 - call void @sorbet_popFrame() #16, !dbg !75 - store i64* getelementptr inbounds ([18 x i64], [18 x i64]* @iseqEncodedArray, i64 0, i64 16), i64** %32, align 8, !dbg !75, !tbaa !14 - %36 = load i64, i64* @guard_epoch_HasFib, align 8, !dbg !72 - %37 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !72, !tbaa !30 - %needTakeSlowPath = icmp ne i64 %36, %37, !dbg !72 - br i1 %needTakeSlowPath, label %38, label %39, !dbg !72, !prof !32 - -38: ; preds = %entry - call void @const_recompute_HasFib(), !dbg !72 - br label %39, !dbg !72 - -39: ; preds = %entry, %38 - %40 = load i64, i64* @guarded_const_HasFib, align 8, !dbg !72 - %41 = load i64, i64* @guard_epoch_HasFib, align 8, !dbg !72 - %42 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !72, !tbaa !30 - %guardUpdated = icmp eq i64 %41, %42, !dbg !72 - call void @llvm.assume(i1 %guardUpdated), !dbg !72 - %callArgs0Addr.i = getelementptr [2 x i64], [2 x i64]* %callArgs.i, i32 0, i64 0, !dbg !72 - store i64 69, i64* %callArgs0Addr.i, align 8, !dbg !72 - %43 = getelementptr [2 x i64], [2 x i64]* %callArgs.i, i64 0, i64 0, !dbg !72 - %44 = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @stackFramePrecomputed_func_HasFib.3fib, align 8, !dbg !72 - %45 = load %struct.rb_callable_method_entry_struct*, %struct.rb_callable_method_entry_struct** getelementptr inbounds (%struct.FunctionInlineCache, %struct.FunctionInlineCache* @ic_fib.1, i64 0, i32 0, i32 0, i32 2), align 16, !dbg !72, !tbaa !56 - %46 = icmp eq %struct.rb_callable_method_entry_struct* %45, null, !dbg !72 - br i1 %46, label %47, label %"func_.17$152.exit", !dbg !72, !prof !48 - -47: ; preds = %39 - call void @sorbet_vmMethodSearch(%struct.FunctionInlineCache* noundef @ic_fib.1, i64 %40) #16, !dbg !72 - br label %"func_.17$152.exit", !dbg !72 - -"func_.17$152.exit": ; preds = %39, %47 - %48 = call %struct.rb_control_frame_struct* @sorbet_pushCfuncFrame(%struct.FunctionInlineCache* noundef @ic_fib.1, i64 %40, %struct.rb_iseq_struct* %44) #16, !dbg !72 - %49 = call i64 @func_HasFib.3fib(i32 noundef 1, i64* nocapture noundef nonnull readonly align 8 dereferenceable(16) %43, i64 %40, %struct.rb_control_frame_struct* align 8 %48, i8* noalias nocapture nofree nonnull readnone align 16 dereferenceable(88) undef) #16, !dbg !72 - call void @sorbet_popFrame() #16, !dbg !72 - call void @llvm.lifetime.end.p0i8(i64 16, i8* nonnull %26) - ret void -} - -; Function Attrs: inaccessiblememonly nofree nosync nounwind willreturn -declare void @llvm.experimental.noalias.scope.decl(metadata) #10 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly -declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #11 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #6 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #6 - -; Function Attrs: cold minsize noreturn nounwind sspreq uwtable -define internal fastcc void @func_HasFib.3fib.cold.1(i64 %".sroa.0.0") unnamed_addr #12 !dbg !76 { -newFuncRoot: - tail call void @sorbet_cast_failure(i64 %".sroa.0.0", i8* noundef getelementptr inbounds ([13 x i8], [13 x i8]* @"str_Return value", i64 0, i64 0), i8* noundef getelementptr inbounds ([8 x i8], [8 x i8]* @str_Integer, i64 0, i64 0)) #20 - unreachable -} - -; Function Attrs: cold minsize noreturn nounwind sspreq uwtable -define internal fastcc void @func_HasFib.3fib.cold.2(i64 %rawArg_n) unnamed_addr #12 !dbg !78 { -newFuncRoot: - tail call void @sorbet_cast_failure(i64 %rawArg_n, i8* noundef getelementptr inbounds ([4 x i8], [4 x i8]* @str_sig, i64 0, i64 0), i8* noundef getelementptr inbounds ([8 x i8], [8 x i8]* @str_Integer, i64 0, i64 0)) #20, !dbg !79 - unreachable, !dbg !79 -} - -; Function Attrs: nofree nosync nounwind willreturn -declare void @llvm.assume(i1 noundef) #13 - -; Function Attrs: ssp -define linkonce void @const_recompute_HasFib() local_unnamed_addr #14 { - %1 = tail call i64 @sorbet_getConstant(i8* getelementptr inbounds ([7 x i8], [7 x i8]* @str_HasFib, i64 0, i64 0), i64 6) - store i64 %1, i64* @guarded_const_HasFib, align 8 - %2 = load i64, i64* @ruby_vm_global_constant_state, align 8, !tbaa !30 - store i64 %2, i64* @guard_epoch_HasFib, align 8 - ret void -} - -attributes #0 = { nounwind readnone willreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #1 = { cold noreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #2 = { noreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #3 = { "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #4 = { nofree nosync nounwind readnone speculatable willreturn } -attributes #5 = { allocsize(0,1) "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #6 = { argmemonly nofree nosync nounwind willreturn } -attributes #7 = { nounwind ssp uwtable "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #8 = { nounwind sspreq uwtable } -attributes #9 = { sspreq } -attributes #10 = { inaccessiblememonly nofree nosync nounwind willreturn } -attributes #11 = { argmemonly nofree nosync nounwind willreturn writeonly } -attributes #12 = { cold minsize noreturn nounwind sspreq uwtable } -attributes #13 = { nofree nosync nounwind willreturn } -attributes #14 = { ssp } -attributes #15 = { noreturn nounwind } -attributes #16 = { nounwind } -attributes #17 = { nounwind allocsize(0,1) } -attributes #18 = { nounwind willreturn } -attributes #19 = { nounwind readnone willreturn } -attributes #20 = { noreturn } -attributes #21 = { noinline } - -!llvm.module.flags = !{!0, !1, !2} -!llvm.dbg.cu = !{!3} - -!0 = !{i32 2, !"Debug Info Version", i32 3} -!1 = !{i32 4, !"cf-protection-return", i32 1} -!2 = !{i32 4, !"cf-protection-branch", i32 1} -!3 = distinct !DICompileUnit(language: DW_LANG_C, file: !4, producer: "Sorbet LLVM", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !5) -!4 = !DIFile(filename: "test/testdata/ruby_benchmark/app_fib.rb", directory: ".") -!5 = !{} -!6 = !{!7, !7, i64 0} -!7 = !{!"long", !8, i64 0} -!8 = !{!"omnipotent char", !9, i64 0} -!9 = !{!"Simple C/C++ TBAA"} -!10 = distinct !DISubprogram(name: "HasFib.", linkageName: "func_HasFib.13L64", scope: null, file: !4, line: 5, type: !11, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!11 = !DISubroutineType(types: !12) -!12 = !{!13} -!13 = !DIBasicType(name: "VALUE", size: 64, encoding: DW_ATE_signed) -!14 = !{!15, !15, i64 0} -!15 = !{!"any pointer", !8, i64 0} -!16 = !{!17, !15, i64 16} -!17 = !{!"rb_execution_context_struct", !15, i64 0, !7, i64 8, !15, i64 16, !15, i64 24, !15, i64 32, !18, i64 40, !18, i64 44, !15, i64 48, !15, i64 56, !15, i64 64, !7, i64 72, !7, i64 80, !15, i64 88, !7, i64 96, !15, i64 104, !15, i64 112, !7, i64 120, !7, i64 128, !8, i64 136, !8, i64 137, !7, i64 144, !19, i64 152} -!18 = !{!"int", !8, i64 0} -!19 = !{!"", !15, i64 0, !15, i64 8, !7, i64 16, !8, i64 24} -!20 = !{!21, !15, i64 16} -!21 = !{!"rb_control_frame_struct", !15, i64 0, !15, i64 8, !15, i64 16, !7, i64 24, !15, i64 32, !15, i64 40, !15, i64 48} -!22 = !{!21, !15, i64 32} -!23 = !DILocation(line: 0, scope: !10) -!24 = !DILocation(line: 6, column: 3, scope: !10) -!25 = !{!17, !18, i64 40} -!26 = !{!17, !18, i64 44} -!27 = !{!"branch_weights", i32 2000, i32 1} -!28 = !{!17, !15, i64 56} -!29 = !DILocation(line: 7, column: 3, scope: !10) -!30 = !{!31, !31, i64 0} -!31 = !{!"long long", !8, i64 0} -!32 = !{!"branch_weights", i32 1, i32 10000} -!33 = !{!34, !18, i64 8} -!34 = !{!"rb_sorbet_param_struct", !35, i64 0, !18, i64 4, !18, i64 8, !18, i64 12, !18, i64 16, !18, i64 20, !18, i64 24, !18, i64 28, !15, i64 32, !18, i64 40, !18, i64 44, !18, i64 48, !18, i64 52, !15, i64 56} -!35 = !{!"", !18, i64 0, !18, i64 0, !18, i64 0, !18, i64 0, !18, i64 0, !18, i64 0, !18, i64 0, !18, i64 0, !18, i64 1, !18, i64 1} -!36 = !{!34, !18, i64 4} -!37 = !{!34, !15, i64 32} -!38 = distinct !DISubprogram(name: "HasFib.fib", linkageName: "func_HasFib.3fib", scope: null, file: !4, line: 7, type: !11, scopeLine: 7, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!39 = !DILocation(line: 7, column: 3, scope: !38) -!40 = !{!"branch_weights", i32 4001, i32 4000000} -!41 = !DILocation(line: 11, column: 11, scope: !38) -!42 = !{!43} -!43 = distinct !{!43, !44, !"sorbet_rb_int_minus: argument 0"} -!44 = distinct !{!44, !"sorbet_rb_int_minus"} -!45 = !DILocation(line: 11, column: 7, scope: !38) -!46 = !{!"branch_weights", i32 1073205, i32 2146410443} -!47 = !DILocation(line: 0, scope: !38) -!48 = !{!"branch_weights", i32 1, i32 2000} -!49 = !{!50, !7, i64 0} -!50 = !{!"RBasic", !7, i64 0, !7, i64 8} -!51 = !DILocation(line: 7, column: 16, scope: !38) -!52 = !DILocation(line: 8, column: 8, scope: !38) -!53 = !{!54} -!54 = distinct !{!54, !55, !"sorbet_rb_int_lt: argument 0"} -!55 = distinct !{!55, !"sorbet_rb_int_lt"} -!56 = !{!57, !15, i64 32} -!57 = !{!"FunctionInlineCache", !58, i64 0} -!58 = !{!"rb_kwarg_call_data", !59, i64 0, !60, i64 64} -!59 = !{!"rb_call_cache", !31, i64 0, !8, i64 8, !15, i64 32, !7, i64 40, !15, i64 48, !8, i64 56} -!60 = !{!"rb_call_info_with_kwarg", !61, i64 0, !15, i64 16} -!61 = !{!"rb_call_info", !7, i64 0, !18, i64 8, !18, i64 12} -!62 = !DILocation(line: 11, column: 22, scope: !38) -!63 = !{!64} -!64 = distinct !{!64, !65, !"sorbet_rb_int_minus: argument 0"} -!65 = distinct !{!65, !"sorbet_rb_int_minus"} -!66 = !DILocation(line: 11, column: 18, scope: !38) -!67 = !{!68} -!68 = distinct !{!68, !69, !"sorbet_rb_int_plus: argument 0"} -!69 = distinct !{!69, !"sorbet_rb_int_plus"} -!70 = !DILocation(line: 6, column: 39, scope: !71) -!71 = distinct !DISubprogram(name: "HasFib.", linkageName: "func_HasFib.13L64$block_1", scope: !10, file: !4, line: 5, type: !11, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!72 = !DILocation(line: 16, column: 1, scope: !73) -!73 = distinct !DISubprogram(name: ".", linkageName: "func_.17$152", scope: null, file: !4, line: 5, type: !11, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!74 = !DILocation(line: 0, scope: !73) -!75 = !DILocation(line: 5, column: 1, scope: !73) -!76 = distinct !DISubprogram(name: "func_HasFib.3fib.cold.1", linkageName: "func_HasFib.3fib.cold.1", scope: null, file: !4, type: !77, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !3, retainedNodes: !5) -!77 = !DISubroutineType(types: !5) -!78 = distinct !DISubprogram(name: "func_HasFib.3fib.cold.2", linkageName: "func_HasFib.3fib.cold.2", scope: null, file: !4, type: !77, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !3, retainedNodes: !5) -!79 = !DILocation(line: 7, column: 16, scope: !78) diff --git a/test/testdata/ruby_benchmark/app_fib.rb b/test/testdata/ruby_benchmark/app_fib.rb deleted file mode 100644 index 6a184dfe7c..0000000000 --- a/test/testdata/ruby_benchmark/app_fib.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true -# typed: strict -# compiled: true - -class HasFib - T::Sig::WithoutRuntime.sig(:final) {params(n: Integer).returns(Integer)} - def self.fib(n) - if n < 3 - 1 - else - fib(n-1) + fib(n-2) - end - end -end - -HasFib.fib(34) - diff --git a/test/testdata/ruby_benchmark/app_mandelbrot.rb b/test/testdata/ruby_benchmark/app_mandelbrot.rb deleted file mode 100644 index a77eecab0b..0000000000 --- a/test/testdata/ruby_benchmark/app_mandelbrot.rb +++ /dev/null @@ -1,26 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -require 'complex' - -def mandelbrot? z - i = 0 - while i<100 - i += 1 - z = z * z - return false if z.abs > 2 - end - true -end - -ary = [] - -(0..1000).each{|dx| - (0..1000).each{|dy| - x = dx / 50.0 - y = dy / 50.0 - c = Complex(x, y) - ary << c if mandelbrot?(c) - } -} - diff --git a/test/testdata/ruby_benchmark/app_raise.rb b/test/testdata/ruby_benchmark/app_raise.rb deleted file mode 100644 index d4f0d21a3e..0000000000 --- a/test/testdata/ruby_benchmark/app_raise.rb +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true -# typed: strict -# compiled: true -i = 0 -while i < 10_000_000 - begin - raise - rescue - end - i += 1 -end - -p i diff --git a/test/testdata/ruby_benchmark/app_strconcat.llo.exp b/test/testdata/ruby_benchmark/app_strconcat.llo.exp deleted file mode 100644 index d30927f759..0000000000 --- a/test/testdata/ruby_benchmark/app_strconcat.llo.exp +++ /dev/null @@ -1,576 +0,0 @@ -; ModuleID = 'payload' -source_filename = "llvm-link" -target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } -%struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } -%struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } -%struct.anon.0 = type { i16, [2 x i8] } -%struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } -%struct.rb_iseq_location_struct = type { i64, i64, i64, i64, i32, %struct.rb_code_location_struct } -%struct.rb_code_location_struct = type { %struct.rb_code_position_struct, %struct.rb_code_position_struct } -%struct.rb_code_position_struct = type { i32, i32 } -%struct.iseq_insn_info = type { %struct.iseq_insn_info_entry*, i32*, i32, %struct.succ_index_table* } -%struct.iseq_insn_info_entry = type opaque -%struct.succ_index_table = type opaque -%struct.iseq_catch_table = type opaque -%union.iseq_inline_storage_entry = type { %struct.iseq_inline_cache_entry } -%struct.iseq_inline_cache_entry = type { i64, %struct.rb_cref_struct*, i64 } -%struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } -%struct.rb_scope_visi_struct = type { i8, [3 x i8] } -%struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } -%struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } -%union.anon.13 = type { %struct.rb_method_cfunc_struct } -%struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%struct.rb_calling_info = type { i64, i64, i32, i32 } -%union.anon.15 = type { i32 } -%struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.16 = type { i64, i64, i64, i64* } -%union.anon.17 = type { %struct.anon.18 } -%struct.anon.18 = type { i64, i32 } -%struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } -%struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } -%struct.rb_fiber_struct = type opaque -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } -%struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } -%struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.__pthread_cond_s } -%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } -%union.anon = type { i64 } -%struct.anon.5 = type { [65 x i64] } -%struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } -%struct.rb_event_hook_struct = type opaque -%struct.rb_postponed_job_struct = type opaque -%struct.list_head = type { %struct.list_node } -%struct.rb_objspace = type opaque -%struct.rb_at_exit_list = type { void (%struct.rb_vm_struct*)*, %struct.rb_at_exit_list* } -%struct.rb_builtin_function = type opaque -%struct.anon.6 = type { i64, i64, i64, i64 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } -%union.anon.9 = type { %union.pthread_cond_t } -%union.pthread_mutex_t = type { %struct.__pthread_mutex_s } -%struct.__pthread_mutex_s = type { i32, i32, i32, i32, i32, i16, i16, %struct.__pthread_internal_list } -%struct.__pthread_internal_list = type { %struct.__pthread_internal_list*, %struct.__pthread_internal_list* } -%struct.rb_unblock_callback = type { void (i8*)*, i8* } -%struct.rb_mutex_struct = type opaque -%struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.10 = type { %struct.anon.11 } -%struct.anon.11 = type { i64, i64, i32 } -%struct.st_table = type { i8, i8, i8, i32, %struct.st_hash_type*, i64, i64*, i64, i64, %struct.st_table_entry* } -%struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } -%struct.st_table_entry = type opaque -%struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } -%struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } -%struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } -%struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } -%struct.__sigset_t = type { [16 x i64] } -%struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } -%struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } -%struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } -%struct.rb_call_info_with_kwarg = type { %struct.rb_call_info, %struct.rb_call_info_kw_arg* } -%struct.rb_call_info_kw_arg = type { i32, [1 x i64] } -%struct.iseq_inline_iv_cache_entry = type { i64, i64 } - -@ruby_current_execution_context_ptr = external local_unnamed_addr global %struct.rb_execution_context_struct*, align 8 -@rb_eRuntimeError = external local_unnamed_addr global i64, align 8 -@.str.9 = private unnamed_addr constant [95 x i8] c"sorbet_getBuildSCMRevision: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@.str.10 = private unnamed_addr constant [93 x i8] c"sorbet_getIsReleaseBuild: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@"stackFramePrecomputed_func_.17$152" = unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"rubyIdPrecomputed_" = internal unnamed_addr global i64 0, align 8 -@"str_" = private unnamed_addr constant [17 x i8] c"\00", align 1 -@"rubyStrFrozen_" = internal unnamed_addr global i64 0, align 8 -@"rubyStrFrozen_test/testdata/ruby_benchmark/app_strconcat.rb" = internal unnamed_addr global i64 0, align 8 -@"str_test/testdata/ruby_benchmark/app_strconcat.rb" = private unnamed_addr constant [46 x i8] c"test/testdata/ruby_benchmark/app_strconcat.rb\00", align 1 -@iseqEncodedArray = internal global [9 x i64] zeroinitializer -@fileLineNumberInfo = internal global %struct.SorbetLineNumberInfo zeroinitializer -@"rubyIdPrecomputed_<" = internal unnamed_addr global i64 0, align 8 -@"str_<" = private unnamed_addr constant [2 x i8] c"<\00", align 1 -@"ic_<" = internal global %struct.FunctionInlineCache zeroinitializer -@"rubyIdPrecomputed_+" = internal unnamed_addr global i64 0, align 8 -@"str_+" = private unnamed_addr constant [2 x i8] c"+\00", align 1 -@"rubyStrFrozen_ " = internal unnamed_addr global i64 0, align 8 -@"str_ " = private unnamed_addr constant [2 x i8] c" \00", align 1 -@"rubyIdPrecomputed_" = internal unnamed_addr global i64 0, align 8 -@"str_" = private unnamed_addr constant [21 x i8] c"\00", align 1 -@"ic_+.3" = internal global %struct.FunctionInlineCache zeroinitializer - -declare %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64, i64, i64, i64, %struct.rb_iseq_struct*, i32, i32, %struct.SorbetLineNumberInfo*, i64*, i32, i32) local_unnamed_addr #0 - -declare void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo*, i64*, i32) local_unnamed_addr #0 - -declare i64 @sorbet_readRealpath() local_unnamed_addr #0 - -declare void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache*, i64, i32, i32, i32, i64*) local_unnamed_addr #0 - -declare i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache*, i64) local_unnamed_addr #0 - -declare void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_iseq_struct*) local_unnamed_addr #0 - -declare i64 @sorbet_stringInterpolate(i64, i64, i32, i64*, i64 (i64, i64, i32, i64*, i64)*, i64) local_unnamed_addr #0 - -declare i64 @sorbet_rb_int_plus_slowpath(i64, i64) local_unnamed_addr #0 - -declare i64 @sorbet_rb_int_lt_slowpath(i64, i64) local_unnamed_addr #0 - -declare i64 @sorbet_vm_fstring_new(i8*, i64) local_unnamed_addr #0 - -declare i64 @rb_intern2(i8*, i64) local_unnamed_addr #0 - -declare void @rb_gc_register_mark_object(i64) local_unnamed_addr #0 - -; Function Attrs: noreturn -declare void @rb_raise(i64, i8*, ...) local_unnamed_addr #1 - -declare i64 @rb_int2big(i64) local_unnamed_addr #0 - -; Function Attrs: nofree nosync nounwind readnone speculatable willreturn -declare { i64, i1 } @llvm.sadd.with.overflow.i64(i64, i64) #2 - -declare i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct*, i32) local_unnamed_addr #0 - -; Function Attrs: nounwind ssp uwtable -define weak i32 @sorbet_getIsReleaseBuild() local_unnamed_addr #3 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([93 x i8], [93 x i8]* @.str.10, i64 0, i64 0)) #7 - unreachable -} - -; Function Attrs: nounwind ssp uwtable -define weak i8* @sorbet_getBuildSCMRevision() local_unnamed_addr #3 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([95 x i8], [95 x i8]* @.str.9, i64 0, i64 0)) #7 - unreachable -} - -; Function Attrs: sspreq -define void @Init_app_strconcat() local_unnamed_addr #4 { -entry: - %callArgs.i = alloca [6 x i64], align 8 - %locals.i.i = alloca i64, i32 0, align 8 - %realpath = tail call i64 @sorbet_readRealpath() - %0 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([17 x i8], [17 x i8]* @"str_", i64 0, i64 0), i64 noundef 16) #8 - store i64 %0, i64* @"rubyIdPrecomputed_", align 8 - %1 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([2 x i8], [2 x i8]* @"str_<", i64 0, i64 0), i64 noundef 1) #8 - store i64 %1, i64* @"rubyIdPrecomputed_<", align 8 - %2 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([2 x i8], [2 x i8]* @"str_+", i64 0, i64 0), i64 noundef 1) #8 - store i64 %2, i64* @"rubyIdPrecomputed_+", align 8 - %3 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([21 x i8], [21 x i8]* @"str_", i64 0, i64 0), i64 noundef 20) #8 - store i64 %3, i64* @"rubyIdPrecomputed_", align 8 - %4 = tail call i64 @sorbet_vm_fstring_new(i8* noundef getelementptr inbounds ([17 x i8], [17 x i8]* @"str_", i64 0, i64 0), i64 noundef 16) #8 - tail call void @rb_gc_register_mark_object(i64 %4) #8 - store i64 %4, i64* @"rubyStrFrozen_", align 8 - %5 = tail call i64 @sorbet_vm_fstring_new(i8* noundef getelementptr inbounds ([46 x i8], [46 x i8]* @"str_test/testdata/ruby_benchmark/app_strconcat.rb", i64 0, i64 0), i64 noundef 45) #8 - tail call void @rb_gc_register_mark_object(i64 %5) #8 - store i64 %5, i64* @"rubyStrFrozen_test/testdata/ruby_benchmark/app_strconcat.rb", align 8 - tail call void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef getelementptr inbounds ([9 x i64], [9 x i64]* @iseqEncodedArray, i32 0, i32 0), i32 noundef 9) - %"rubyId_.i.i" = load i64, i64* @"rubyIdPrecomputed_", align 8 - %"rubyStr_.i.i" = load i64, i64* @"rubyStrFrozen_", align 8 - %"rubyStr_test/testdata/ruby_benchmark/app_strconcat.rb.i.i" = load i64, i64* @"rubyStrFrozen_test/testdata/ruby_benchmark/app_strconcat.rb", align 8 - %6 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_.i.i", i64 %"rubyId_.i.i", i64 %"rubyStr_test/testdata/ruby_benchmark/app_strconcat.rb.i.i", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 0, i32 noundef 4, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals.i.i, i32 noundef 0, i32 noundef 6) - store %struct.rb_iseq_struct* %6, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.17$152", align 8 - %"rubyId_<.i" = load i64, i64* @"rubyIdPrecomputed_<", align 8, !dbg !10 - call void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @"ic_<", i64 %"rubyId_<.i", i32 noundef 16, i32 noundef 1, i32 noundef 0, i64* noundef null), !dbg !10 - %7 = call i64 @sorbet_vm_fstring_new(i8* noundef getelementptr inbounds ([2 x i8], [2 x i8]* @"str_ ", i64 0, i64 0), i64 noundef 1) #8 - call void @rb_gc_register_mark_object(i64 %7) #8 - store i64 %7, i64* @"rubyStrFrozen_ ", align 8 - %"rubyId_+6.i" = load i64, i64* @"rubyIdPrecomputed_+", align 8, !dbg !15 - call void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @"ic_+.3", i64 %"rubyId_+6.i", i32 noundef 16, i32 noundef 1, i32 noundef 0, i64* noundef null), !dbg !15 - %8 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !16 - %9 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %8, i64 0, i32 2 - %10 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %9, align 8, !tbaa !18 - %11 = bitcast [6 x i64]* %callArgs.i to i8* - call void @llvm.lifetime.start.p0i8(i64 48, i8* nonnull %11) - %stackFrame.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.17$152", align 8 - %12 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %9, align 8, !tbaa !18 - %13 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %12, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame.i, %struct.rb_iseq_struct** %13, align 8, !tbaa !22 - %14 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %12, i64 0, i32 4 - %15 = load i64*, i64** %14, align 8, !tbaa !24 - %16 = load i64, i64* %15, align 8, !tbaa !6 - %17 = and i64 %16, -33 - store i64 %17, i64* %15, align 8, !tbaa !6 - call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %8, %struct.rb_control_frame_struct* %12, %struct.rb_iseq_struct* %stackFrame.i) #8 - %18 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %10, i64 0, i32 0 - %callArgs0Addr.i = getelementptr [6 x i64], [6 x i64]* %callArgs.i, i32 0, i64 0 - %19 = getelementptr [6 x i64], [6 x i64]* %callArgs.i, i64 0, i64 0 - %callArgs1Addr.i = getelementptr [6 x i64], [6 x i64]* %callArgs.i, i32 0, i64 1 - %callArgs2Addr.i = getelementptr [6 x i64], [6 x i64]* %callArgs.i, i32 0, i64 2 - %callArgs3Addr.i = getelementptr [6 x i64], [6 x i64]* %callArgs.i, i32 0, i64 3 - %callArgs4Addr.i = getelementptr [6 x i64], [6 x i64]* %callArgs.i, i32 0, i64 4 - br label %BB2.i, !dbg !25 - -BB2.i: ; preds = %BB2.i.backedge, %entry - %i.sroa.0.0.i = phi i64 [ 1, %entry ], [ %i.sroa.0.0.i.be, %BB2.i.backedge ], !dbg !26 - store i64* getelementptr inbounds ([9 x i64], [9 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %18, align 8, !tbaa !16 - %20 = and i64 %i.sroa.0.0.i, 1, !dbg !10 - %21 = icmp eq i64 %20, 0, !dbg !10 - br i1 %21, label %22, label %"fastSymCallIntrinsic_Integer_<.i", !dbg !10, !prof !27 - -22: ; preds = %BB2.i - %23 = and i64 %i.sroa.0.0.i, 7, !dbg !10 - %24 = icmp ne i64 %23, 0, !dbg !10 - %25 = and i64 %i.sroa.0.0.i, -9, !dbg !10 - %26 = icmp eq i64 %25, 0, !dbg !10 - %27 = or i1 %24, %26, !dbg !10 - br i1 %27, label %"alternativeCallIntrinsic_Integer_<.i", label %sorbet_isa_Integer.exit, !dbg !10, !prof !28 - -sorbet_isa_Integer.exit: ; preds = %22 - %28 = inttoptr i64 %i.sroa.0.0.i to %struct.iseq_inline_iv_cache_entry*, !dbg !10 - %29 = getelementptr inbounds %struct.iseq_inline_iv_cache_entry, %struct.iseq_inline_iv_cache_entry* %28, i64 0, i32 0, !dbg !10 - %30 = load i64, i64* %29, align 8, !dbg !10, !tbaa !29 - %31 = and i64 %30, 31, !dbg !10 - %32 = icmp eq i64 %31, 10, !dbg !10 - br i1 %32, label %"fastSymCallIntrinsic_Integer_<.i", label %"alternativeCallIntrinsic_Integer_<.i", !dbg !10, !prof !31 - -BB5.i: ; preds = %afterSend.i - store i64* getelementptr inbounds ([9 x i64], [9 x i64]* @iseqEncodedArray, i64 0, i64 6), i64** %18, align 8, !tbaa !16 - store i64 3, i64* %callArgs0Addr.i, align 8, !dbg !32 - call void @llvm.experimental.noalias.scope.decl(metadata !33) #8, !dbg !32 - %33 = load i64, i64* %19, align 8, !dbg !32, !tbaa !6, !alias.scope !33 - %34 = and i64 %33, 1, !dbg !32 - %35 = icmp eq i64 %34, 0, !dbg !32 - br i1 %35, label %45, label %36, !dbg !32, !prof !36 - -36: ; preds = %BB5.i - %37 = add nsw i64 %33, -1, !dbg !32 - %38 = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 3, i64 %37) #9, !dbg !32 - %39 = extractvalue { i64, i1 } %38, 1, !dbg !32 - %40 = extractvalue { i64, i1 } %38, 0, !dbg !32 - br i1 %39, label %41, label %sorbet_rb_int_plus.exit118.i, !dbg !32 - -41: ; preds = %36 - %42 = ashr i64 %40, 1, !dbg !32 - %43 = xor i64 %42, -9223372036854775808, !dbg !32 - %44 = call i64 @rb_int2big(i64 %43) #8, !dbg !32, !noalias !33 - br label %sorbet_rb_int_plus.exit118.i, !dbg !32 - -45: ; preds = %BB5.i - %46 = call i64 @sorbet_rb_int_plus_slowpath(i64 noundef 3, i64 %33) #8, !dbg !32, !noalias !33 - br label %sorbet_rb_int_plus.exit118.i, !dbg !32 - -sorbet_rb_int_plus.exit118.i: ; preds = %45, %41, %36 - %47 = phi i64 [ %46, %45 ], [ %44, %41 ], [ %40, %36 ], !dbg !32 - %48 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !32, !tbaa !16 - %49 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %48, i64 0, i32 5, !dbg !32 - %50 = load i32, i32* %49, align 8, !dbg !32, !tbaa !37 - %51 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %48, i64 0, i32 6, !dbg !32 - %52 = load i32, i32* %51, align 4, !dbg !32, !tbaa !38 - %53 = xor i32 %52, -1, !dbg !32 - %54 = and i32 %53, %50, !dbg !32 - %55 = icmp eq i32 %54, 0, !dbg !32 - br i1 %55, label %rb_vm_check_ints.exit3.i, label %56, !dbg !32, !prof !31 - -56: ; preds = %sorbet_rb_int_plus.exit118.i - %57 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %48, i64 0, i32 8, !dbg !32 - %58 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %57, align 8, !dbg !32, !tbaa !39 - %59 = call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %58, i32 noundef 0) #8, !dbg !32 - br label %rb_vm_check_ints.exit3.i, !dbg !32 - -rb_vm_check_ints.exit3.i: ; preds = %56, %sorbet_rb_int_plus.exit118.i - %"rubyStr_ .i" = load i64, i64* @"rubyStrFrozen_ ", align 8, !dbg !40 - store i64 3, i64* %callArgs0Addr.i, align 8, !dbg !41 - call void @llvm.experimental.noalias.scope.decl(metadata !42) #8, !dbg !41 - %60 = load i64, i64* %19, align 8, !dbg !41, !tbaa !6, !alias.scope !42 - %61 = and i64 %60, 1, !dbg !41 - %62 = icmp eq i64 %61, 0, !dbg !41 - br i1 %62, label %72, label %63, !dbg !41, !prof !36 - -63: ; preds = %rb_vm_check_ints.exit3.i - %64 = add nsw i64 %60, -1, !dbg !41 - %65 = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 3, i64 %64) #9, !dbg !41 - %66 = extractvalue { i64, i1 } %65, 1, !dbg !41 - %67 = extractvalue { i64, i1 } %65, 0, !dbg !41 - br i1 %66, label %68, label %sorbet_rb_int_plus.exit119.i, !dbg !41 - -68: ; preds = %63 - %69 = ashr i64 %67, 1, !dbg !41 - %70 = xor i64 %69, -9223372036854775808, !dbg !41 - %71 = call i64 @rb_int2big(i64 %70) #8, !dbg !41, !noalias !42 - br label %sorbet_rb_int_plus.exit119.i, !dbg !41 - -72: ; preds = %rb_vm_check_ints.exit3.i - %73 = call i64 @sorbet_rb_int_plus_slowpath(i64 noundef 3, i64 %60) #8, !dbg !41, !noalias !42 - br label %sorbet_rb_int_plus.exit119.i, !dbg !41 - -sorbet_rb_int_plus.exit119.i: ; preds = %72, %68, %63 - %74 = phi i64 [ %73, %72 ], [ %71, %68 ], [ %67, %63 ], !dbg !41 - %75 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !41, !tbaa !16 - %76 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %75, i64 0, i32 5, !dbg !41 - %77 = load i32, i32* %76, align 8, !dbg !41, !tbaa !37 - %78 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %75, i64 0, i32 6, !dbg !41 - %79 = load i32, i32* %78, align 4, !dbg !41, !tbaa !38 - %80 = xor i32 %79, -1, !dbg !41 - %81 = and i32 %80, %77, !dbg !41 - %82 = icmp eq i32 %81, 0, !dbg !41 - br i1 %82, label %rb_vm_check_ints.exit6.i, label %83, !dbg !41, !prof !31 - -83: ; preds = %sorbet_rb_int_plus.exit119.i - %84 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %75, i64 0, i32 8, !dbg !41 - %85 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %84, align 8, !dbg !41, !tbaa !39 - %86 = call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %85, i32 noundef 0) #8, !dbg !41 - br label %rb_vm_check_ints.exit6.i, !dbg !41 - -rb_vm_check_ints.exit6.i: ; preds = %83, %sorbet_rb_int_plus.exit119.i - %"rubyStr_ 64.i" = load i64, i64* @"rubyStrFrozen_ ", align 8, !dbg !45 - store i64 3, i64* %callArgs0Addr.i, align 8, !dbg !46 - call void @llvm.experimental.noalias.scope.decl(metadata !47) #8, !dbg !46 - %87 = load i64, i64* %19, align 8, !dbg !46, !tbaa !6, !alias.scope !47 - %88 = and i64 %87, 1, !dbg !46 - %89 = icmp eq i64 %88, 0, !dbg !46 - br i1 %89, label %99, label %90, !dbg !46, !prof !36 - -90: ; preds = %rb_vm_check_ints.exit6.i - %91 = add nsw i64 %87, -1, !dbg !46 - %92 = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 3, i64 %91) #9, !dbg !46 - %93 = extractvalue { i64, i1 } %92, 1, !dbg !46 - %94 = extractvalue { i64, i1 } %92, 0, !dbg !46 - br i1 %93, label %95, label %sorbet_rb_int_plus.exit120.i, !dbg !46 - -95: ; preds = %90 - %96 = ashr i64 %94, 1, !dbg !46 - %97 = xor i64 %96, -9223372036854775808, !dbg !46 - %98 = call i64 @rb_int2big(i64 %97) #8, !dbg !46, !noalias !47 - br label %sorbet_rb_int_plus.exit120.i, !dbg !46 - -99: ; preds = %rb_vm_check_ints.exit6.i - %100 = call i64 @sorbet_rb_int_plus_slowpath(i64 noundef 3, i64 %87) #8, !dbg !46, !noalias !47 - br label %sorbet_rb_int_plus.exit120.i, !dbg !46 - -sorbet_rb_int_plus.exit120.i: ; preds = %99, %95, %90 - %101 = phi i64 [ %100, %99 ], [ %98, %95 ], [ %94, %90 ], !dbg !46 - %102 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !46, !tbaa !16 - %103 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %102, i64 0, i32 5, !dbg !46 - %104 = load i32, i32* %103, align 8, !dbg !46, !tbaa !37 - %105 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %102, i64 0, i32 6, !dbg !46 - %106 = load i32, i32* %105, align 4, !dbg !46, !tbaa !38 - %107 = xor i32 %106, -1, !dbg !46 - %108 = and i32 %107, %104, !dbg !46 - %109 = icmp eq i32 %108, 0, !dbg !46 - br i1 %109, label %rb_vm_check_ints.exit5.i, label %110, !dbg !46, !prof !31 - -110: ; preds = %sorbet_rb_int_plus.exit120.i - %111 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %102, i64 0, i32 8, !dbg !46 - %112 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %111, align 8, !dbg !46, !tbaa !39 - %113 = call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %112, i32 noundef 0) #8, !dbg !46 - br label %rb_vm_check_ints.exit5.i, !dbg !46 - -rb_vm_check_ints.exit5.i: ; preds = %110, %sorbet_rb_int_plus.exit120.i - store i64 %47, i64* %callArgs0Addr.i, align 8, !dbg !50 - store i64 %"rubyStr_ .i", i64* %callArgs1Addr.i, align 8, !dbg !50 - store i64 %74, i64* %callArgs2Addr.i, align 8, !dbg !50 - store i64 %"rubyStr_ 64.i", i64* %callArgs3Addr.i, align 8, !dbg !50 - store i64 %101, i64* %callArgs4Addr.i, align 8, !dbg !50 - %"rubyId_.i" = load i64, i64* @"rubyIdPrecomputed_", align 8, !dbg !50 - %rawSendResult89.i = call i64 @sorbet_stringInterpolate(i64 noundef 8, i64 %"rubyId_.i", i32 noundef 5, i64* noundef nonnull %19, i64 (i64, i64, i32, i64*, i64)* noundef null, i64 noundef 0) #8, !dbg !50 - store i64* getelementptr inbounds ([9 x i64], [9 x i64]* @iseqEncodedArray, i64 0, i64 7), i64** %18, align 8, !dbg !50, !tbaa !16 - br i1 %114, label %"fastSymCallIntrinsic_Integer_+97.i", label %"alternativeCallIntrinsic_Integer_+96.i", !dbg !15 - -afterSend.i: ; preds = %142, %sorbet_rb_int_lt.exit.i, %"alternativeCallIntrinsic_Integer_<.i" - %114 = phi i1 [ %117, %"alternativeCallIntrinsic_Integer_<.i" ], [ %122, %sorbet_rb_int_lt.exit.i ], [ %122, %142 ] - %"symIntrinsicRawPhi_<.i" = phi i64 [ %send, %"alternativeCallIntrinsic_Integer_<.i" ], [ %rawSendResult117.i, %sorbet_rb_int_lt.exit.i ], [ %rawSendResult117.i, %142 ], !dbg !10 - %115 = and i64 %"symIntrinsicRawPhi_<.i", -9, !dbg !10 - %116 = icmp ne i64 %115, 0, !dbg !10 - br i1 %116, label %BB5.i, label %"func_.17$152.exit", !dbg !10 - -"alternativeCallIntrinsic_Integer_<.i": ; preds = %22, %sorbet_isa_Integer.exit - %117 = phi i1 [ %32, %sorbet_isa_Integer.exit ], [ false, %22 ] - %118 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %10, i64 0, i32 1, !dbg !10 - %119 = load i64*, i64** %118, align 8, !dbg !10 - store i64 %i.sroa.0.0.i, i64* %119, align 8, !dbg !10, !tbaa !6 - %120 = getelementptr inbounds i64, i64* %119, i64 1, !dbg !10 - store i64 4000001, i64* %120, align 8, !dbg !10, !tbaa !6 - %121 = getelementptr inbounds i64, i64* %120, i64 1, !dbg !10 - store i64* %121, i64** %118, align 8, !dbg !10 - %send = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @"ic_<", i64 0), !dbg !10 - br label %afterSend.i, !dbg !10 - -"fastSymCallIntrinsic_Integer_<.i": ; preds = %BB2.i, %sorbet_isa_Integer.exit - %122 = phi i1 [ %32, %sorbet_isa_Integer.exit ], [ true, %BB2.i ] - store i64 4000001, i64* %callArgs0Addr.i, align 8, !dbg !10 - call void @llvm.experimental.noalias.scope.decl(metadata !51) #8, !dbg !10 - %123 = load i64, i64* %19, align 8, !dbg !10, !tbaa !6, !alias.scope !51 - %124 = and i64 %i.sroa.0.0.i, 1, !dbg !10 - %125 = and i64 %124, %123, !dbg !10 - %126 = icmp eq i64 %125, 0, !dbg !10 - br i1 %126, label %132, label %127, !dbg !10, !prof !36 - -127: ; preds = %"fastSymCallIntrinsic_Integer_<.i" - %128 = ashr i64 %i.sroa.0.0.i, 1, !dbg !10 - %129 = ashr i64 %123, 1, !dbg !10 - %130 = icmp slt i64 %128, %129, !dbg !10 - %131 = select i1 %130, i64 20, i64 0, !dbg !10 - br label %sorbet_rb_int_lt.exit.i, !dbg !10 - -132: ; preds = %"fastSymCallIntrinsic_Integer_<.i" - %133 = call i64 @sorbet_rb_int_lt_slowpath(i64 %i.sroa.0.0.i, i64 %123) #8, !dbg !10, !noalias !51 - br label %sorbet_rb_int_lt.exit.i, !dbg !10 - -sorbet_rb_int_lt.exit.i: ; preds = %132, %127 - %rawSendResult117.i = phi i64 [ %131, %127 ], [ %133, %132 ] - %134 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !10, !tbaa !16 - %135 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %134, i64 0, i32 5, !dbg !10 - %136 = load i32, i32* %135, align 8, !dbg !10, !tbaa !37 - %137 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %134, i64 0, i32 6, !dbg !10 - %138 = load i32, i32* %137, align 4, !dbg !10, !tbaa !38 - %139 = xor i32 %138, -1, !dbg !10 - %140 = and i32 %139, %136, !dbg !10 - %141 = icmp eq i32 %140, 0, !dbg !10 - br i1 %141, label %afterSend.i, label %142, !dbg !10, !prof !31 - -142: ; preds = %sorbet_rb_int_lt.exit.i - %143 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %134, i64 0, i32 8, !dbg !10 - %144 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %143, align 8, !dbg !10, !tbaa !39 - %145 = call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %144, i32 noundef 0) #8, !dbg !10 - br label %afterSend.i, !dbg !10 - -"alternativeCallIntrinsic_Integer_+96.i": ; preds = %rb_vm_check_ints.exit5.i - %146 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %10, i64 0, i32 1, !dbg !15 - %147 = load i64*, i64** %146, align 8, !dbg !15 - store i64 %i.sroa.0.0.i, i64* %147, align 8, !dbg !15, !tbaa !6 - %148 = getelementptr inbounds i64, i64* %147, i64 1, !dbg !15 - store i64 3, i64* %148, align 8, !dbg !15, !tbaa !6 - %149 = getelementptr inbounds i64, i64* %148, i64 1, !dbg !15 - store i64* %149, i64** %146, align 8, !dbg !15 - %send2 = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @"ic_+.3", i64 0), !dbg !15 - br label %BB2.i.backedge, !dbg !15 - -"fastSymCallIntrinsic_Integer_+97.i": ; preds = %rb_vm_check_ints.exit5.i - store i64 3, i64* %callArgs0Addr.i, align 8, !dbg !15 - call void @llvm.experimental.noalias.scope.decl(metadata !54) #8, !dbg !15 - %150 = load i64, i64* %19, align 8, !dbg !15, !tbaa !6, !alias.scope !54 - %151 = and i64 %i.sroa.0.0.i, 1, !dbg !15 - %152 = and i64 %151, %150, !dbg !15 - %153 = icmp eq i64 %152, 0, !dbg !15 - br i1 %153, label %163, label %154, !dbg !15, !prof !36 - -154: ; preds = %"fastSymCallIntrinsic_Integer_+97.i" - %155 = add nsw i64 %150, -1, !dbg !15 - %156 = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %i.sroa.0.0.i, i64 %155) #9, !dbg !15 - %157 = extractvalue { i64, i1 } %156, 1, !dbg !15 - %158 = extractvalue { i64, i1 } %156, 0, !dbg !15 - br i1 %157, label %159, label %sorbet_rb_int_plus.exit.i, !dbg !15 - -159: ; preds = %154 - %160 = ashr i64 %158, 1, !dbg !15 - %161 = xor i64 %160, -9223372036854775808, !dbg !15 - %162 = call i64 @rb_int2big(i64 %161) #8, !dbg !15, !noalias !54 - br label %sorbet_rb_int_plus.exit.i, !dbg !15 - -163: ; preds = %"fastSymCallIntrinsic_Integer_+97.i" - %164 = call i64 @sorbet_rb_int_plus_slowpath(i64 %i.sroa.0.0.i, i64 %150) #8, !dbg !15, !noalias !54 - br label %sorbet_rb_int_plus.exit.i, !dbg !15 - -sorbet_rb_int_plus.exit.i: ; preds = %163, %159, %154 - %165 = phi i64 [ %164, %163 ], [ %162, %159 ], [ %158, %154 ], !dbg !15 - %166 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !15, !tbaa !16 - %167 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %166, i64 0, i32 5, !dbg !15 - %168 = load i32, i32* %167, align 8, !dbg !15, !tbaa !37 - %169 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %166, i64 0, i32 6, !dbg !15 - %170 = load i32, i32* %169, align 4, !dbg !15, !tbaa !38 - %171 = xor i32 %170, -1, !dbg !15 - %172 = and i32 %171, %168, !dbg !15 - %173 = icmp eq i32 %172, 0, !dbg !15 - br i1 %173, label %BB2.i.backedge, label %174, !dbg !15, !prof !31 - -174: ; preds = %sorbet_rb_int_plus.exit.i - %175 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %166, i64 0, i32 8, !dbg !15 - %176 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %175, align 8, !dbg !15, !tbaa !39 - %177 = call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %176, i32 noundef 0) #8, !dbg !15 - br label %BB2.i.backedge, !dbg !15 - -BB2.i.backedge: ; preds = %174, %sorbet_rb_int_plus.exit.i, %"alternativeCallIntrinsic_Integer_+96.i" - %i.sroa.0.0.i.be = phi i64 [ %send2, %"alternativeCallIntrinsic_Integer_+96.i" ], [ %165, %sorbet_rb_int_plus.exit.i ], [ %165, %174 ] - br label %BB2.i - -"func_.17$152.exit": ; preds = %afterSend.i - store i64* getelementptr inbounds ([9 x i64], [9 x i64]* @iseqEncodedArray, i64 0, i64 8), i64** %18, align 8, !tbaa !16 - call void @llvm.lifetime.end.p0i8(i64 48, i8* nonnull %11) - ret void -} - -; Function Attrs: inaccessiblememonly nofree nosync nounwind willreturn -declare void @llvm.experimental.noalias.scope.decl(metadata) #5 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #6 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #6 - -attributes #0 = { "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #1 = { noreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #2 = { nofree nosync nounwind readnone speculatable willreturn } -attributes #3 = { nounwind ssp uwtable "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #4 = { sspreq } -attributes #5 = { inaccessiblememonly nofree nosync nounwind willreturn } -attributes #6 = { argmemonly nofree nosync nounwind willreturn } -attributes #7 = { noreturn nounwind } -attributes #8 = { nounwind } -attributes #9 = { nounwind willreturn } - -!llvm.module.flags = !{!0, !1, !2} -!llvm.dbg.cu = !{!3} - -!0 = !{i32 2, !"Debug Info Version", i32 3} -!1 = !{i32 4, !"cf-protection-return", i32 1} -!2 = !{i32 4, !"cf-protection-branch", i32 1} -!3 = distinct !DICompileUnit(language: DW_LANG_C, file: !4, producer: "Sorbet LLVM", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !5) -!4 = !DIFile(filename: "test/testdata/ruby_benchmark/app_strconcat.rb", directory: ".") -!5 = !{} -!6 = !{!7, !7, i64 0} -!7 = !{!"long", !8, i64 0} -!8 = !{!"omnipotent char", !9, i64 0} -!9 = !{!"Simple C/C++ TBAA"} -!10 = !DILocation(line: 5, column: 7, scope: !11) -!11 = distinct !DISubprogram(name: ".", linkageName: "func_.17$152", scope: null, file: !4, line: 4, type: !12, scopeLine: 4, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!12 = !DISubroutineType(types: !13) -!13 = !{!14} -!14 = !DIBasicType(name: "VALUE", size: 64, encoding: DW_ATE_signed) -!15 = !DILocation(line: 7, column: 3, scope: !11) -!16 = !{!17, !17, i64 0} -!17 = !{!"any pointer", !8, i64 0} -!18 = !{!19, !17, i64 16} -!19 = !{!"rb_execution_context_struct", !17, i64 0, !7, i64 8, !17, i64 16, !17, i64 24, !17, i64 32, !20, i64 40, !20, i64 44, !17, i64 48, !17, i64 56, !17, i64 64, !7, i64 72, !7, i64 80, !17, i64 88, !7, i64 96, !17, i64 104, !17, i64 112, !7, i64 120, !7, i64 128, !8, i64 136, !8, i64 137, !7, i64 144, !21, i64 152} -!20 = !{!"int", !8, i64 0} -!21 = !{!"", !17, i64 0, !17, i64 8, !7, i64 16, !8, i64 24} -!22 = !{!23, !17, i64 16} -!23 = !{!"rb_control_frame_struct", !17, i64 0, !17, i64 8, !17, i64 16, !7, i64 24, !17, i64 32, !17, i64 40, !17, i64 48} -!24 = !{!23, !17, i64 32} -!25 = !DILocation(line: 4, column: 5, scope: !11) -!26 = !DILocation(line: 0, scope: !11) -!27 = !{!"branch_weights", i32 1, i32 2000} -!28 = !{!"branch_weights", i32 1073205, i32 2146410443} -!29 = !{!30, !7, i64 0} -!30 = !{!"RBasic", !7, i64 0, !7, i64 8} -!31 = !{!"branch_weights", i32 2000, i32 1} -!32 = !DILocation(line: 6, column: 6, scope: !11) -!33 = !{!34} -!34 = distinct !{!34, !35, !"sorbet_rb_int_plus: argument 0"} -!35 = distinct !{!35, !"sorbet_rb_int_plus"} -!36 = !{!"branch_weights", i32 4001, i32 4000000} -!37 = !{!19, !20, i64 40} -!38 = !{!19, !20, i64 44} -!39 = !{!19, !17, i64 56} -!40 = !DILocation(line: 6, column: 10, scope: !11) -!41 = !DILocation(line: 6, column: 13, scope: !11) -!42 = !{!43} -!43 = distinct !{!43, !44, !"sorbet_rb_int_plus: argument 0"} -!44 = distinct !{!44, !"sorbet_rb_int_plus"} -!45 = !DILocation(line: 6, column: 17, scope: !11) -!46 = !DILocation(line: 6, column: 20, scope: !11) -!47 = !{!48} -!48 = distinct !{!48, !49, !"sorbet_rb_int_plus: argument 0"} -!49 = distinct !{!49, !"sorbet_rb_int_plus"} -!50 = !DILocation(line: 6, column: 3, scope: !11) -!51 = !{!52} -!52 = distinct !{!52, !53, !"sorbet_rb_int_lt: argument 0"} -!53 = distinct !{!53, !"sorbet_rb_int_lt"} -!54 = !{!55} -!55 = distinct !{!55, !56, !"sorbet_rb_int_plus: argument 0"} -!56 = distinct !{!56, !"sorbet_rb_int_plus"} diff --git a/test/testdata/ruby_benchmark/app_strconcat.rb b/test/testdata/ruby_benchmark/app_strconcat.rb deleted file mode 100644 index adacbff08d..0000000000 --- a/test/testdata/ruby_benchmark/app_strconcat.rb +++ /dev/null @@ -1,8 +0,0 @@ -# frozen_string_literal: true -# typed: strong -# compiled: true -i = 0 -while i<2_000_000 - "#{1+1} #{1+1} #{1+1}" - i += 1 -end diff --git a/test/testdata/ruby_benchmark/app_tarai.rb b/test/testdata/ruby_benchmark/app_tarai.rb deleted file mode 100644 index 08525016ab..0000000000 --- a/test/testdata/ruby_benchmark/app_tarai.rb +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -def tarai( x, y, z ) - if x <= y - then y - else tarai(tarai(x-1, y, z), - tarai(y-1, z, x), - tarai(z-1, x, y)) - end -end - -tarai(12, 6, 0) diff --git a/test/testdata/ruby_benchmark/app_tarai_sig.rb b/test/testdata/ruby_benchmark/app_tarai_sig.rb deleted file mode 100644 index dda0f3bb11..0000000000 --- a/test/testdata/ruby_benchmark/app_tarai_sig.rb +++ /dev/null @@ -1,15 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -T::Sig::WithoutRuntime.sig(:final) {params(x: Integer, y: Integer, z: Integer).returns(Integer)} -def tarai( x, y, z ) - if x <= y - then y - else tarai(tarai(x-1, y, z), - tarai(y-1, z, x), - tarai(z-1, x, y)) - end -end - -puts tarai(12, 6, 0) diff --git a/test/testdata/ruby_benchmark/app_tarai_sig.rb.out b/test/testdata/ruby_benchmark/app_tarai_sig.rb.out deleted file mode 100644 index 48082f72f0..0000000000 --- a/test/testdata/ruby_benchmark/app_tarai_sig.rb.out +++ /dev/null @@ -1 +0,0 @@ -12 diff --git a/test/testdata/ruby_benchmark/app_uri.rb b/test/testdata/ruby_benchmark/app_uri.rb deleted file mode 100644 index da29854028..0000000000 --- a/test/testdata/ruby_benchmark/app_uri.rb +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true -# typed: strong -# compiled: true -require 'uri' - -100_000.times{ - uri = URI.parse('http://www.ruby-lang.org') - uri.scheme - uri.host - uri.port -} diff --git a/test/testdata/ruby_benchmark/array_sample_100k_10.rb b/test/testdata/ruby_benchmark/array_sample_100k_10.rb deleted file mode 100644 index 9a2e09fe16..0000000000 --- a/test/testdata/ruby_benchmark/array_sample_100k_10.rb +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true -# typed: strong -# compiled: true -arr = [*0...100000] -10_000.times {arr.sample 10} diff --git a/test/testdata/ruby_benchmark/array_sample_100k_11.rb b/test/testdata/ruby_benchmark/array_sample_100k_11.rb deleted file mode 100644 index c737f2730f..0000000000 --- a/test/testdata/ruby_benchmark/array_sample_100k_11.rb +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true -# typed: strong -# compiled: true -arr = [*0...100000] -10_000.times {arr.sample 11} diff --git a/test/testdata/ruby_benchmark/array_sample_100k__100.rb b/test/testdata/ruby_benchmark/array_sample_100k__100.rb deleted file mode 100644 index 54f7d101af..0000000000 --- a/test/testdata/ruby_benchmark/array_sample_100k__100.rb +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true -# typed: strong -# compiled: true -arr = [*0...100000] -10_000.times {arr.sample 100} diff --git a/test/testdata/ruby_benchmark/array_sample_100k__1k.rb b/test/testdata/ruby_benchmark/array_sample_100k__1k.rb deleted file mode 100644 index 40aa190d21..0000000000 --- a/test/testdata/ruby_benchmark/array_sample_100k__1k.rb +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true -# typed: strong -# compiled: true -arr = [*0...100000] -10_000.times {arr.sample 1000} diff --git a/test/testdata/ruby_benchmark/array_sample_100k__6k.rb b/test/testdata/ruby_benchmark/array_sample_100k__6k.rb deleted file mode 100644 index 5c52928e43..0000000000 --- a/test/testdata/ruby_benchmark/array_sample_100k__6k.rb +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true -# typed: strong -# compiled: true -arr = [*0...100000] -10_000.times {arr.sample 6000} diff --git a/test/testdata/ruby_benchmark/array_sample_100k___10k.rb b/test/testdata/ruby_benchmark/array_sample_100k___10k.rb deleted file mode 100644 index 8d928c402f..0000000000 --- a/test/testdata/ruby_benchmark/array_sample_100k___10k.rb +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true -# typed: strong -# compiled: true -arr = [*0...100000] -10_000.times {arr.sample 10_000} diff --git a/test/testdata/ruby_benchmark/array_sample_100k___50k.rb b/test/testdata/ruby_benchmark/array_sample_100k___50k.rb deleted file mode 100644 index 5dd26a0c2e..0000000000 --- a/test/testdata/ruby_benchmark/array_sample_100k___50k.rb +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true -# typed: strong -# compiled: true -arr = [*0...100000] -10_000.times {arr.sample 50_000} diff --git a/test/testdata/ruby_benchmark/array_small_and.rb b/test/testdata/ruby_benchmark/array_small_and.rb deleted file mode 100644 index 6e952e42b5..0000000000 --- a/test/testdata/ruby_benchmark/array_small_and.rb +++ /dev/null @@ -1,20 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -MIN_SIZE = ENV.fetch('SMALL_ARRAY_MIN', 0).to_i -MAX_SIZE = ENV.fetch('SMALL_ARRAY_MAX', 16).to_i -ITERATIONS = ENV.fetch('SMALL_ARRAY_ITERATIONS', 100).to_i - -ARRAYS = (MIN_SIZE..MAX_SIZE).map do |size1| - (MIN_SIZE..MAX_SIZE).map do |size2| - [Array.new(size1) { rand(MAX_SIZE) }, Array.new(size2) { rand(MAX_SIZE) }] - end -end - -ITERATIONS.times do - ARRAYS.each do |group| - group.each do |arr1, arr2| - arr1 & arr2 - end - end -end diff --git a/test/testdata/ruby_benchmark/array_small_diff.rb b/test/testdata/ruby_benchmark/array_small_diff.rb deleted file mode 100644 index c359079dcb..0000000000 --- a/test/testdata/ruby_benchmark/array_small_diff.rb +++ /dev/null @@ -1,20 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -MIN_SIZE = ENV.fetch('SMALL_ARRAY_MIN', 0).to_i -MAX_SIZE = ENV.fetch('SMALL_ARRAY_MAX', 16).to_i -ITERATIONS = ENV.fetch('SMALL_ARRAY_ITERATIONS', 100).to_i - -ARRAYS = (MIN_SIZE..MAX_SIZE).map do |size1| - (MIN_SIZE..MAX_SIZE).map do |size2| - [Array.new(size1) { rand(MAX_SIZE) }, Array.new(size2) { rand(MAX_SIZE) }] - end -end - -ITERATIONS.times do - ARRAYS.each do |group| - group.each do |arr1, arr2| - arr1 - arr2 - end - end -end diff --git a/test/testdata/ruby_benchmark/array_small_or.rb b/test/testdata/ruby_benchmark/array_small_or.rb deleted file mode 100644 index f434cb92b1..0000000000 --- a/test/testdata/ruby_benchmark/array_small_or.rb +++ /dev/null @@ -1,20 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -MIN_SIZE = ENV.fetch('SMALL_ARRAY_MIN', 0).to_i -MAX_SIZE = ENV.fetch('SMALL_ARRAY_MAX', 16).to_i -ITERATIONS = ENV.fetch('SMALL_ARRAY_ITERATIONS', 100).to_i - -ARRAYS = (MIN_SIZE..MAX_SIZE).map do |size1| - (MIN_SIZE..MAX_SIZE).map do |size2| - [Array.new(size1) { rand(MAX_SIZE) }, Array.new(size2) { rand(MAX_SIZE) }] - end -end - -ITERATIONS.times do - ARRAYS.each do |group| - group.each do |arr1, arr2| - arr1 | arr2 - end - end -end diff --git a/test/testdata/ruby_benchmark/array_sort_float.rb b/test/testdata/ruby_benchmark/array_sort_float.rb deleted file mode 100644 index 8d5724e47e..0000000000 --- a/test/testdata/ruby_benchmark/array_sort_float.rb +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true -# typed: strong -# compiled: true -arr = Array.new(1000) { rand } -10000.times { arr.sort } diff --git a/test/testdata/ruby_benchmark/array_values_at_int.rb b/test/testdata/ruby_benchmark/array_values_at_int.rb deleted file mode 100644 index f6844bc2f7..0000000000 --- a/test/testdata/ruby_benchmark/array_values_at_int.rb +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true -# typed: strict -# compiled: true -ary = Array.new(10000) {|i| i} -100000.times { ary.values_at(500) } diff --git a/test/testdata/ruby_benchmark/array_values_at_range.rb b/test/testdata/ruby_benchmark/array_values_at_range.rb deleted file mode 100644 index 1696d7865f..0000000000 --- a/test/testdata/ruby_benchmark/array_values_at_range.rb +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true -# typed: strict -# compiled: true -ary = Array.new(10000) {|i| i} -100000.times { ary.values_at(1..2000) } diff --git a/test/testdata/ruby_benchmark/bighash.rb b/test/testdata/ruby_benchmark/bighash.rb deleted file mode 100644 index 625d21195c..0000000000 --- a/test/testdata/ruby_benchmark/bighash.rb +++ /dev/null @@ -1,4 +0,0 @@ -# frozen_string_literal: true -# typed: strong -# compiled: true -h = {}; 5000000.times {|n| h[n] = n } diff --git a/test/testdata/ruby_benchmark/cgi_escape_html.yml b/test/testdata/ruby_benchmark/cgi_escape_html.yml deleted file mode 100644 index af6abd08ac..0000000000 --- a/test/testdata/ruby_benchmark/cgi_escape_html.yml +++ /dev/null @@ -1,40 +0,0 @@ -prelude: require 'cgi/escape' -benchmark: - - name: escape_html_blank - prelude: str = "" - script: CGI.escapeHTML(str) - loop_count: 20000000 - - name: escape_html_short_none - prelude: str = "abcde" - script: CGI.escapeHTML(str) - loop_count: 20000000 - - name: escape_html_short_one - prelude: str = "abcd<" - script: CGI.escapeHTML(str) - loop_count: 20000000 - - name: escape_html_short_all - prelude: str = "'&\"<>" - script: CGI.escapeHTML(str) - loop_count: 5000000 - - name: escape_html_long_none - prelude: str = "abcde" * 300 - script: CGI.escapeHTML(str) - loop_count: 1000000 - - name: escape_html_long_all - prelude: str = "'&\"<>" * 10 - script: CGI.escapeHTML(str) - loop_count: 1000000 - - name: escape_html_real - prelude: | # http://example.com/ - str = <<~HTML - -
-

Example Domain

-

This domain is established to be used for illustrative examples in documents. You may use this - domain in examples without prior coordination or asking for permission.

-

More information...

-
- - HTML - script: CGI.escapeHTML(str) - loop_count: 1000000 diff --git a/test/testdata/ruby_benchmark/complex_float_add.yml b/test/testdata/ruby_benchmark/complex_float_add.yml deleted file mode 100644 index d0150c5e5b..0000000000 --- a/test/testdata/ruby_benchmark/complex_float_add.yml +++ /dev/null @@ -1,7 +0,0 @@ -prelude: | - max, min = 1000.0, -1000.0 - a = Complex(rand(max)+min, rand(max)+min) - b = Complex(rand(max)+min, rand(max)+min) -benchmark: - complex_float_add: c = a + b -loop_count: 1000000 diff --git a/test/testdata/ruby_benchmark/complex_float_div.yml b/test/testdata/ruby_benchmark/complex_float_div.yml deleted file mode 100644 index b9f5e1d51c..0000000000 --- a/test/testdata/ruby_benchmark/complex_float_div.yml +++ /dev/null @@ -1,7 +0,0 @@ -prelude: | - max, min = 1000.0, -1000.0 - a = Complex(rand(max)+min, rand(max)+min) - b = Complex(rand(max)+min, rand(max)+min) -benchmark: - complex_float_div: c = a / b -loop_count: 1000000 diff --git a/test/testdata/ruby_benchmark/complex_float_mul.yml b/test/testdata/ruby_benchmark/complex_float_mul.yml deleted file mode 100644 index 59b096a6dc..0000000000 --- a/test/testdata/ruby_benchmark/complex_float_mul.yml +++ /dev/null @@ -1,7 +0,0 @@ -prelude: | - max, min = 1000.0, -1000.0 - a = Complex(rand(max)+min, rand(max)+min) - b = Complex(rand(max)+min, rand(max)+min) -benchmark: - complex_float_mul: c = a * b -loop_count: 1000000 diff --git a/test/testdata/ruby_benchmark/complex_float_new.yml b/test/testdata/ruby_benchmark/complex_float_new.yml deleted file mode 100644 index 6fcde3125b..0000000000 --- a/test/testdata/ruby_benchmark/complex_float_new.yml +++ /dev/null @@ -1,7 +0,0 @@ -prelude: | - max, min = 1000.0, -1000.0 - a = Complex(rand(max)+min, rand(max)+min) - b = Complex(rand(max)+min, rand(max)+min) -benchmark: - complex_float_new: c = Complex(a, b) -loop_count: 1000000 diff --git a/test/testdata/ruby_benchmark/complex_float_power.yml b/test/testdata/ruby_benchmark/complex_float_power.yml deleted file mode 100644 index c40a31ab55..0000000000 --- a/test/testdata/ruby_benchmark/complex_float_power.yml +++ /dev/null @@ -1,7 +0,0 @@ -prelude: | - max, min = 1000.0, -1000.0 - a = Complex(rand(max)+min, rand(max)+min) - b = Complex(rand(max)+min, rand(max)+min) -benchmark: - complex_float_power: c = a ** b -loop_count: 1000000 diff --git a/test/testdata/ruby_benchmark/complex_float_sub.yml b/test/testdata/ruby_benchmark/complex_float_sub.yml deleted file mode 100644 index 3fafe7cdbe..0000000000 --- a/test/testdata/ruby_benchmark/complex_float_sub.yml +++ /dev/null @@ -1,7 +0,0 @@ -prelude: | - max, min = 1000.0, -1000.0 - a = Complex(rand(max)+min, rand(max)+min) - b = Complex(rand(max)+min, rand(max)+min) -benchmark: - complex_float_sub: c = a - b -loop_count: 1000000 diff --git a/test/testdata/ruby_benchmark/dir_empty_p.rb b/test/testdata/ruby_benchmark/dir_empty_p.rb deleted file mode 100644 index 8321afcc10..0000000000 --- a/test/testdata/ruby_benchmark/dir_empty_p.rb +++ /dev/null @@ -1,8 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -require 'tmpdir' -max = 100_000 -Dir.mktmpdir('bm_dir_empty_p') do |dir| - max.times { Dir.empty?(dir) } -end diff --git a/test/testdata/ruby_benchmark/disabled/lib/benchmark_driver/output/driver.rb b/test/testdata/ruby_benchmark/disabled/lib/benchmark_driver/output/driver.rb deleted file mode 100644 index 36729939e9..0000000000 --- a/test/testdata/ruby_benchmark/disabled/lib/benchmark_driver/output/driver.rb +++ /dev/null @@ -1,39 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -require 'benchmark_driver/output/simple' - -# This replicates the legacy benchmark/driver.rb behavior. -class BenchmarkDriver::Output::Driver < BenchmarkDriver::Output::Simple - def initialize(*) - super - @stdout = $stdout - @strio = StringIO.new - $stdout = IOMultiplexer.new(@stdout, @strio) - end - - def with_benchmark(*) - super - ensure - logfile = "bmlog-#{Time.now.strftime('%Y%m%d-%H%M%S')}.#{$$}.log" - puts "\nLog file: #{logfile}" - - $stdout = @stdout - File.write(logfile, @strio.tap(&:rewind).read) - end - - class IOMultiplexer - def initialize(io1, io2) - @io1 = io1 - @io2 = io2 - end - - [:write, :sync, :sync=, :puts, :print, :flush].each do |method| - define_method(method) do |*args| - @io1.send(method, *args) - @io2.send(method, *args) - end - end - end - private_constant :IOMultiplexer -end diff --git a/test/testdata/ruby_benchmark/disabled/lib/benchmark_driver/runner/cstime.rb b/test/testdata/ruby_benchmark/disabled/lib/benchmark_driver/runner/cstime.rb deleted file mode 100644 index 701552611b..0000000000 --- a/test/testdata/ruby_benchmark/disabled/lib/benchmark_driver/runner/cstime.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -require 'benchmark_driver/runner/total' - -class BenchmarkDriver::Runner::Cstime < BenchmarkDriver::Runner::Total - METRIC = BenchmarkDriver::Metric.new(name: 'cstime', unit: 's', larger_better: false) - - # JobParser returns this, `BenchmarkDriver::Runner.runner_for` searches "*::Job" - Job = Class.new(BenchmarkDriver::DefaultJob) - # Dynamically fetched and used by `BenchmarkDriver::JobParser.parse` - JobParser = BenchmarkDriver::DefaultJobParser.for(klass: Job, metrics: [METRIC]) - - private - - # Overriding BenchmarkDriver::Runner::Total#metric - def metric - METRIC - end - - # Overriding BenchmarkDriver::Runner::Total#target - def target - :cstime - end -end diff --git a/test/testdata/ruby_benchmark/disabled/lib/benchmark_driver/runner/cutime.rb b/test/testdata/ruby_benchmark/disabled/lib/benchmark_driver/runner/cutime.rb deleted file mode 100644 index 6cf4774329..0000000000 --- a/test/testdata/ruby_benchmark/disabled/lib/benchmark_driver/runner/cutime.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -require 'benchmark_driver/runner/total' - -class BenchmarkDriver::Runner::Cutime < BenchmarkDriver::Runner::Total - METRIC = BenchmarkDriver::Metric.new(name: 'cutime', unit: 's', larger_better: false) - - # JobParser returns this, `BenchmarkDriver::Runner.runner_for` searches "*::Job" - Job = Class.new(BenchmarkDriver::DefaultJob) - # Dynamically fetched and used by `BenchmarkDriver::JobParser.parse` - JobParser = BenchmarkDriver::DefaultJobParser.for(klass: Job, metrics: [METRIC]) - - private - - # Overriding BenchmarkDriver::Runner::Total#metric - def metric - METRIC - end - - # Overriding BenchmarkDriver::Runner::Total#target - def target - :cutime - end -end diff --git a/test/testdata/ruby_benchmark/disabled/lib/benchmark_driver/runner/peak.rb b/test/testdata/ruby_benchmark/disabled/lib/benchmark_driver/runner/peak.rb deleted file mode 100644 index 3e53df9c86..0000000000 --- a/test/testdata/ruby_benchmark/disabled/lib/benchmark_driver/runner/peak.rb +++ /dev/null @@ -1,154 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -require 'benchmark_driver/struct' -require 'benchmark_driver/metric' -require 'benchmark_driver/default_job' -require 'benchmark_driver/default_job_parser' -require 'tempfile' - -class BenchmarkDriver::Runner::Peak - METRIC = BenchmarkDriver::Metric.new( - name: 'Peak memory usage', unit: 'bytes', larger_better: false, worse_word: 'larger', - ) - - # JobParser returns this, `BenchmarkDriver::Runner.runner_for` searches "*::Job" - Job = Class.new(BenchmarkDriver::DefaultJob) - # Dynamically fetched and used by `BenchmarkDriver::JobParser.parse` - JobParser = BenchmarkDriver::DefaultJobParser.for(klass: Job, metrics: [METRIC]) - - # @param [BenchmarkDriver::Config::RunnerConfig] config - # @param [BenchmarkDriver::Output] output - # @param [BenchmarkDriver::Context] contexts - def initialize(config:, output:, contexts:) - @config = config - @output = output - @contexts = contexts - end - - # This method is dynamically called by `BenchmarkDriver::JobRunner.run` - # @param [Array] jobs - def run(jobs) - if jobs.any? { |job| job.loop_count.nil? } - jobs = jobs.map do |job| - job.loop_count ? job : Job.new(job.to_h.merge(loop_count: 1)) - end - end - - @output.with_benchmark do - jobs.each do |job| - @output.with_job(name: job.name) do - job.runnable_contexts(@contexts).each do |context| - value = BenchmarkDriver::Repeater.with_repeat(config: @config, larger_better: false) do - run_benchmark(job, context: context) - end - @output.with_context(name: context.name, executable: context.executable, gems: context.gems, prelude: context.prelude) do - @output.report(values: { metric => value }, loop_count: job.loop_count) - end - end - end - end - end - end - - private - - # @param [BenchmarkDriver::Runner::Ips::Job] job - loop_count is not nil - # @param [BenchmarkDriver::Context] context - # @return [BenchmarkDriver::Metrics] - def run_benchmark(job, context:) - benchmark = BenchmarkScript.new( - preludes: [context.prelude, job.prelude], - script: job.script, - teardown: job.teardown, - loop_count: job.loop_count, - ) - - memory_status = File.expand_path('../../../../test/lib/memory_status', __dir__) - Tempfile.open(['benchmark_driver-', '.rb']) do |f| - with_script(benchmark.render) do |path| - output = IO.popen([*context.executable.command, path, f.path, target, memory_status], &:read) - if $?.success? - Integer(f.read) - else - $stdout.print(output) - BenchmarkDriver::Result::ERROR - end - end - end - end - - # Overridden by BenchmarkDriver::Runner::Size - def target - 'peak' - end - - # Overridden by BenchmarkDriver::Runner::Size - def metric - METRIC - end - - def with_script(script) - if @config.verbose >= 2 - sep = '-' * 30 - $stdout.puts "\n\n#{sep}[Script begin]#{sep}\n#{script}#{sep}[Script end]#{sep}\n\n" - end - - Tempfile.open(['benchmark_driver-', '.rb']) do |f| - f.puts script - f.close - return yield(f.path) - end - end - - # @param [String] prelude - # @param [String] script - # @param [String] teardown - # @param [Integer] loop_count - BenchmarkScript = ::BenchmarkDriver::Struct.new(:preludes, :script, :teardown, :loop_count) do - def render - prelude = preludes.reject(&:nil?).reject(&:empty?).join("\n") - <<-RUBY -#{prelude} -#{while_loop(script, loop_count)} -#{teardown} - -result_file, target, memory_status = ARGV -require_relative memory_status - -ms = Memory::Status.new -case target.to_sym -when :peak - key = ms.respond_to?(:hwm) ? :hwm : :peak -when :size - key = ms.respond_to?(:rss) ? :rss : :size -else - raise('unexpected target: ' + target) -end - -File.write(result_file, ms[key]) - RUBY - end - - private - - def while_loop(content, times) - if !times.is_a?(Integer) || times <= 0 - raise ArgumentError.new("Unexpected times: #{times.inspect}") - end - - if times > 1 - <<-RUBY -__bmdv_i = 0 -while __bmdv_i < #{times} - #{content} - __bmdv_i += 1 -end - RUBY - else - content - end - end - end - private_constant :BenchmarkScript -end diff --git a/test/testdata/ruby_benchmark/disabled/lib/benchmark_driver/runner/size.rb b/test/testdata/ruby_benchmark/disabled/lib/benchmark_driver/runner/size.rb deleted file mode 100644 index af1528db4b..0000000000 --- a/test/testdata/ruby_benchmark/disabled/lib/benchmark_driver/runner/size.rb +++ /dev/null @@ -1,28 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -require 'benchmark_driver/runner/peak' - -# Actually the same as BenchmarkDriver::Runner::Memory -class BenchmarkDriver::Runner::Size < BenchmarkDriver::Runner::Peak - METRIC = BenchmarkDriver::Metric.new( - name: 'Max resident set size', unit: 'bytes', larger_better: false, worse_word: 'larger', - ) - - # JobParser returns this, `BenchmarkDriver::Runner.runner_for` searches "*::Job" - Job = Class.new(BenchmarkDriver::DefaultJob) - # Dynamically fetched and used by `BenchmarkDriver::JobParser.parse` - JobParser = BenchmarkDriver::DefaultJobParser.for(klass: Job, metrics: [METRIC]) - - private - - # Overriding BenchmarkDriver::Runner::Peak#metric - def metric - METRIC - end - - # Overriding BenchmarkDriver::Runner::Peak#target - def target - 'size' - end -end diff --git a/test/testdata/ruby_benchmark/disabled/lib/benchmark_driver/runner/stime.rb b/test/testdata/ruby_benchmark/disabled/lib/benchmark_driver/runner/stime.rb deleted file mode 100644 index 4bffa8327b..0000000000 --- a/test/testdata/ruby_benchmark/disabled/lib/benchmark_driver/runner/stime.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -require 'benchmark_driver/runner/total' - -class BenchmarkDriver::Runner::Stime < BenchmarkDriver::Runner::Total - METRIC = BenchmarkDriver::Metric.new(name: 'stime', unit: 's', larger_better: false) - - # JobParser returns this, `BenchmarkDriver::Runner.runner_for` searches "*::Job" - Job = Class.new(BenchmarkDriver::DefaultJob) - # Dynamically fetched and used by `BenchmarkDriver::JobParser.parse` - JobParser = BenchmarkDriver::DefaultJobParser.for(klass: Job, metrics: [METRIC]) - - private - - # Overriding BenchmarkDriver::Runner::Total#metric - def metric - METRIC - end - - # Overriding BenchmarkDriver::Runner::Total#target - def target - :stime - end -end diff --git a/test/testdata/ruby_benchmark/disabled/lib/benchmark_driver/runner/total.rb b/test/testdata/ruby_benchmark/disabled/lib/benchmark_driver/runner/total.rb deleted file mode 100644 index c88bed9a42..0000000000 --- a/test/testdata/ruby_benchmark/disabled/lib/benchmark_driver/runner/total.rb +++ /dev/null @@ -1,140 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -require 'benchmark_driver/struct' -require 'benchmark_driver/metric' -require 'benchmark_driver/default_job' -require 'benchmark_driver/default_job_parser' -require 'tempfile' - -class BenchmarkDriver::Runner::Total - METRIC = BenchmarkDriver::Metric.new(name: 'Total time', unit: 's', larger_better: false) - - # JobParser returns this, `BenchmarkDriver::Runner.runner_for` searches "*::Job" - Job = Class.new(BenchmarkDriver::DefaultJob) - # Dynamically fetched and used by `BenchmarkDriver::JobParser.parse` - JobParser = BenchmarkDriver::DefaultJobParser.for(klass: Job, metrics: [METRIC]) - - # @param [BenchmarkDriver::Config::RunnerConfig] config - # @param [BenchmarkDriver::Output] output - # @param [BenchmarkDriver::Context] contexts - def initialize(config:, output:, contexts:) - @config = config - @output = output - @contexts = contexts - end - - # This method is dynamically called by `BenchmarkDriver::JobRunner.run` - # @param [Array] jobs - def run(jobs) - if jobs.any? { |job| job.loop_count.nil? } - raise 'missing loop_count is not supported in Ruby repository' - end - - @output.with_benchmark do - jobs.each do |job| - @output.with_job(name: job.name) do - job.runnable_contexts(@contexts).each do |context| - duration = BenchmarkDriver::Repeater.with_repeat(config: @config, larger_better: false) do - run_benchmark(job, context: context) - end - @output.with_context(name: context.name, executable: context.executable, gems: context.gems, prelude: context.prelude) do - @output.report(values: { metric => duration }, duration: duration, loop_count: job.loop_count) - end - end - end - end - end - end - - private - - # @param [BenchmarkDriver::Runner::Ips::Job] job - loop_count is not nil - # @param [BenchmarkDriver::Context] context - # @return [BenchmarkDriver::Metrics] - def run_benchmark(job, context:) - benchmark = BenchmarkScript.new( - preludes: [context.prelude, job.prelude], - script: job.script, - teardown: job.teardown, - loop_count: job.loop_count, - ) - - Tempfile.open(['benchmark_driver-', '.rb']) do |f| - with_script(benchmark.render(result: f.path, target: target)) do |path| - IO.popen([*context.executable.command, path], &:read) # TODO: print stdout if verbose=2 - if $?.success? - Float(f.read) - else - BenchmarkDriver::Result::ERROR - end - end - end - end - - # This method is overridden by some subclasses - def metric - METRIC - end - - # This method is overridden by some subclasses - def target - :total - end - - def with_script(script) - if @config.verbose >= 2 - sep = '-' * 30 - $stdout.puts "\n\n#{sep}[Script begin]#{sep}\n#{script}#{sep}[Script end]#{sep}\n\n" - end - - Tempfile.open(['benchmark_driver-', '.rb']) do |f| - f.puts script - f.close - return yield(f.path) - end - end - - # @param [String] prelude - # @param [String] script - # @param [String] teardown - # @param [Integer] loop_count - BenchmarkScript = ::BenchmarkDriver::Struct.new(:preludes, :script, :teardown, :loop_count) do - # @param [String] result - A file to write result - def render(result:, target:) - prelude = preludes.reject(&:nil?).reject(&:empty?).join("\n") - <<-RUBY -#{prelude} - -require 'benchmark' -__bmdv_result = Benchmark.measure { - #{while_loop(script, loop_count)} -} - -#{teardown} - -File.write(#{result.dump}, __bmdv_result.#{target}) - RUBY - end - - private - - def while_loop(content, times) - if !times.is_a?(Integer) || times <= 0 - raise ArgumentError.new("Unexpected times: #{times.inspect}") - elsif times == 1 - return content - end - - # TODO: execute in batch - <<-RUBY -__bmdv_i = 0 -while __bmdv_i < #{times} - #{content} - __bmdv_i += 1 -end - RUBY - end - end - private_constant :BenchmarkScript -end diff --git a/test/testdata/ruby_benchmark/disabled/lib/benchmark_driver/runner/utime.rb b/test/testdata/ruby_benchmark/disabled/lib/benchmark_driver/runner/utime.rb deleted file mode 100644 index 3ed6b36316..0000000000 --- a/test/testdata/ruby_benchmark/disabled/lib/benchmark_driver/runner/utime.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -require 'benchmark_driver/runner/total' - -class BenchmarkDriver::Runner::Utime < BenchmarkDriver::Runner::Total - METRIC = BenchmarkDriver::Metric.new(name: 'utime', unit: 's', larger_better: false) - - # JobParser returns this, `BenchmarkDriver::Runner.runner_for` searches "*::Job" - Job = Class.new(BenchmarkDriver::DefaultJob) - # Dynamically fetched and used by `BenchmarkDriver::JobParser.parse` - JobParser = BenchmarkDriver::DefaultJobParser.for(klass: Job, metrics: [METRIC]) - - private - - # Overriding BenchmarkDriver::Runner::Total#metric - def metric - METRIC - end - - # Overriding BenchmarkDriver::Runner::Total#target - def target - :utime - end -end diff --git a/test/testdata/ruby_benchmark/disabled/lib/load.rb b/test/testdata/ruby_benchmark/disabled/lib/load.rb deleted file mode 100755 index 0192ad94d9..0000000000 --- a/test/testdata/ruby_benchmark/disabled/lib/load.rb +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true -# typed: strict -# compiled: true -$:.unshift(File.join(__dir__, '../benchmark-driver/lib')) -require 'benchmark_driver' diff --git a/test/testdata/ruby_benchmark/disabled/other-lang/eval.rb b/test/testdata/ruby_benchmark/disabled/other-lang/eval.rb deleted file mode 100644 index 3d57d3172a..0000000000 --- a/test/testdata/ruby_benchmark/disabled/other-lang/eval.rb +++ /dev/null @@ -1,69 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -Bench = %w( - loop - ack - fib - tak - fact -) - -Lang = < 1.0e-17 then - v.x = v.x / len - v.y = v.y / len - v.z = v.z / len - end - v - end -end - - -class Sphere - def initialize(center, radius) - @center = center - @radius = radius - end - - attr_reader :center, :radius - - def intersect(ray, isect) - rs = ray.org.vsub(@center) - b = rs.vdot(ray.dir) - c = rs.vdot(rs) - (@radius * @radius) - d = b * b - c - if d > 0.0 then - t = - b - Math.sqrt(d) - - if t > 0.0 and t < isect.t then - isect.t = t - isect.hit = true - isect.pl = Vec.new(ray.org.x + ray.dir.x * t, - ray.org.y + ray.dir.y * t, - ray.org.z + ray.dir.z * t) - n = isect.pl.vsub(@center) - isect.n = n.vnormalize - else - 0.0 - end - end - nil - end -end - -class Plane - def initialize(p, n) - @p = p - @n = n - end - - def intersect(ray, isect) - d = -@p.vdot(@n) - v = ray.dir.vdot(@n) - v0 = v - if v < 0.0 then - v0 = -v - end - if v0 < 1.0e-17 then - return - end - - t = -(ray.org.vdot(@n) + d) / v - - if t > 0.0 and t < isect.t then - isect.hit = true - isect.t = t - isect.n = @n - isect.pl = Vec.new(ray.org.x + t * ray.dir.x, - ray.org.y + t * ray.dir.y, - ray.org.z + t * ray.dir.z) - end - nil - end -end - -class Ray - def initialize(org, dir) - @org = org - @dir = dir - end - - attr_accessor :org, :dir -end - -class Isect - def initialize - @t = 10000000.0 - @hit = false - @pl = Vec.new(0.0, 0.0, 0.0) - @n = Vec.new(0.0, 0.0, 0.0) - end - - attr_accessor :t, :hit, :pl, :n -end - -def clamp(f) - i = f * 255.5 - if i > 255.0 then - i = 255.0 - end - if i < 0.0 then - i = 0.0 - end - i.to_i -end - -def otherBasis(basis, n) - basis[2] = Vec.new(n.x, n.y, n.z) - basis[1] = Vec.new(0.0, 0.0, 0.0) - - if n.x < 0.6 and n.x > -0.6 then - basis[1].x = 1.0 - elsif n.y < 0.6 and n.y > -0.6 then - basis[1].y = 1.0 - elsif n.z < 0.6 and n.z > -0.6 then - basis[1].z = 1.0 - else - basis[1].x = 1.0 - end - - basis[0] = basis[1].vcross(basis[2]) - basis[0] = basis[0].vnormalize - - basis[1] = basis[2].vcross(basis[0]) - basis[1] = basis[1].vnormalize -end - -class Scene - def initialize - @spheres = Array.new - @spheres[0] = Sphere.new(Vec.new(-2.0, 0.0, -3.5), 0.5) - @spheres[1] = Sphere.new(Vec.new(-0.5, 0.0, -3.0), 0.5) - @spheres[2] = Sphere.new(Vec.new(1.0, 0.0, -2.2), 0.5) - @plane = Plane.new(Vec.new(0.0, -0.5, 0.0), Vec.new(0.0, 1.0, 0.0)) - end - - def ambient_occlusion(isect) - basis = Array.new - otherBasis(basis, isect.n) - - ntheta = NAO_SAMPLES - nphi = NAO_SAMPLES - eps = 0.0001 - occlusion = 0.0 - - p0 = Vec.new(isect.pl.x + eps * isect.n.x, - isect.pl.y + eps * isect.n.y, - isect.pl.z + eps * isect.n.z) - nphi.times do |j| - ntheta.times do |i| - r = rand - phi = 2.0 * 3.14159265 * rand - x = Math.cos(phi) * Math.sqrt(1.0 - r) - y = Math.sin(phi) * Math.sqrt(1.0 - r) - z = Math.sqrt(r) - - rx = x * basis[0].x + y * basis[1].x + z * basis[2].x - ry = x * basis[0].y + y * basis[1].y + z * basis[2].y - rz = x * basis[0].z + y * basis[1].z + z * basis[2].z - - raydir = Vec.new(rx, ry, rz) - ray = Ray.new(p0, raydir) - - occisect = Isect.new - @spheres[0].intersect(ray, occisect) - @spheres[1].intersect(ray, occisect) - @spheres[2].intersect(ray, occisect) - @plane.intersect(ray, occisect) - if occisect.hit then - occlusion = occlusion + 1.0 - else - 0.0 - end - end - end - - occlusion = (ntheta.to_f * nphi.to_f - occlusion) / (ntheta.to_f * nphi.to_f) - - Vec.new(occlusion, occlusion, occlusion) - end - - def render(w, h, nsubsamples) - cnt = 0 - nsf = nsubsamples.to_f - h.times do |y| - w.times do |x| - rad = Vec.new(0.0, 0.0, 0.0) - - # Subsampling - nsubsamples.times do |v| - nsubsamples.times do |u| - - cnt = cnt + 1 - wf = w.to_f - hf = h.to_f - xf = x.to_f - yf = y.to_f - uf = u.to_f - vf = v.to_f - - px = (xf + (uf / nsf) - (wf / 2.0)) / (wf / 2.0) - py = -(yf + (vf / nsf) - (hf / 2.0)) / (hf / 2.0) - - eye = Vec.new(px, py, -1.0).vnormalize - - ray = Ray.new(Vec.new(0.0, 0.0, 0.0), eye) - - isect = Isect.new - @spheres[0].intersect(ray, isect) - @spheres[1].intersect(ray, isect) - @spheres[2].intersect(ray, isect) - @plane.intersect(ray, isect) - if isect.hit then - col = ambient_occlusion(isect) - rad.x = rad.x + col.x - rad.y = rad.y + col.y - rad.z = rad.z + col.z - end - end - end - - r = rad.x / (nsf * nsf) - g = rad.y / (nsf * nsf) - b = rad.z / (nsf * nsf) - printf("%c", clamp(r)) - printf("%c", clamp(g)) - printf("%c", clamp(b)) - end - nil - end - - nil - end -end - -alias printf_orig printf -def printf *args - # $fp.printf(*args) -end - -# File.open("ao.ppm", "w") do |fp| - # $fp = fp - printf("P6\n") - printf("%d %d\n", IMAGE_WIDTH, IMAGE_HEIGHT) - printf("255\n") - Scene.new.render(IMAGE_WIDTH, IMAGE_HEIGHT, NSUBSAMPLES) -# end - -undef printf -alias printf printf_orig diff --git a/test/testdata/ruby_benchmark/disabled/too_slow/app_factorial.rb b/test/testdata/ruby_benchmark/disabled/too_slow/app_factorial.rb deleted file mode 100644 index 4f1782fe52..0000000000 --- a/test/testdata/ruby_benchmark/disabled/too_slow/app_factorial.rb +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true -# typed: strict -# compiled: true -def fact(n) - if(n > 1) - n * fact(n-1) - else - 1 - end -end - -100.times { - fact(5000) -} diff --git a/test/testdata/ruby_benchmark/disabled/too_slow/app_lc_fizzbuzz.rb b/test/testdata/ruby_benchmark/disabled/too_slow/app_lc_fizzbuzz.rb deleted file mode 100644 index d59534acc4..0000000000 --- a/test/testdata/ruby_benchmark/disabled/too_slow/app_lc_fizzbuzz.rb +++ /dev/null @@ -1,55 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -# -# FizzBuzz program using only lambda calculus -# -# This program is quoted from -# "Understanding Computation" by Tom Stuart -# http://computationbook.com/ -# -# You can understand why this program works fine by reading this book. -# - -solution = -> k { -> f { -> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> l { -> x { -> g { -> b { b }[-> p { p[-> x { -> y { x } }] }[l]][x][-> y { g[f[-> l { -> p { p[-> x { -> y { y } }] }[-> p { p[-> x { -> y { y } }] }[l]] }[l]][x][g]][-> l { -> p { p[-> x { -> y { x } }] }[-> p { p[-> x { -> y { y } }] }[l]] }[l]][y] }] } } } }][k][-> x { -> y { -> f { f[x][y] } } }[-> x { -> y { x } }][-> x { -> y { x } }]][-> l { -> x { -> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[l][f[x]] } }] } }[-> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> m { -> n { -> b { b }[-> m { -> n { -> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]] } }[m][n]][-> x { -> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[f[-> n { -> p { -> x { p[n[p][x]] } } }[m]][n]][m][x] }][-> x { -> y { -> f { f[x][y] } } }[-> x { -> y { x } }][-> x { -> y { x } }]] } } }][-> p { -> x { p[x] } }][-> p { -> x { p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[x]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] } }]][-> n { -> b { b }[-> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> m { -> n { -> b { b }[-> m { -> n { -> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]] } }[n][m]][-> x { f[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]][n][x] }][m] } } }][n][-> p { -> x { p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[x]]]]]]]]]]]]]]] } }]]][-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> x { -> y { -> f { f[x][y] } } }[-> x { -> y { x } }][-> x { -> y { x } }]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]][-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]][-> b { b }[-> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> m { -> n { -> b { b }[-> m { -> n { -> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]] } }[n][m]][-> x { f[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]][n][x] }][m] } } }][n][-> p { -> x { p[p[p[x]]] } }]]][-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> x { -> y { -> f { f[x][y] } } }[-> x { -> y { x } }][-> x { -> y { x } }]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]][-> b { b }[-> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> m { -> n { -> b { b }[-> m { -> n { -> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]] } }[n][m]][-> x { f[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]][n][x] }][m] } } }][n][-> p { -> x { p[p[p[p[p[x]]]]] } }]]][-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> x { -> y { -> f { f[x][y] } } }[-> x { -> y { x } }][-> x { -> y { x } }]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]][-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]][-> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> n { -> l { -> x { -> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> l { -> x { -> g { -> b { b }[-> p { p[-> x { -> y { x } }] }[l]][x][-> y { g[f[-> l { -> p { p[-> x { -> y { y } }] }[-> p { p[-> x { -> y { y } }] }[l]] }[l]][x][g]][-> l { -> p { p[-> x { -> y { x } }] }[-> p { p[-> x { -> y { y } }] }[l]] }[l]][y] }] } } } }][l][-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> x { -> y { -> f { f[x][y] } } }[-> x { -> y { x } }][-> x { -> y { x } }]][x]][-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }] } }[-> b { b }[-> m { -> n { -> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]] } }[n][-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]][-> x { -> y { -> f { f[x][y] } } }[-> x { -> y { x } }][-> x { -> y { x } }]][-> x { f[-> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> m { -> n { -> b { b }[-> m { -> n { -> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]] } }[n][m]][-> x { -> n { -> p { -> x { p[n[p][x]] } } }[f[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]][n]][x] }][-> p { -> x { x } }] } } }][n][-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]][x] }]][-> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> m { -> n { -> b { b }[-> m { -> n { -> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]] } }[n][m]][-> x { f[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]][n][x] }][m] } } }][n][-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]] } }][n]]]] }] - -FIRST = -> l { LEFT[RIGHT[l]] } -IF = -> b { b } -LEFT = -> p { p[-> x { -> y { x } } ] } -RIGHT = -> p { p[-> x { -> y { y } } ] } -IS_EMPTY = LEFT -REST = -> l { RIGHT[RIGHT[l]] } - -def to_integer(proc) - proc[-> n { n + 1 }][0] -end - -def to_boolean(proc) - IF[proc][true][false] -end - -def to_array(proc) - array = [] - - until to_boolean(IS_EMPTY[proc]) - array.push(FIRST[proc]) - proc = REST[proc] - end - - array -end - -def to_char(c) - '0123456789BFiuz'.slice(to_integer(c)) -end - -def to_string(s) - to_array(s).map { |c| to_char(c) }.join -end - -answer = to_array(solution).map do |p| - to_string(p) -end - -answer_ary = answer.to_a -# puts answer_ary diff --git a/test/testdata/ruby_benchmark/disabled/too_slow/app_pentomino.rb b/test/testdata/ruby_benchmark/disabled/too_slow/app_pentomino.rb deleted file mode 100644 index 1d3a04513c..0000000000 --- a/test/testdata/ruby_benchmark/disabled/too_slow/app_pentomino.rb +++ /dev/null @@ -1,133 +0,0 @@ -# frozen_string_literal: true -#!/usr/local/bin/ruby -# typed: true -# compiled: true -# This program is contributed by Shin Nishiyama - - -# modified by K.Sasada - -NP = 5 -ROW = 8 + NP -COL = 8 - -$p = [] -$b = [] -$no = 0 - -def piece(n, a, nb) - nb.each{|x| - a[n] = x - if n == NP-1 - $p << [a.sort] - else - nbc=nb.dup - [-ROW, -1, 1, ROW].each{|d| - if x+d > 0 and not a.include?(x+d) and not nbc.include?(x+d) - nbc << x+d - end - } - nbc.delete x - piece(n+1,a[0..n],nbc) - end - } -end - -def kikaku(a) - a.collect {|x| x - a[0]} -end -def ud(a) - kikaku(a.collect {|x| ((x+NP)%ROW)-ROW*((x+NP)/ROW) }.sort) -end -def rl(a) - kikaku(a.collect {|x| ROW*((x+NP)/ROW)+ROW-((x+NP)%ROW)}.sort) -end -def xy(a) - kikaku(a.collect {|x| ROW*((x+NP)%ROW) + (x+NP)/ROW }.sort) -end - -def mkpieces - piece(0,[],[0]) - $p.each do |a| - a0 = a[0] - a[1] = ud(a0) - a[2] = rl(a0) - a[3] = ud(rl(a0)) - a[4] = xy(a0) - a[5] = ud(xy(a0)) - a[6] = rl(xy(a0)) - a[7] = ud(rl(xy(a0))) - a.sort! - a.uniq! - end - $p.uniq!.sort! {|x,y| x[0] <=> y[0] } -end - -def mkboard - (0...ROW*COL).each{|i| - if i % ROW >= ROW-NP - $b[i] = -2 - else - $b[i] = -1 - end - $b[3*ROW+3]=$b[3*ROW+4]=$b[4*ROW+3]=$b[4*ROW+4]=-2 - } -end - -def pboard - return # skip print - print "No. #$no\n" - (0...COL).each{|i| - print "|" - (0...ROW-NP).each{|j| - x = $b[i*ROW+j] - if x < 0 - print "..|" - else - printf "%2d|",x+1 - end - } - print "\n" - } - print "\n" -end - -$pnum=[] -def setpiece(a,pos) - if a.length == $p.length then - $no += 1 - pboard - return - end - while $b[pos] != -1 - pos += 1 - end - ($pnum - a).each do |i| - $p[i].each do |x| - f = 0 - x.each{|s| - if $b[pos+s] != -1 - f=1 - break - end - } - if f == 0 then - x.each{|s| - $b[pos+s] = i - } - a << i - setpiece(a.dup, pos) - a.pop - x.each{|s| - $b[pos+s] = -1 - } - end - end - end -end - -mkpieces -mkboard -$p[4] = [$p[4][0]] -$pnum = (0...$p.length).to_a -setpiece([],0) diff --git a/test/testdata/ruby_benchmark/disabled/too_slow/app_tak.rb b/test/testdata/ruby_benchmark/disabled/too_slow/app_tak.rb deleted file mode 100644 index 1015bce110..0000000000 --- a/test/testdata/ruby_benchmark/disabled/too_slow/app_tak.rb +++ /dev/null @@ -1,16 +0,0 @@ -# frozen_string_literal: true -# typed: strict -# compiled: true - -def tak x, y, z - unless y < x - z - else - tak( tak(x-1, y, z), - tak(y-1, z, x), - tak(z-1, x, y)) - end -end - -tak(18, 9, 0) - diff --git a/test/testdata/ruby_benchmark/disabled/too_slow/array_shift.rb b/test/testdata/ruby_benchmark/disabled/too_slow/array_shift.rb deleted file mode 100644 index e68a602f01..0000000000 --- a/test/testdata/ruby_benchmark/disabled/too_slow/array_shift.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -require 'benchmark' - -Benchmark.bm do |x| - [10_000,1_000_000,100_000_000].each do |n| - ary = Array.new(n,0) - GC.start - x.report("#{n}:shift"){ ary.shift } - (0..4).each do |i| - ary = Array.new(n,0) - GC.start - x.report("#{n}:shift(#{i})"){ ary.shift(i) } - end - end -end diff --git a/test/testdata/ruby_benchmark/disabled/too_slow/array_sort_block.rb b/test/testdata/ruby_benchmark/disabled/too_slow/array_sort_block.rb deleted file mode 100644 index 0d169e952f..0000000000 --- a/test/testdata/ruby_benchmark/disabled/too_slow/array_sort_block.rb +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true -# typed: strict -# compiled: true -ary = Array.new(1000) { rand(1000) } -10000.times { ary.sort { |a, b| a <=> b } } diff --git a/test/testdata/ruby_benchmark/disabled/too_slow/fiber_chain.rb b/test/testdata/ruby_benchmark/disabled/too_slow/fiber_chain.rb deleted file mode 100755 index 78d1e2357a..0000000000 --- a/test/testdata/ruby_benchmark/disabled/too_slow/fiber_chain.rb +++ /dev/null @@ -1,43 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -# Check performance of fiber creation and transfer. - -def make_link(previous) - Fiber.new do - while message = previous.resume - Fiber.yield(message) - end - end -end - -def make_chain(length, &block) - chain = Fiber.new(&block) - - (length - 1).times do - chain = make_link(chain) - end - - return chain -end - -def run_benchmark(length, repeats, message = :hello) - chain = nil - - chain = make_chain(length) do - while true - Fiber.yield(message) - end - end - - repeats.times do - abort "invalid result" unless chain.resume == message - end -end - -n = (ARGV[0] || 1000).to_i -m = (ARGV[1] || 1000).to_i - -5.times do - run_benchmark(n, m) -end diff --git a/test/testdata/ruby_benchmark/disabled/too_slow/file_chmod.rb b/test/testdata/ruby_benchmark/disabled/too_slow/file_chmod.rb deleted file mode 100644 index acc631e4e6..0000000000 --- a/test/testdata/ruby_benchmark/disabled/too_slow/file_chmod.rb +++ /dev/null @@ -1,12 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -# chmod file -require 'tempfile' -max = 200_000 -tmp = Tempfile.new('chmod') -path = tmp.path -max.times do - File.chmod(0777, path) -end -tmp.close! diff --git a/test/testdata/ruby_benchmark/disabled/too_slow/file_rename.rb b/test/testdata/ruby_benchmark/disabled/too_slow/file_rename.rb deleted file mode 100644 index c44b344f3d..0000000000 --- a/test/testdata/ruby_benchmark/disabled/too_slow/file_rename.rb +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -# rename file -require 'tempfile' - -max = 100_000 -tmp = [ Tempfile.new('rename-a'), Tempfile.new('rename-b') ] -a, b = tmp.map { |x| x.path } -tmp.each { |t| t.close } # Windows can't rename files without closing them -max.times do - File.rename(a, b) - File.rename(b, a) -end diff --git a/test/testdata/ruby_benchmark/disabled/too_slow/gc/aobench.rb b/test/testdata/ruby_benchmark/disabled/too_slow/gc/aobench.rb deleted file mode 100644 index 3b7904dc82..0000000000 --- a/test/testdata/ruby_benchmark/disabled/too_slow/gc/aobench.rb +++ /dev/null @@ -1,4 +0,0 @@ -# frozen_string_literal: true -# typed: strong -# compiled: true -require_relative '../app_aobench' diff --git a/test/testdata/ruby_benchmark/disabled/too_slow/gc/binary_trees.rb b/test/testdata/ruby_benchmark/disabled/too_slow/gc/binary_trees.rb deleted file mode 100644 index d8de68a19c..0000000000 --- a/test/testdata/ruby_benchmark/disabled/too_slow/gc/binary_trees.rb +++ /dev/null @@ -1,4 +0,0 @@ -# frozen_string_literal: true -# typed: strong -# compiled: true -require_relative '../so_binary_trees' diff --git a/test/testdata/ruby_benchmark/disabled/too_slow/gc/gcbench.rb b/test/testdata/ruby_benchmark/disabled/too_slow/gc/gcbench.rb deleted file mode 100644 index 18cf92b7b7..0000000000 --- a/test/testdata/ruby_benchmark/disabled/too_slow/gc/gcbench.rb +++ /dev/null @@ -1,60 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -require 'benchmark' -require 'pp' -require 'optparse' - -$list = true -$gcprof = false - -opt = OptionParser.new -opt.on('-q'){$list = false} -opt.on('-d'){$gcprof = false} -opt.on('-p'){$gcprof = true} -opt.parse!(ARGV) - -script = File.join(File.dirname(__FILE__), ARGV.shift) -script += '.rb' unless FileTest.exist?(script) -raise "#{script} not found" unless FileTest.exist?(script) - -puts "Script: #{script}" - -if $gcprof - GC::Profiler.enable -end - -tms = Benchmark.measure{|x| - load script -} - -gc_time = 0 - -if $gcprof - gc_time = GC::Profiler.total_time - GC::Profiler.report if $list and RUBY_VERSION >= '2.0.0' # before 1.9.3, report() may run infinite loop - GC::Profiler.disable -end - -pp GC.stat - -puts "#{RUBY_DESCRIPTION} #{GC::OPTS.inspect}" if defined?(GC::OPTS) - -desc = "#{RUBY_VERSION}#{RUBY_PATCHLEVEL >= 0 ? "p#{RUBY_PATCHLEVEL}" : "dev"}" -name = File.basename(script, '.rb') - -puts -puts script -puts Benchmark::CAPTION -puts tms -puts "GC total time (sec): #{gc_time}" - -# show High-Water Mark on Linux -if File.exist?('/proc/self/status') && /VmHWM:\s*(\d+.+)/ =~ File.read('/proc/self/status') - puts - puts "VmHWM: #{$1.chomp}" -end - -puts -puts "Summary of #{name} on #{desc}\t#{tms.real}\t#{gc_time}\t#{GC.count}" -puts " (real time in sec, GC time in sec, GC count)" diff --git a/test/testdata/ruby_benchmark/disabled/too_slow/gc/hash2.rb b/test/testdata/ruby_benchmark/disabled/too_slow/gc/hash2.rb deleted file mode 100644 index 13803fc348..0000000000 --- a/test/testdata/ruby_benchmark/disabled/too_slow/gc/hash2.rb +++ /dev/null @@ -1,10 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -value = 0.01 -h = {} -n = 4*(10**6) - -1.upto(n){|i| - h["%020d" % i] = value * i -} diff --git a/test/testdata/ruby_benchmark/disabled/too_slow/gc/pentomino.rb b/test/testdata/ruby_benchmark/disabled/too_slow/gc/pentomino.rb deleted file mode 100644 index 9c3b9a5d21..0000000000 --- a/test/testdata/ruby_benchmark/disabled/too_slow/gc/pentomino.rb +++ /dev/null @@ -1,4 +0,0 @@ -# frozen_string_literal: true -# typed: strong -# compiled: true -require_relative '../app_pentomino' diff --git a/test/testdata/ruby_benchmark/disabled/too_slow/gc/redblack.rb b/test/testdata/ruby_benchmark/disabled/too_slow/gc/redblack.rb deleted file mode 100644 index 17c6d3c75c..0000000000 --- a/test/testdata/ruby_benchmark/disabled/too_slow/gc/redblack.rb +++ /dev/null @@ -1,369 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -# This benchmark is imported from https://github.com/jruby/rubybench/blob/master/time/bench_red_black.rb -# License is License is Apache-2 - -require 'benchmark' - -# Algorithm based on "Introduction to Algorithms" by Cormen and others -class RedBlackTree - class Node - attr_accessor :color - attr_accessor :key - attr_accessor :left - attr_accessor :right - attr_accessor :parent - - RED = :red - BLACK = :black - COLORS = [RED, BLACK].freeze - - def initialize(key, color = RED) - raise ArgumentError, "Bad value for color parameter" unless COLORS.include?(color) - @color = color - @key = key - @left = @right = @parent = NilNode.instance - end - - def black? - return color == BLACK - end - - def red? - return color == RED - end - end - - class NilNode < Node - class << self - private :new - - # it's not thread safe - def instance - @instance ||= begin - def instance - return @instance - end - - new - end - end - end - - def initialize - self.color = BLACK - self.key = 0 - self.left = nil - self.right = nil - self.parent = nil - end - - def nil? - return true - end - end - - include Enumerable - - attr_accessor :root - attr_accessor :size - - def initialize - self.root = NilNode.instance - self.size = 0 - end - - def add(key) - insert(Node.new(key)) - end - - def insert(x) - insert_helper(x) - - x.color = Node::RED - while x != root && x.parent.color == Node::RED - if x.parent == x.parent.parent.left - y = x.parent.parent.right - if !y.nil? && y.color == Node::RED - x.parent.color = Node::BLACK - y.color = Node::BLACK - x.parent.parent.color = Node::RED - x = x.parent.parent - else - if x == x.parent.right - x = x.parent - left_rotate(x) - end - x.parent.color = Node::BLACK - x.parent.parent.color = Node::RED - right_rotate(x.parent.parent) - end - else - y = x.parent.parent.left - if !y.nil? && y.color == Node::RED - x.parent.color = Node::BLACK - y.color = Node::BLACK - x.parent.parent.color = Node::RED - x = x.parent.parent - else - if x == x.parent.left - x = x.parent - right_rotate(x) - end - x.parent.color = Node::BLACK - x.parent.parent.color = Node::RED - left_rotate(x.parent.parent) - end - end - end - root.color = Node::BLACK - end - - alias << insert - - def delete(z) - y = (z.left.nil? || z.right.nil?) ? z : successor(z) - x = y.left.nil? ? y.right : y.left - x.parent = y.parent - - if y.parent.nil? - self.root = x - else - if y == y.parent.left - y.parent.left = x - else - y.parent.right = x - end - end - - z.key = y.key if y != z - - if y.color == Node::BLACK - delete_fixup(x) - end - - self.size -= 1 - return y - end - - def minimum(x = root) - while !x.left.nil? - x = x.left - end - return x - end - - def maximum(x = root) - while !x.right.nil? - x = x.right - end - return x - end - - def successor(x) - if !x.right.nil? - return minimum(x.right) - end - y = x.parent - while !y.nil? && x == y.right - x = y - y = y.parent - end - return y - end - - def predecessor(x) - if !x.left.nil? - return maximum(x.left) - end - y = x.parent - while !y.nil? && x == y.left - x = y - y = y.parent - end - return y - end - - def inorder_walk(x = root) - x = self.minimum - while !x.nil? - yield x.key - x = successor(x) - end - end - - alias each inorder_walk - - def reverse_inorder_walk(x = root) - x = self.maximum - while !x.nil? - yield x.key - x = predecessor(x) - end - end - - alias reverse_each reverse_inorder_walk - - def search(key, x = root) - while !x.nil? && x.key != key - key < x.key ? x = x.left : x = x.right - end - return x - end - - def empty? - return self.root.nil? - end - - def black_height(x = root) - height = 0 - while !x.nil? - x = x.left - height +=1 if x.nil? || x.black? - end - return height - end - -private - - def left_rotate(x) - raise "x.right is nil!" if x.right.nil? - y = x.right - x.right = y.left - y.left.parent = x if !y.left.nil? - y.parent = x.parent - if x.parent.nil? - self.root = y - else - if x == x.parent.left - x.parent.left = y - else - x.parent.right = y - end - end - y.left = x - x.parent = y - end - - def right_rotate(x) - raise "x.left is nil!" if x.left.nil? - y = x.left - x.left = y.right - y.right.parent = x if !y.right.nil? - y.parent = x.parent - if x.parent.nil? - self.root = y - else - if x == x.parent.left - x.parent.left = y - else - x.parent.right = y - end - end - y.right = x - x.parent = y - end - - def insert_helper(z) - y = NilNode.instance - x = root - while !x.nil? - y = x - z.key < x.key ? x = x.left : x = x.right - end - z.parent = y - if y.nil? - self.root = z - else - z.key < y.key ? y.left = z : y.right = z - end - self.size += 1 - end - - def delete_fixup(x) - while x != root && x.color == Node::BLACK - if x == x.parent.left - w = x.parent.right - if w.color == Node::RED - w.color = Node::BLACK - x.parent.color = Node::RED - left_rotate(x.parent) - w = x.parent.right - end - if w.left.color == Node::BLACK && w.right.color == Node::BLACK - w.color = Node::RED - x = x.parent - else - if w.right.color == Node::BLACK - w.left.color = Node::BLACK - w.color = Node::RED - right_rotate(w) - w = x.parent.right - end - w.color = x.parent.color - x.parent.color = Node::BLACK - w.right.color = Node::BLACK - left_rotate(x.parent) - x = root - end - else - w = x.parent.left - if w.color == Node::RED - w.color = Node::BLACK - x.parent.color = Node::RED - right_rotate(x.parent) - w = x.parent.left - end - if w.right.color == Node::BLACK && w.left.color == Node::BLACK - w.color = Node::RED - x = x.parent - else - if w.left.color == Node::BLACK - w.right.color = Node::BLACK - w.color = Node::RED - left_rotate(w) - w = x.parent.left - end - w.color = x.parent.color - x.parent.color = Node::BLACK - w.left.color = Node::BLACK - right_rotate(x.parent) - x = root - end - end - end - x.color = Node::BLACK - end -end - -def rbt_bm - n = 100_000 - a1 = []; n.times { a1 << rand(999_999) } - a2 = []; n.times { a2 << rand(999_999) } - - start = Time.now - - tree = RedBlackTree.new - - n.times {|i| tree.add(i) } - n.times { tree.delete(tree.root) } - - tree = RedBlackTree.new - a1.each {|e| tree.add(e) } - a2.each {|e| tree.search(e) } - tree.inorder_walk {|key| key + 1 } - tree.reverse_inorder_walk {|key| key + 1 } - n.times { tree.minimum } - n.times { tree.maximum } - - return Time.now - start -end - -N = (ARGV[0] || 10).to_i - -N.times do - # puts rbt_bm.to_f - rbt_bm.to_f - # puts "GC.count = #{GC.count}" if GC.respond_to?(:count) -end diff --git a/test/testdata/ruby_benchmark/disabled/too_slow/gc/ring.rb b/test/testdata/ruby_benchmark/disabled/too_slow/gc/ring.rb deleted file mode 100644 index 276a135900..0000000000 --- a/test/testdata/ruby_benchmark/disabled/too_slow/gc/ring.rb +++ /dev/null @@ -1,32 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -# create many old objects - -max = 30_000_000 - -class Ring - attr_reader :next_ring - def initialize n = nil - @next_ring = n - end - - - def size - s = 1 - ring = self - while ring.next_ring - s += 1 - ring = ring.next_ring - end - s - end -end - -ring = Ring.new - -max.times{ - ring = Ring.new(ring) -} - -# p ring.size diff --git a/test/testdata/ruby_benchmark/disabled/too_slow/hash_aref_dsym_long.rb b/test/testdata/ruby_benchmark/disabled/too_slow/hash_aref_dsym_long.rb deleted file mode 100644 index 9c9db49713..0000000000 --- a/test/testdata/ruby_benchmark/disabled/too_slow/hash_aref_dsym_long.rb +++ /dev/null @@ -1,24 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -# [ruby-core:70129] [Bug #11396] -collection_size = 200000 -sample_size = 10000 - -values = (1..collection_size).to_a.map do |x| - "THIS IS A LONGER STRING THAT IS ALSO UNIQUE #{x}" -end - -symbol_hash = {} - -values.each do |x| - symbol_hash[x.to_sym] = 1 -end - -# use the same samples each time to minimize deviations -rng = Random.new(0) -symbol_sample_array = values.sample(sample_size, random: rng).map(&:to_sym) - -3000.times do - symbol_sample_array.each { |x| symbol_hash[x] } -end diff --git a/test/testdata/ruby_benchmark/disabled/too_slow/hash_long.rb b/test/testdata/ruby_benchmark/disabled/too_slow/hash_long.rb deleted file mode 100644 index c89e8fc2c1..0000000000 --- a/test/testdata/ruby_benchmark/disabled/too_slow/hash_long.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -k1 = "Ping Pong Ping Pong Ping Pong Ping Pong Ping Pong Ping Pong Ping Pong Ping Pong Ping Pong Ping Pong"; -k2 = "Pong Ping Pong Ping Pong Ping Pong Ping Pong Ping Pong Ping Pong Ping Pong Ping Pong Ping Pong Ping"; -h = {k1 => 0, k2 => 0}; -3000000.times{|i| k = i % 2 ? k2 : k1; h [k] = h[k] + 1} diff --git a/test/testdata/ruby_benchmark/disabled/too_slow/io_copy_stream_write.rb b/test/testdata/ruby_benchmark/disabled/too_slow/io_copy_stream_write.rb deleted file mode 100644 index 90a040cb21..0000000000 --- a/test/testdata/ruby_benchmark/disabled/too_slow/io_copy_stream_write.rb +++ /dev/null @@ -1,27 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -# The goal of this is to use a synthetic (non-IO) reader -# to trigger the read/write loop of IO.copy_stream, -# bypassing in-kernel mechanisms like sendfile for zero copy, -# so we wrap the /dev/zero IO object: - -class Zero - def initialize - @n = 100000 - @in = File.open('/dev/zero', 'rb') - end - - def read(len, buf) - return if (@n -= 1) == 0 - @in.read(len, buf) - end -end - -begin - src = Zero.new - dst = File.open(IO::NULL, 'wb') - n = IO.copy_stream(src, dst) -rescue Errno::ENOENT - # not *nix -end if IO.respond_to?(:copy_stream) && IO.const_defined?(:NULL) diff --git a/test/testdata/ruby_benchmark/disabled/too_slow/io_copy_stream_write_socket.rb b/test/testdata/ruby_benchmark/disabled/too_slow/io_copy_stream_write_socket.rb deleted file mode 100644 index 3b03f3208a..0000000000 --- a/test/testdata/ruby_benchmark/disabled/too_slow/io_copy_stream_write_socket.rb +++ /dev/null @@ -1,38 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -# The goal of this is to use a synthetic (non-IO) reader -# to trigger the read/write loop of IO.copy_stream, -# bypassing in-kernel mechanisms like sendfile for zero copy, -# so we wrap the /dev/zero IO object: -class Zero - def initialize - @n = 100000 - @in = File.open('/dev/zero', 'rb') - end - - def read(len, buf) - return if (@n -= 1) == 0 - @in.read(len, buf) - end -end - -begin - require 'socket' - src = Zero.new - rd, wr = UNIXSocket.pair - pid = fork do - wr.close - buf = String.new - while rd.read(16384, buf) - end - end - rd.close - IO.copy_stream(src, wr) -rescue Errno::ENOENT, NotImplementedError, NameError - # not *nix: missing /dev/zero, fork, or UNIXSocket -rescue LoadError # no socket? -ensure - wr.close if wr - Process.waitpid(pid) if pid -end if IO.respond_to?(:copy_stream) diff --git a/test/testdata/ruby_benchmark/disabled/too_slow/io_file_create.rb b/test/testdata/ruby_benchmark/disabled/too_slow/io_file_create.rb deleted file mode 100644 index 8bcdb88e9c..0000000000 --- a/test/testdata/ruby_benchmark/disabled/too_slow/io_file_create.rb +++ /dev/null @@ -1,16 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -# -# Create files -# - -max = 200_000 -file = './tmpfile_of_bm_io_file_create' - -max.times{ - f = open(file, 'w') - f.close#(true) -} -File.unlink(file) - diff --git a/test/testdata/ruby_benchmark/disabled/too_slow/io_nonblock_noex.rb b/test/testdata/ruby_benchmark/disabled/too_slow/io_nonblock_noex.rb deleted file mode 100644 index 5b062a13e2..0000000000 --- a/test/testdata/ruby_benchmark/disabled/too_slow/io_nonblock_noex.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -nr = 1_000_000 -i = 0 -msg = '.' -buf = '.' -noex = { exception: false } -begin - r, w = IO.pipe - while i < nr - i += 1 - w.write_nonblock(msg, noex) - r.read_nonblock(1, buf, noex) - end -rescue ArgumentError # old Rubies - while i < nr - i += 1 - w.write_nonblock(msg) - r.read_nonblock(1, buf) - end -ensure - r.close - w.close -end diff --git a/test/testdata/ruby_benchmark/disabled/too_slow/io_nonblock_noex2.rb b/test/testdata/ruby_benchmark/disabled/too_slow/io_nonblock_noex2.rb deleted file mode 100644 index e5c5d481ac..0000000000 --- a/test/testdata/ruby_benchmark/disabled/too_slow/io_nonblock_noex2.rb +++ /dev/null @@ -1,24 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -nr = 1_000_000 -i = 0 -msg = '.' -buf = '.' -begin - r, w = IO.pipe - while i < nr - i += 1 - w.write_nonblock(msg, exception: false) - r.read_nonblock(1, buf, exception: false) - end -rescue ArgumentError # old Rubies - while i < nr - i += 1 - w.write_nonblock(msg) - r.read_nonblock(1, buf) - end -ensure - r.close - w.close -end diff --git a/test/testdata/ruby_benchmark/disabled/too_slow/io_select2.rb b/test/testdata/ruby_benchmark/disabled/too_slow/io_select2.rb deleted file mode 100644 index 356b8711ae..0000000000 --- a/test/testdata/ruby_benchmark/disabled/too_slow/io_select2.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true -# typed: strict -# compiled: true -# IO.select performance. worst case of single fd. - -ios = [] -nr = 1000000 -if defined?(Process::RLIMIT_NOFILE) - max = Process.getrlimit(Process::RLIMIT_NOFILE)[0] -else - max = 64 -end -puts "max fd: #{max} (results not apparent with <= 1024 max fd)" - -((max / 2) - 10).times do - ios.concat IO.pipe -end - -last = [ ios[-1] ] -puts "last IO: #{last[0].inspect}" - -nr.times do - IO.select nil, last -end - diff --git a/test/testdata/ruby_benchmark/disabled/too_slow/loop_generator.rb b/test/testdata/ruby_benchmark/disabled/too_slow/loop_generator.rb deleted file mode 100644 index 7bbcb19481..0000000000 --- a/test/testdata/ruby_benchmark/disabled/too_slow/loop_generator.rb +++ /dev/null @@ -1,20 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -max = 600000 - -class Fiber; end -class Generator; end - -if defined? Fiber - gen = (1..max).each - loop do - gen.next - end -else - require 'generator' - gen = Generator.new((0..max)) - while gen.next? - gen.next - end -end diff --git a/test/testdata/ruby_benchmark/disabled/too_slow/marshal_dump_load_geniv.rb b/test/testdata/ruby_benchmark/disabled/too_slow/marshal_dump_load_geniv.rb deleted file mode 100644 index f2a5ace1dc..0000000000 --- a/test/testdata/ruby_benchmark/disabled/too_slow/marshal_dump_load_geniv.rb +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -a = '' -a.instance_eval do - @a = :a - @b = :b - @c = :c -end -100000.times do - a = Marshal.load(Marshal.dump(a)) -end -#p(a.instance_eval { @a == :a && @b == :b && @c == :c }) diff --git a/test/testdata/ruby_benchmark/disabled/too_slow/so_array.rb b/test/testdata/ruby_benchmark/disabled/too_slow/so_array.rb deleted file mode 100644 index 48e9581339..0000000000 --- a/test/testdata/ruby_benchmark/disabled/too_slow/so_array.rb +++ /dev/null @@ -1,26 +0,0 @@ -# frozen_string_literal: true -#!/usr/bin/ruby -# typed: true -# compiled: true -# -*- Ruby -*- -# $Id: ary-ruby.code,v 1.4 2004/11/13 07:41:27 bfulgham Exp $ -# http://www.bagley.org/~doug/shootout/ -# with help from Paul Brannan and Mark Hubbart - -n = 9000 # Integer(ARGV.shift || 1) - -x = Array.new(n) -y = Array.new(n, 0) - -n.times{|bi| - x[bi] = bi + 1 -} - -(0 .. 999).each do |e| - (n-1).step(0,-1) do |bi| - y[bi] += x.at(bi) - end -end -# puts "#{y.first} #{y.last}" - - diff --git a/test/testdata/ruby_benchmark/disabled/too_slow/so_binary_trees.rb b/test/testdata/ruby_benchmark/disabled/too_slow/so_binary_trees.rb deleted file mode 100644 index 4ba31e8716..0000000000 --- a/test/testdata/ruby_benchmark/disabled/too_slow/so_binary_trees.rb +++ /dev/null @@ -1,65 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -# The Computer Language Shootout Benchmarks -# http://shootout.alioth.debian.org -# -# contributed by Jesse Millikan - -# disable output -alias puts_orig puts -def puts str - # disable puts -end - -def item_check(tree) - if tree[0] == nil - tree[1] - else - tree[1] + item_check(tree[0]) - item_check(tree[2]) - end -end - -def bottom_up_tree(item, depth) - if depth > 0 - item_item = 2 * item - depth -= 1 - [bottom_up_tree(item_item - 1, depth), item, bottom_up_tree(item_item, depth)] - else - [nil, item, nil] - end -end - -max_depth = 16 # ARGV[0].to_i -min_depth = 4 - -max_depth = min_depth + 2 if min_depth + 2 > max_depth - -stretch_depth = max_depth + 1 -stretch_tree = bottom_up_tree(0, stretch_depth) - -puts "stretch tree of depth #{stretch_depth}\t check: #{item_check(stretch_tree)}" -stretch_tree = nil - -long_lived_tree = bottom_up_tree(0, max_depth) - -min_depth.step(max_depth + 1, 2) do |depth| - iterations = 2**(max_depth - depth + min_depth) - - check = 0 - - for i in 1..iterations - temp_tree = bottom_up_tree(i, depth) - check += item_check(temp_tree) - - temp_tree = bottom_up_tree(-i, depth) - check += item_check(temp_tree) - end - - puts "#{iterations * 2}\t trees of depth #{depth}\t check: #{check}" -end - -puts "long lived tree of depth #{max_depth}\t check: #{item_check(long_lived_tree)}" - -undef puts -alias puts puts_orig diff --git a/test/testdata/ruby_benchmark/disabled/too_slow/so_fannkuch.rb b/test/testdata/ruby_benchmark/disabled/too_slow/so_fannkuch.rb deleted file mode 100644 index 1d647e7e52..0000000000 --- a/test/testdata/ruby_benchmark/disabled/too_slow/so_fannkuch.rb +++ /dev/null @@ -1,48 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -# The Computer Language Shootout -# http://shootout.alioth.debian.org/ -# Contributed by Sokolov Yura -# Modified by Ryan Williams - -def fannkuch(n) - maxFlips, m, r, check = 0, n-1, n, 0 - count = (1..n).to_a - perm = (1..n).to_a - - while true - if check < 30 - puts "#{perm}" - check += 1 - end - - while r != 1 - count[r-1] = r - r -= 1 - end - - if perm[0] != 1 and perm[m] != n - perml = perm.clone #.dup - flips = 0 - while (k = perml.first ) != 1 - perml = perml.slice!(0, k).reverse + perml - flips += 1 - end - maxFlips = flips if flips > maxFlips - end - while true - if r==n then return maxFlips end - perm.insert r,perm.shift - break if (count[r] -= 1) > 0 - r += 1 - end - end -end - -def puts *args -end - -N = 9 # (ARGV[0] || 1).to_i -puts "Pfannkuchen(#{N}) = #{fannkuch(N)}" - diff --git a/test/testdata/ruby_benchmark/disabled/too_slow/so_fasta.rb b/test/testdata/ruby_benchmark/disabled/too_slow/so_fasta.rb deleted file mode 100644 index f6bc697824..0000000000 --- a/test/testdata/ruby_benchmark/disabled/too_slow/so_fasta.rb +++ /dev/null @@ -1,84 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -# The Computer Language Shootout -# http://shootout.alioth.debian.org/ -# Contributed by Sokolov Yura - -$last = 42.0 -def gen_random(max, im=139968, ia=3877, ic=29573) - (max * ($last = ($last * ia + ic) % im)) / im -end - -alu = - "GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGG"+ - "GAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGA"+ - "CCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAAT"+ - "ACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCA"+ - "GCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGG"+ - "AGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCC"+ - "AGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAA" - -iub = [ - ["a", 0.27], - ["c", 0.12], - ["g", 0.12], - ["t", 0.27], - - ["B", 0.02], - ["D", 0.02], - ["H", 0.02], - ["K", 0.02], - ["M", 0.02], - ["N", 0.02], - ["R", 0.02], - ["S", 0.02], - ["V", 0.02], - ["W", 0.02], - ["Y", 0.02], -] -homosapiens = [ - ["a", 0.3029549426680], - ["c", 0.1979883004921], - ["g", 0.1975473066391], - ["t", 0.3015094502008], -] - -def make_repeat_fasta(id, desc, src, n) - puts ">#{id} #{desc}" - v = nil - width = 60 - l = src.length - s = src * ((n / l) + 1) - s.slice!(n, l) - puts(s.scan(/.{1,#{width}}/).join("\n")) -end - -def make_random_fasta(id, desc, table, n) - puts ">#{id} #{desc}" - rand, v = nil,nil - width = 60 - chunk = 1 * width - prob = 0.0 - table.each{|v| v[1]= (prob += v[1])} - for i in 1..(n/width) - puts((1..width).collect{ - rand = gen_random(1.0) - table.find{|v| v[1]>rand}[0] - }.join) - end - if n%width != 0 - puts((1..(n%width)).collect{ - rand = gen_random(1.0) - table.find{|v| v[1]>rand}[0] - }.join) - end -end - - -n = (ARGV[0] or 250_000).to_i - -make_repeat_fasta('ONE', 'Homo sapiens alu', alu, n*2) -make_random_fasta('TWO', 'IUB ambiguity codes', iub, n*3) -make_random_fasta('THREE', 'Homo sapiens frequency', homosapiens, n*5) - diff --git a/test/testdata/ruby_benchmark/disabled/too_slow/so_lists.rb b/test/testdata/ruby_benchmark/disabled/too_slow/so_lists.rb deleted file mode 100644 index ead5116273..0000000000 --- a/test/testdata/ruby_benchmark/disabled/too_slow/so_lists.rb +++ /dev/null @@ -1,50 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -#from http://www.bagley.org/~doug/shootout/bench/lists/lists.ruby - -NUM = 300 -SIZE = 10000 - -def test_lists() - # create a list of integers (Li1) from 1 to SIZE - li1 = (1..SIZE).to_a - # copy the list to li2 (not by individual items) - li2 = li1.dup - # remove each individual item from left side of li2 and - # append to right side of li3 (preserving order) - li3 = Array.new - while (not li2.empty?) - li3.push(li2.shift) - end - # li2 must now be empty - # remove each individual item from right side of li3 and - # append to right side of li2 (reversing list) - while (not li3.empty?) - li2.push(li3.pop) - end - # li3 must now be empty - # reverse li1 in place - li1.reverse! - # check that first item is now SIZE - if li1[0] != SIZE then - p "not SIZE" - 0 - else - # compare li1 and li2 for equality - if li1 != li2 then - return(0) - else - # return the length of the list - li1.length - end - end -end - -i = 0 -while i LIMIT_SQUARED - escape = true - break - end - end - - byte_acc = (byte_acc << 1) | (escape ? 0b0 : 0b1) - bit_num += 1 - - # Code is very similar for these cases, but using separate blocks - # ensures we skip the shifting when it's unnecessary, which is most cases. - if (bit_num == 8) - print byte_acc.chr - byte_acc = 0 - bit_num = 0 - elsif (x == count_size) - byte_acc <<= (8 - bit_num) - print byte_acc.chr - byte_acc = 0 - bit_num = 0 - end - end -end diff --git a/test/testdata/ruby_benchmark/disabled/too_slow/so_matrix.rb b/test/testdata/ruby_benchmark/disabled/too_slow/so_matrix.rb deleted file mode 100644 index 19d37a70f0..0000000000 --- a/test/testdata/ruby_benchmark/disabled/too_slow/so_matrix.rb +++ /dev/null @@ -1,51 +0,0 @@ -# frozen_string_literal: true -#!/usr/bin/ruby -# typed: true -# compiled: true -# -*- Ruby -*- -# $Id: matrix-ruby.code,v 1.4 2004/11/13 07:42:14 bfulgham Exp $ -# http://www.bagley.org/~doug/shootout/ - -n = 60 #Integer(ARGV.shift || 1) - -size = 40 - -def mkmatrix(rows, cols) - count = 1 - mx = Array.new(rows) - (0 .. (rows - 1)).each do |bi| - row = Array.new(cols, 0) - (0 .. (cols - 1)).each do |j| - row[j] = count - count += 1 - end - mx[bi] = row - end - mx -end - -def mmult(rows, cols, m1, m2) - m3 = Array.new(rows) - (0 .. (rows - 1)).each do |bi| - row = Array.new(cols, 0) - (0 .. (cols - 1)).each do |j| - val = 0 - (0 .. (cols - 1)).each do |k| - val += m1.at(bi).at(k) * m2.at(k).at(j) - end - row[j] = val - end - m3[bi] = row - end - m3 -end - -m1 = mkmatrix(size, size) -m2 = mkmatrix(size, size) -mm = Array.new -n.times do - mm = mmult(size, size, m1, m2) -end -# puts "#{mm[0][0]} #{mm[2][3]} #{mm[3][2]} #{mm[4][4]}" - - diff --git a/test/testdata/ruby_benchmark/disabled/too_slow/so_meteor_contest.rb b/test/testdata/ruby_benchmark/disabled/too_slow/so_meteor_contest.rb deleted file mode 100644 index 8c98b45326..0000000000 --- a/test/testdata/ruby_benchmark/disabled/too_slow/so_meteor_contest.rb +++ /dev/null @@ -1,566 +0,0 @@ -# frozen_string_literal: true -#!/usr/bin/env ruby -# typed: true -# compiled: true -# -# The Computer Language Shootout -# http://shootout.alioth.debian.org -# contributed by Kevin Barnes (Ruby novice) - -# PROGRAM: the main body is at the bottom. -# 1) read about the problem here: http://www-128.ibm.com/developerworks/java/library/j-javaopt/ -# 2) see how I represent a board as a bitmask by reading the blank_board comments -# 3) read as your mental paths take you - -def print *args -end - -# class to represent all information about a particular rotation of a particular piece -class Rotation - # an array (by location) containing a bit mask for how the piece maps at the given location. - # if the rotation is invalid at that location the mask will contain false - attr_reader :start_masks - - # maps a direction to a relative location. these differ depending on whether it is an even or - # odd row being mapped from - @@rotation_even_adder = { :west => -1, :east => 1, :nw => -7, :ne => -6, :sw => 5, :se => 6 } - @@rotation_odd_adder = { :west => -1, :east => 1, :nw => -6, :ne => -5, :sw => 6, :se => 7 } - - def initialize( directions ) - @even_offsets, @odd_offsets = normalize_offsets( get_values( directions )) - - @even_mask = mask_for_offsets( @even_offsets) - @odd_mask = mask_for_offsets( @odd_offsets) - - @start_masks = Array.new(60) - - # create the rotational masks by placing the base mask at the location and seeing if - # 1) it overlaps the boundaries and 2) it produces a prunable board. if either of these - # is true the piece cannot be placed - 0.upto(59) do | offset | - mask = is_even(offset) ? (@even_mask << offset) : (@odd_mask << offset) - if (blank_board & mask == 0 && !prunable(blank_board | mask, 0, true)) then - imask = compute_required( mask, offset) - @start_masks[offset] = [ mask, imask, imask | mask ] - else - @start_masks[offset] = false - end - end - end - - def compute_required( mask, offset ) - board = blank_board - 0.upto(offset) { | i | board |= 1 << i } - board |= mask - return 0 if (!prunable(board | mask, offset)) - board = flood_fill(board,58) - count = 0 - imask = 0 - 0.upto(59) do | i | - if (board[i] == 0) then - imask |= (1 << i) - count += 1 - end - end - (count > 0 && count < 5) ? imask : 0 - end - - def flood_fill( board, location) - return board if (board[location] == 1) - board |= 1 << location - row, col = location.divmod(6) - board = flood_fill( board, location - 1) if (col > 0) - board = flood_fill( board, location + 1) if (col < 4) - if (row % 2 == 0) then - board = flood_fill( board, location - 7) if (col > 0 && row > 0) - board = flood_fill( board, location - 6) if (row > 0) - board = flood_fill( board, location + 6) if (row < 9) - board = flood_fill( board, location + 5) if (col > 0 && row < 9) - else - board = flood_fill( board, location - 5) if (col < 4 && row > 0) - board = flood_fill( board, location - 6) if (row > 0) - board = flood_fill( board, location + 6) if (row < 9) - board = flood_fill( board, location + 7) if (col < 4 && row < 9) - end - board - end - - # given a location, produces a list of relative locations covered by the piece at this rotation - def offsets( location) - if is_even( location) then - @even_offsets.collect { | value | value + location } - else - @odd_offsets.collect { | value | value + location } - end - end - - # returns a set of offsets relative to the top-left most piece of the rotation (by even or odd rows) - # this is hard to explain. imagine we have this partial board: - # 0 0 0 0 0 x [positions 0-5] - # 0 0 1 1 0 x [positions 6-11] - # 0 0 1 0 0 x [positions 12-17] - # 0 1 0 0 0 x [positions 18-23] - # 0 1 0 0 0 x [positions 24-29] - # 0 0 0 0 0 x [positions 30-35] - # ... - # The top-left of the piece is at position 8, the - # board would be passed as a set of positions (values array) containing [8,9,14,19,25] not necessarily in that - # sorted order. Since that array starts on an odd row, the offsets for an odd row are: [0,1,6,11,17] obtained - # by subtracting 8 from everything. Now imagine the piece shifted up and to the right so it's on an even row: - # 0 0 0 1 1 x [positions 0-5] - # 0 0 1 0 0 x [positions 6-11] - # 0 0 1 0 0 x [positions 12-17] - # 0 1 0 0 0 x [positions 18-23] - # 0 0 0 0 0 x [positions 24-29] - # 0 0 0 0 0 x [positions 30-35] - # ... - # Now the positions are [3,4,8,14,19] which after subtracting the lowest value (3) gives [0,1,5,11,16] thus, the - # offsets for this particular piece are (in even, odd order) [0,1,5,11,16],[0,1,6,11,17] which is what - # this function would return - def normalize_offsets( values) - min = values.min - even_min = is_even(min) - other_min = even_min ? min + 6 : min + 7 - other_values = values.collect do | value | - if is_even(value) then - value + 6 - other_min - else - value + 7 - other_min - end - end - values.collect! { | value | value - min } - - if even_min then - [values, other_values] - else - [other_values, values] - end - end - - # produce a bitmask representation of an array of offset locations - def mask_for_offsets( offsets ) - mask = 0 - offsets.each { | value | mask = mask + ( 1 << value ) } - mask - end - - # finds a "safe" position that a position as described by a list of directions can be placed - # without falling off any edge of the board. the values returned a location to place the first piece - # at so it will fit after making the described moves - def start_adjust( directions ) - south = east = 0; - directions.each do | direction | - east += 1 if ( direction == :sw || direction == :nw || direction == :west ) - south += 1 if ( direction == :nw || direction == :ne ) - end - south * 6 + east - end - - # given a set of directions places the piece (as defined by a set of directions) on the board at - # a location that will not take it off the edge - def get_values( directions ) - start = start_adjust(directions) - values = [ start ] - directions.each do | direction | - if (start % 12 >= 6) then - start += @@rotation_odd_adder[direction] - else - start += @@rotation_even_adder[direction] - end - values += [ start ] - end - - # some moves take you back to an existing location, we'll strip duplicates - values.uniq - end -end - -# describes a piece and caches information about its rotations to as to be efficient for iteration -# ATTRIBUTES: -# rotations -- all the rotations of the piece -# type -- a numeic "name" of the piece -# masks -- an array by location of all legal rotational masks (a n inner array) for that location -# placed -- the mask that this piece was last placed at (not a location, but the actual mask used) -class Piece - attr_reader :rotations, :type, :masks - attr_accessor :placed - - # transform hashes that change one direction into another when you either flip or rotate a set of directions - @@flip_converter = { :west => :west, :east => :east, :nw => :sw, :ne => :se, :sw => :nw, :se => :ne } - @@rotate_converter = { :west => :nw, :east => :se, :nw => :ne, :ne => :east, :sw => :west, :se => :sw } - - def initialize( directions, type ) - @type = type - @rotations = Array.new(); - @map = {} - - generate_rotations( directions ) - directions.collect! { | value | @@flip_converter[value] } - generate_rotations( directions ) - - # creates the masks AND a map that returns [location, rotation] for any given mask - # this is used when a board is found and we want to draw it, otherwise the map is unused - @masks = Array.new(); - 0.upto(59) do | i | - even = true - @masks[i] = @rotations.collect do | rotation | - mask = rotation.start_masks[i] - @map[mask[0]] = [ i, rotation ] if (mask) - mask || nil - end - @masks[i].compact! - end - end - - # rotates a set of directions through all six angles and adds a Rotation to the list for each one - def generate_rotations( directions ) - 6.times do - rotations.push( Rotation.new(directions)) - directions.collect! { | value | @@rotate_converter[value] } - end - end - - # given a board string, adds this piece to the board at whatever location/rotation - # important: the outbound board string is 5 wide, the normal location notation is six wide (padded) - def fill_string( board_string) - location, rotation = @map[@placed] - rotation.offsets(location).each do | offset | - row, col = offset.divmod(6) - board_string[ row*5 + col, 1 ] = @type.to_s - end - end -end - -# a blank bit board having this form: -# -# 0 0 0 0 0 1 -# 0 0 0 0 0 1 -# 0 0 0 0 0 1 -# 0 0 0 0 0 1 -# 0 0 0 0 0 1 -# 0 0 0 0 0 1 -# 0 0 0 0 0 1 -# 0 0 0 0 0 1 -# 0 0 0 0 0 1 -# 0 0 0 0 0 1 -# 1 1 1 1 1 1 -# -# where left lest significant bit is the top left and the most significant is the lower right -# the actual board only consists of the 0 places, the 1 places are blockers to keep things from running -# off the edges or bottom -def blank_board - 0b111111100000100000100000100000100000100000100000100000100000100000 -end - -def full_board - 0b111111111111111111111111111111111111111111111111111111111111111111 -end - -# determines if a location (bit position) is in an even row -def is_even( location) - (location % 12) < 6 -end - -# support function that create three utility maps: -# $converter -- for each row an array that maps a five bit row (via array mapping) -# to the a five bit representation of the bits below it -# $bit_count -- maps a five bit row (via array mapping) to the number of 1s in the row -# @@new_regions -- maps a five bit row (via array mapping) to an array of "region" arrays -# a region array has three values the first is a mask of bits in the region, -# the second is the count of those bits and the third is identical to the first -# examples: -# 0b10010 => [ 0b01100, 2, 0b01100 ], [ 0b00001, 1, 0b00001] -# 0b01010 => [ 0b10000, 1, 0b10000 ], [ 0b00100, 1, 0b00100 ], [ 0b00001, 1, 0b00001] -# 0b10001 => [ 0b01110, 3, 0b01110 ] -def create_collector_support - odd_map = [0b11, 0b110, 0b1100, 0b11000, 0b10000] - even_map = [0b1, 0b11, 0b110, 0b1100, 0b11000] - - all_odds = Array.new(0b100000) - all_evens = Array.new(0b100000) - bit_counts = Array.new(0b100000) - new_regions = Array.new(0b100000) - 0.upto(0b11111) do | i | - bit_count = odd = even = 0 - 0.upto(4) do | bit | - if (i[bit] == 1) then - bit_count += 1 - odd |= odd_map[bit] - even |= even_map[bit] - end - end - all_odds[i] = odd - all_evens[i] = even - bit_counts[i] = bit_count - new_regions[i] = create_regions( i) - end - - $converter = [] - 10.times { | row | $converter.push((row % 2 == 0) ? all_evens : all_odds) } - $bit_counts = bit_counts - $regions = new_regions.collect { | set | set.collect { | value | [ value, bit_counts[value], value] } } -end - -# determines if a board is punable, meaning that there is no possibility that it -# can be filled up with pieces. A board is prunable if there is a grouping of unfilled spaces -# that are not a multiple of five. The following board is an example of a prunable board: -# 0 0 1 0 0 -# 0 1 0 0 0 -# 1 1 0 0 0 -# 0 1 0 0 0 -# 0 0 0 0 0 -# ... -# -# This board is prunable because the top left corner is only 3 bits in area, no piece will ever fit it -# parameters: -# board -- an initial bit board (6 bit padded rows, see blank_board for format) -# location -- starting location, everything above and to the left is already full -# slotting -- set to true only when testing initial pieces, when filling normally -# additional assumptions are possible -# -# Algorithm: -# The algorithm starts at the top row (as determined by location) and iterates a row at a time -# maintainng counts of active open areas (kept in the collector array) each collector contains -# three values at the start of an iteration: -# 0: mask of bits that would be adjacent to the collector in this row -# 1: the number of bits collected so far -# 2: a scratch space starting as zero, but used during the computation to represent -# the empty bits in the new row that are adjacent (position 0) -# The exact procedure is described in-code -def prunable( board, location, slotting = false) - collectors = [] - # loop across the rows - (location / 6).to_i.upto(9) do | row_on | - # obtain a set of regions representing the bits of the current row. - regions = $regions[(board >> (row_on * 6)) & 0b11111] - converter = $converter[row_on] - - # track the number of collectors at the start of the cycle so that - # we don't compute against newly created collectors, only existing collectors - initial_collector_count = collectors.length - - # loop against the regions. For each region of the row - # we will see if it connects to one or more existing collectors. - # if it connects to 1 collector, the bits from the region are added to the - # bits of the collector and the mask is placed in collector[2] - # If the region overlaps more than one collector then all the collectors - # it overlaps with are merged into the first one (the others are set to nil in the array) - # if NO collectors are found then the region is copied as a new collector - regions.each do | region | - collector_found = nil - region_mask = region[2] - initial_collector_count.times do | collector_num | - collector = collectors[collector_num] - if (collector) then - collector_mask = collector[0] - if (collector_mask & region_mask != 0) then - if (collector_found) then - collector_found[0] |= collector_mask - collector_found[1] += collector[1] - collector_found[2] |= collector[2] - collectors[collector_num] = nil - else - collector_found = collector - collector[1] += region[1] - collector[2] |= region_mask - end - end - end - end - if (collector_found == nil) then - collectors.push(Array.new(region)) - end - end - - # check the existing collectors, if any collector overlapped no bits in the region its [2] value will - # be zero. The size of any such reaason is tested if it is not a multiple of five true is returned since - # the board is prunable. if it is a multiple of five it is removed. - # Collector that are still active have a new adjacent value [0] set based n the matched bits - # and have [2] cleared out for the next cycle. - collectors.length.times do | collector_num | - collector = collectors[collector_num] - if (collector) then - if (collector[2] == 0) then - return true if (collector[1] % 5 != 0) - collectors[collector_num] = nil - else - # if a collector matches all bits in the row then we can return unprunable early for the - # following reasons: - # 1) there can be no more unavailable bits bince we fill from the top left downward - # 2) all previous regions have been closed or joined so only this region can fail - # 3) this region must be good since there can never be only 1 region that is nuot - # a multiple of five - # this rule only applies when filling normally, so we ignore the rule if we are "slotting" - # in pieces to see what configurations work for them (the only other time this algorithm is used). - return false if (collector[2] == 0b11111 && !slotting) - collector[0] = converter[collector[2]] - collector[2] = 0 - end - end - end - - # get rid of all the empty converters for the next round - collectors.compact! - end - return false if (collectors.length <= 1) # 1 collector or less and the region is fine - collectors.any? { | collector | (collector[1] % 5) != 0 } # more than 1 and we test them all for bad size -end - -# creates a region given a row mask. see prunable for what a "region" is -def create_regions( value ) - regions = [] - cur_region = 0 - 5.times do | bit | - if (value[bit] == 0) then - cur_region |= 1 << bit - else - if (cur_region != 0 ) then - regions.push( cur_region) - cur_region = 0; - end - end - end - regions.push(cur_region) if (cur_region != 0) - regions -end - -# find up to the counted number of solutions (or all solutions) and prints the final result -def find_all - find_top( 1) - find_top( 0) - print_results -end - -# show the board -def print_results - print "#{@boards_found} solutions found\n\n" - print_full_board( @min_board) - print "\n" - print_full_board( @max_board) - print "\n" -end - -# finds solutions. This special version of the main function is only used for the top level -# the reason for it is basically to force a particular ordering on how the rotations are tested for -# the first piece. It is called twice, first looking for placements of the odd rotations and then -# looking for placements of the even locations. -# -# WHY? -# Since any found solution has an inverse we want to maximize finding solutions that are not already found -# as an inverse. The inverse will ALWAYS be 3 one of the piece configurations that is exactly 3 rotations away -# (an odd number). Checking even vs odd then produces a higher probability of finding more pieces earlier -# in the cycle. We still need to keep checking all the permutations, but our probability of finding one will -# diminsh over time. Since we are TOLD how many to search for this lets us exit before checking all pieces -# this bennifit is very great when seeking small numbers of solutions and is 0 when looking for more than the -# maximum number -def find_top( rotation_skip) - board = blank_board - (@pieces.length-1).times do - piece = @pieces.shift - piece.masks[0].each do | mask, imask, cmask | - if ((rotation_skip += 1) % 2 == 0) then - piece.placed = mask - find( 1, 1, board | mask) - end - end - @pieces.push(piece) - end - piece = @pieces.shift - @pieces.push(piece) -end - -# the normail find routine, iterates through the available pieces, checks all rotations at the current location -# and adds any boards found. depth is achieved via recursion. the overall approach is described -# here: http://www-128.ibm.com/developerworks/java/library/j-javaopt/ -# parameters: -# start_location -- where to start looking for place for the next piece at -# placed -- number of pieces placed -# board -- current state of the board -# -# see in-code comments -def find( start_location, placed, board) - # find the next location to place a piece by looking for an empty bit - while board[start_location] == 1 - start_location += 1 - end - - @pieces.length.times do - piece = @pieces.shift - piece.masks[start_location].each do | mask, imask, cmask | - if ( board & cmask == imask) then - piece.placed = mask - if (placed == 9) then - add_board - else - find( start_location + 1, placed + 1, board | mask) - end - end - end - @pieces.push(piece) - end -end - -# print the board -def print_full_board( board_string) - 10.times do | row | - print " " if (row % 2 == 1) - 5.times do | col | - print "#{board_string[row*5 + col,1]} " - end - print "\n" - end -end - -# when a board is found we "draw it" into a string and then flip that string, adding both to -# the list (hash) of solutions if they are unique. -def add_board - board_string = "99999999999999999999999999999999999999999999999999" - @all_pieces.each { | piece | piece.fill_string( board_string ) } - save( board_string) - save( board_string.reverse) -end - -# adds a board string to the list (if new) and updates the current best/worst board -def save( board_string) - if (@all_boards[board_string] == nil) then - @min_board = board_string if (board_string < @min_board) - @max_board = board_string if (board_string > @max_board) - @all_boards.store(board_string,true) - @boards_found += 1 - - # the exit motif is a time saver. Ideally the function should return, but those tests - # take noticeable time (performance). - if (@boards_found == @stop_count) then - print_results - exit(0) - end - end -end - - -## -## MAIN BODY :) -## -create_collector_support -@pieces = [ - Piece.new( [ :nw, :ne, :east, :east ], 2), - Piece.new( [ :ne, :se, :east, :ne ], 7), - Piece.new( [ :ne, :east, :ne, :nw ], 1), - Piece.new( [ :east, :sw, :sw, :se ], 6), - Piece.new( [ :east, :ne, :se, :ne ], 5), - Piece.new( [ :east, :east, :east, :se ], 0), - Piece.new( [ :ne, :nw, :se, :east, :se ], 4), - Piece.new( [ :se, :se, :se, :west ], 9), - Piece.new( [ :se, :se, :east, :se ], 8), - Piece.new( [ :east, :east, :sw, :se ], 3) - ]; - -@all_pieces = Array.new( @pieces) - -@min_board = "99999999999999999999999999999999999999999999999999" -@max_board = "00000000000000000000000000000000000000000000000000" -@stop_count = ARGV[0].to_i || 2089 -@all_boards = {} -@boards_found = 0 - -find_all ######## DO IT!!! diff --git a/test/testdata/ruby_benchmark/disabled/too_slow/so_nsieve.rb b/test/testdata/ruby_benchmark/disabled/too_slow/so_nsieve.rb deleted file mode 100644 index 97fcf1d03f..0000000000 --- a/test/testdata/ruby_benchmark/disabled/too_slow/so_nsieve.rb +++ /dev/null @@ -1,38 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -# The Computer Language Shootout -# http://shootout.alioth.debian.org/ -# -# contributed by Glenn Parker, March 2005 -# modified by Evan Phoenix, Sept 2006 - -def sieve(m) - flags = Flags.dup[0,m] - count = 0 - pmax = m - 1 - p = 2 - while p <= pmax - unless flags[p].zero? - count += 1 - mult = p - while mult <= pmax - flags[mult] = 0 - mult += p - end - end - p += 1 - end - count -end - -n = 9 # (ARGV[0] || 2).to_i -Flags = ("\x1" * ( 2 ** n * 10_000)).unpack("c*") - -n.downto(n-2) do |exponent| - break if exponent < 0 - m = (1 << exponent) * 10_000 - # m = (2 ** exponent) * 10_000 - count = sieve(m) - printf "Primes up to %8d %8d\n", m, count -end diff --git a/test/testdata/ruby_benchmark/disabled/too_slow/so_nsieve_bits.rb b/test/testdata/ruby_benchmark/disabled/too_slow/so_nsieve_bits.rb deleted file mode 100644 index 41ffa1a231..0000000000 --- a/test/testdata/ruby_benchmark/disabled/too_slow/so_nsieve_bits.rb +++ /dev/null @@ -1,46 +0,0 @@ -# frozen_string_literal: true -#!/usr/bin/ruby -# typed: true -# compiled: true -#coding: us-ascii -# -# The Great Computer Language Shootout -# http://shootout.alioth.debian.org/ -# -# nsieve-bits in Ruby -# Contributed by Glenn Parker, March 2005 - -CharExponent = 3 -BitsPerChar = 1 << CharExponent -LowMask = BitsPerChar - 1 - -def sieve(m) - items = "\xFF" * ((m / BitsPerChar) + 1) - masks = "" - BitsPerChar.times do |b| - masks << (1 << b).chr - end - - count = 0 - pmax = m - 1 - 2.step(pmax, 1) do |p| - if items[p >> CharExponent][p & LowMask] == 1 - count += 1 - p.step(pmax, p) do |mult| - a = mult >> CharExponent - b = mult & LowMask - items[a] -= masks[b] if items[a][b] != 0 - end - end - end - count -end - -n = 9 # (ARGV[0] || 2).to_i -n.step(n - 2, -1) do |exponent| - break if exponent < 0 - m = 2 ** exponent * 10_000 - count = sieve(m) - printf "Primes up to %8d %8d\n", m, count -end - diff --git a/test/testdata/ruby_benchmark/disabled/too_slow/so_partial_sums.rb b/test/testdata/ruby_benchmark/disabled/too_slow/so_partial_sums.rb deleted file mode 100644 index a15cd7efc6..0000000000 --- a/test/testdata/ruby_benchmark/disabled/too_slow/so_partial_sums.rb +++ /dev/null @@ -1,34 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -n = 2_500_000 # (ARGV.shift || 1).to_i - -alt = 1.0 ; s0 = s1 = s2 = s3 = s4 = s5 = s6 = s7 = s8 = 0.0 - -1.upto(n) do |d| - d = d.to_f ; d2 = d * d ; d3 = d2 * d ; ds = Math.sin(d) ; dc = Math.cos(d) - - s0 += (2.0 / 3.0) ** (d - 1.0) - s1 += 1.0 / Math.sqrt(d) - s2 += 1.0 / (d * (d + 1.0)) - s3 += 1.0 / (d3 * ds * ds) - s4 += 1.0 / (d3 * dc * dc) - s5 += 1.0 / d - s6 += 1.0 / d2 - s7 += alt / d - s8 += alt / (2.0 * d - 1.0) - - alt = -alt -end - -if false - printf("%.9f\t(2/3)^k\n", s0) - printf("%.9f\tk^-0.5\n", s1) - printf("%.9f\t1/k(k+1)\n", s2) - printf("%.9f\tFlint Hills\n", s3) - printf("%.9f\tCookson Hills\n", s4) - printf("%.9f\tHarmonic\n", s5) - printf("%.9f\tRiemann Zeta\n", s6) - printf("%.9f\tAlternating Harmonic\n", s7) - printf("%.9f\tGregory\n", s8) -end diff --git a/test/testdata/ruby_benchmark/disabled/too_slow/so_pidigits.rb b/test/testdata/ruby_benchmark/disabled/too_slow/so_pidigits.rb deleted file mode 100644 index 728c0bba9f..0000000000 --- a/test/testdata/ruby_benchmark/disabled/too_slow/so_pidigits.rb +++ /dev/null @@ -1,95 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -# The Great Computer Language Shootout -# http://shootout.alioth.debian.org/ -# -# contributed by Gabriele Renzi - -class PiDigitSpigot - - def initialize() - @z = Transformation.new 1,0,0,1 - @x = Transformation.new 0,0,0,0 - @inverse = Transformation.new 0,0,0,0 - end - - def next! - @y = @z.extract(3) - if safe? @y - @z = produce(@y) - @y - else - @z = consume @x.next!() - next!() - end - end - - def safe?(digit) - digit == @z.extract(4) - end - - def produce(i) - @inverse.qrst(10,-10*i,0,1).compose(@z) - end - - def consume(a) - @z.compose(a) - end -end - - -class Transformation - attr_reader :q, :r, :s, :t - def initialize(q, r, s, t) - @q,@r,@s,@t,@k = q,r,s,t,0 - end - - def next!() - @q = @k = @k + 1 - @r = 4 * @k + 2 - @s = 0 - @t = 2 * @k + 1 - self - end - - def extract(j) - (@q * j + @r) / (@s * j + @t) - end - - def compose(a) - self.class.new( @q * a.q, - @q * a.r + r * a.t, - @s * a.q + t * a.s, - @s * a.r + t * a.t - ) - end - - def qrst *args - initialize *args - self - end - - -end - - -WIDTH = 10 -n = 2_500 # Integer(ARGV[0]) -j = 0 - -digits = PiDigitSpigot.new - -while n > 0 - if n >= WIDTH - WIDTH.times {print digits.next!} - j += WIDTH - else - n.times {print digits.next!} - (WIDTH-n).times {print " "} - j += n - end - puts "\t:"+j.to_s - n -= WIDTH -end - diff --git a/test/testdata/ruby_benchmark/disabled/too_slow/so_spectralnorm.rb b/test/testdata/ruby_benchmark/disabled/too_slow/so_spectralnorm.rb deleted file mode 100644 index d746372523..0000000000 --- a/test/testdata/ruby_benchmark/disabled/too_slow/so_spectralnorm.rb +++ /dev/null @@ -1,53 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -# The Computer Language Shootout -# http://shootout.alioth.debian.org/ -# Contributed by Sokolov Yura - -def eval_A(i,j) - return 1.0/((i+j)*(i+j+1)/2+i+1) -end - -def eval_A_times_u(u) - v, i = nil, nil - (0..u.length-1).collect { |i| - v = 0 - for j in 0..u.length-1 - v += eval_A(i,j)*u[j] - end - v - } -end - -def eval_At_times_u(u) - v, i = nil, nil - (0..u.length-1).collect{|i| - v = 0 - for j in 0..u.length-1 - v += eval_A(j,i)*u[j] - end - v - } -end - -def eval_AtA_times_u(u) - return eval_At_times_u(eval_A_times_u(u)) -end - -n = 500 # ARGV[0].to_i - -u=[1]*n -for i in 1..10 - v=eval_AtA_times_u(u) - u=eval_AtA_times_u(v) -end -vBv=0 -vv=0 -for i in 0..n-1 - vBv += u[i]*v[i] - vv += v[i]*v[i] -end - -str = "%0.9f" % (Math.sqrt(vBv/vv)), "\n" -# print str diff --git a/test/testdata/ruby_benchmark/disabled/too_slow/vm3_backtrace.rb b/test/testdata/ruby_benchmark/disabled/too_slow/vm3_backtrace.rb deleted file mode 100644 index 75887ff1bc..0000000000 --- a/test/testdata/ruby_benchmark/disabled/too_slow/vm3_backtrace.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -# get last backtrace - -begin - caller(0, 0) -rescue ArgumentError - alias caller_orig caller - def caller lev, n - caller_orig(lev)[0..n] - end -end - -def rec n - if n < 0 - 100_000.times{ - caller(0, 1) - } - else - rec(n-1) - end -end - -rec 50 diff --git a/test/testdata/ruby_benchmark/disabled/too_slow/vm3_gc.rb b/test/testdata/ruby_benchmark/disabled/too_slow/vm3_gc.rb deleted file mode 100644 index 9157f516b1..0000000000 --- a/test/testdata/ruby_benchmark/disabled/too_slow/vm3_gc.rb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true -# typed: strong -# compiled: true -5000.times do - 100.times do - {"xxxx"=>"yyyy"} - end - GC.start -end diff --git a/test/testdata/ruby_benchmark/disabled/too_slow/vm3_gc_old_full.rb b/test/testdata/ruby_benchmark/disabled/too_slow/vm3_gc_old_full.rb deleted file mode 100644 index fd94a8271c..0000000000 --- a/test/testdata/ruby_benchmark/disabled/too_slow/vm3_gc_old_full.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true -# typed: strong -# compiled: true -old_object = Array.new(1_000_000){''} -100.times do - GC.start -end diff --git a/test/testdata/ruby_benchmark/disabled/too_slow/vm_thread_condvar1.rb b/test/testdata/ruby_benchmark/disabled/too_slow/vm_thread_condvar1.rb deleted file mode 100644 index 553da2f7df..0000000000 --- a/test/testdata/ruby_benchmark/disabled/too_slow/vm_thread_condvar1.rb +++ /dev/null @@ -1,31 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -# two threads, two mutex, two condvar ping-pong -require 'thread' -m1 = Mutex.new -m2 = Mutex.new -cv1 = ConditionVariable.new -cv2 = ConditionVariable.new -max = 100000 -i = 0 -wait = nil -m2.synchronize do - wait = Thread.new do - m1.synchronize do - m2.synchronize { cv2.signal } - while (i += 1) < max - cv1.wait(m1) - cv2.signal - end - end - end - cv2.wait(m2) -end -m1.synchronize do - while i < max - cv1.signal - cv2.wait(m1) - end -end -wait.join diff --git a/test/testdata/ruby_benchmark/disabled/too_slow/vm_thread_condvar2.rb b/test/testdata/ruby_benchmark/disabled/too_slow/vm_thread_condvar2.rb deleted file mode 100644 index b17e1dcf94..0000000000 --- a/test/testdata/ruby_benchmark/disabled/too_slow/vm_thread_condvar2.rb +++ /dev/null @@ -1,38 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -# many threads, one mutex, many condvars -require 'thread' -m = Mutex.new -cv1 = ConditionVariable.new -cv2 = ConditionVariable.new -max = 1000 -n = 100 -waiting = 0 -scvs = [] -waiters = n.times.map do |i| - start_cv = ConditionVariable.new - scvs << start_cv - start_mtx = Mutex.new - start_mtx.synchronize do - th = Thread.new(start_mtx, start_cv) do |sm, scv| - m.synchronize do - sm.synchronize { scv.signal } - max.times do - cv2.signal if (waiting += 1) == n - cv1.wait(m) - end - end - end - start_cv.wait(start_mtx) - th - end -end -m.synchronize do - max.times do - cv2.wait(m) until waiting == n - waiting = 0 - cv1.broadcast - end -end -waiters.each(&:join) diff --git a/test/testdata/ruby_benchmark/empty_method.rb b/test/testdata/ruby_benchmark/empty_method.rb deleted file mode 100644 index 2b236d4935..0000000000 --- a/test/testdata/ruby_benchmark/empty_method.rb +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -class Main - def self.my_main - - end -end - -for _i in 0..1_000_000 - Main.my_main -end diff --git a/test/testdata/ruby_benchmark/enum_lazy_grep_v_100.rb b/test/testdata/ruby_benchmark/enum_lazy_grep_v_100.rb deleted file mode 100644 index c91abb59b5..0000000000 --- a/test/testdata/ruby_benchmark/enum_lazy_grep_v_100.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -grep_data = (1..10).to_a * 1000 -N = 100 -enum = grep_data.lazy.grep_v(->(i){i == 0}).grep_v(->(i){i == 0}) -N.times {enum.each {}} diff --git a/test/testdata/ruby_benchmark/enum_lazy_grep_v_20.rb b/test/testdata/ruby_benchmark/enum_lazy_grep_v_20.rb deleted file mode 100644 index f3e32d0bed..0000000000 --- a/test/testdata/ruby_benchmark/enum_lazy_grep_v_20.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -grep_data = (1..10).to_a * 1000 -N = 100 -enum = grep_data.lazy.grep_v(->(i){i > 2}).grep_v(->(i){i > 2}) -N.times {enum.each {}} diff --git a/test/testdata/ruby_benchmark/enum_lazy_grep_v_50.rb b/test/testdata/ruby_benchmark/enum_lazy_grep_v_50.rb deleted file mode 100644 index 3d3f3190ba..0000000000 --- a/test/testdata/ruby_benchmark/enum_lazy_grep_v_50.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -grep_data = (1..10).to_a * 1000 -N = 100 -enum = grep_data.lazy.grep_v(->(i){i > 5}).grep_v(->(i){i > 5}) -N.times {enum.each {}} diff --git a/test/testdata/ruby_benchmark/enum_lazy_uniq_100.rb b/test/testdata/ruby_benchmark/enum_lazy_uniq_100.rb deleted file mode 100644 index 74e758d2b3..0000000000 --- a/test/testdata/ruby_benchmark/enum_lazy_uniq_100.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -uniq_data = (1..10_000).to_a -N = 100 -enum = uniq_data.lazy.uniq {|i| i % 10000}.uniq {|i| i % 10000} -N.times {enum.each {}} diff --git a/test/testdata/ruby_benchmark/enum_lazy_uniq_20.rb b/test/testdata/ruby_benchmark/enum_lazy_uniq_20.rb deleted file mode 100644 index a2d9ce3056..0000000000 --- a/test/testdata/ruby_benchmark/enum_lazy_uniq_20.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -uniq_data = (1..10_000).to_a -N = 100 -enum = uniq_data.lazy.uniq {|i| i % 2000}.uniq {|i| i % 2000} -N.times {enum.each {}} diff --git a/test/testdata/ruby_benchmark/enum_lazy_uniq_50.rb b/test/testdata/ruby_benchmark/enum_lazy_uniq_50.rb deleted file mode 100644 index 7f87c16135..0000000000 --- a/test/testdata/ruby_benchmark/enum_lazy_uniq_50.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -uniq_data = (1..10_000).to_a -N = 100 -enum = uniq_data.lazy.uniq {|i| i % 5000}.uniq {|i| i % 5000} -N.times {enum.each {}} diff --git a/test/testdata/ruby_benchmark/erb_render.yml b/test/testdata/ruby_benchmark/erb_render.yml deleted file mode 100644 index 15f6c3880b..0000000000 --- a/test/testdata/ruby_benchmark/erb_render.yml +++ /dev/null @@ -1,24 +0,0 @@ -prelude: | - require 'erb' - - data = < - <%= title %> - -

<%= title %>

-

- <%= content %> -

- - - erb - - title = "hello world!" - content = "hello world!\n" * 10 - - src = "def self.render(title, content); #{ERB.new(data).src}; end" - mod = Module.new - mod.instance_eval(src, "(ERB)") -benchmark: - erb_render: mod.render(title, content) -loop_count: 1500000 diff --git a/test/testdata/ruby_benchmark/gc/null.rb b/test/testdata/ruby_benchmark/gc/null.rb deleted file mode 100644 index 9e8a493404..0000000000 --- a/test/testdata/ruby_benchmark/gc/null.rb +++ /dev/null @@ -1,4 +0,0 @@ -# frozen_string_literal: true -# typed: strong -# compiled: true -# null diff --git a/test/testdata/ruby_benchmark/gc/too_slow/rdoc.rb b/test/testdata/ruby_benchmark/gc/too_slow/rdoc.rb deleted file mode 100644 index 1e9ab88769..0000000000 --- a/test/testdata/ruby_benchmark/gc/too_slow/rdoc.rb +++ /dev/null @@ -1,16 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -require 'rdoc/rdoc' -require 'tmpdir' - -srcdir = File.expand_path('../..', __dir__) - -Dir.mktmpdir('rdocbench-'){|d| - dir = File.join(d, 'rdocbench') - args = %W(--root #{srcdir} --page-dir #{srcdir}/doc --encoding=UTF-8 --no-force-update --all --ri --debug --quiet #{srcdir}) - args << '--op' << dir - - r = RDoc::RDoc.new - r.document args -} diff --git a/test/testdata/ruby_benchmark/hash1.rb b/test/testdata/ruby_benchmark/hash1.rb deleted file mode 100644 index dc9a424c8c..0000000000 --- a/test/testdata/ruby_benchmark/hash1.rb +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true -# typed: strong -# compiled: true -value = 0.01 -h = {} -n = 50_000 - -1.upto(n){|i| - h["%020d" % i] = "v-#{i}" -} - -(n * 1_000).times{ - '' -} diff --git a/test/testdata/ruby_benchmark/hash_aref_dsym.rb b/test/testdata/ruby_benchmark/hash_aref_dsym.rb deleted file mode 100644 index a9f910bcba..0000000000 --- a/test/testdata/ruby_benchmark/hash_aref_dsym.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true -# typed: strict -# compiled: true -h = {} -syms = ('a'..'z').map { |s| s.to_sym } -syms.each { |s| h[s] = 1 } -200_000.times { syms.each { |s| h[s] } } diff --git a/test/testdata/ruby_benchmark/hash_aref_fix.rb b/test/testdata/ruby_benchmark/hash_aref_fix.rb deleted file mode 100644 index 3e437b9f8f..0000000000 --- a/test/testdata/ruby_benchmark/hash_aref_fix.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true -# typed: strict -# compiled: true -h = {} -nums = (1..26).to_a -nums.each { |i| h[i] = i } -200_000.times { nums.each { |s| h[s] } } diff --git a/test/testdata/ruby_benchmark/hash_aref_flo.rb b/test/testdata/ruby_benchmark/hash_aref_flo.rb deleted file mode 100644 index a58711c48a..0000000000 --- a/test/testdata/ruby_benchmark/hash_aref_flo.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true -# typed: strict -# compiled: true -h = {} -strs = [*1..10000].map! {|i| i.fdiv(10)} -strs.each { |s| h[s] = s } -50.times { strs.each { |s| h[s] } } diff --git a/test/testdata/ruby_benchmark/hash_aref_miss.rb b/test/testdata/ruby_benchmark/hash_aref_miss.rb deleted file mode 100644 index 93792dfaf5..0000000000 --- a/test/testdata/ruby_benchmark/hash_aref_miss.rb +++ /dev/null @@ -1,8 +0,0 @@ -# frozen_string_literal: true -# typed: strict -# compiled: true -h = {} -strs = ('a'..'z').to_a.map!(&:freeze) -strs.each { |s| h[s] = s } -strs = ('A'..'Z').to_a -200_000.times { strs.each { |s| h[s] } } diff --git a/test/testdata/ruby_benchmark/hash_aref_str.rb b/test/testdata/ruby_benchmark/hash_aref_str.rb deleted file mode 100644 index 884913d123..0000000000 --- a/test/testdata/ruby_benchmark/hash_aref_str.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true -# typed: strict -# compiled: true -h = {} -strs = ('a'..'z').to_a.map!(&:freeze) -strs.each { |s| h[s] = s } -200_000.times { strs.each { |s| h[s] } } diff --git a/test/testdata/ruby_benchmark/hash_aref_sym.rb b/test/testdata/ruby_benchmark/hash_aref_sym.rb deleted file mode 100644 index 701a92dda2..0000000000 --- a/test/testdata/ruby_benchmark/hash_aref_sym.rb +++ /dev/null @@ -1,12 +0,0 @@ -# frozen_string_literal: true -# typed: strict -# compiled: true -h = {} -syms = ('a'..'z').to_a -begin - syms = eval("%i[#{syms.join(' ')}]") -rescue SyntaxError # <= 1.9.3 - syms.map!(&:to_sym) -end -syms.each { |s| h[s] = s } -200_000.times { syms.each { |s| h[s] } } diff --git a/test/testdata/ruby_benchmark/hash_aref_sym_long.rb b/test/testdata/ruby_benchmark/hash_aref_sym_long.rb deleted file mode 100644 index 04f7db5cd1..0000000000 --- a/test/testdata/ruby_benchmark/hash_aref_sym_long.rb +++ /dev/null @@ -1,16 +0,0 @@ -# frozen_string_literal: true -# typed: strict -# compiled: true -h = {} -syms = %w[puts warn syswrite write stat bacon lettuce tomato -some symbols in this array may already be interned others should not be -hash browns make good breakfast but not cooked using prime numbers -shift for division entries delete_if keys exist? -] -begin - syms = eval("%i[#{syms.join(' ')}]") -rescue SyntaxError # <= 1.9.3 - syms.map!(&:to_sym) -end -syms.each { |s| h[s] = s } -200_000.times { syms.each { |s| h[s] } } diff --git a/test/testdata/ruby_benchmark/hash_flatten.rb b/test/testdata/ruby_benchmark/hash_flatten.rb deleted file mode 100644 index 6b9dbde3c1..0000000000 --- a/test/testdata/ruby_benchmark/hash_flatten.rb +++ /dev/null @@ -1,12 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -h = {} - -10000.times do |i| - h[i] = nil -end - -1000.times do - h.flatten -end diff --git a/test/testdata/ruby_benchmark/hash_ident_flo.rb b/test/testdata/ruby_benchmark/hash_ident_flo.rb deleted file mode 100644 index 5e077f73d8..0000000000 --- a/test/testdata/ruby_benchmark/hash_ident_flo.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true -# typed: strict -# compiled: true -h = {}.compare_by_identity -strs = (1..10000).to_a.map!(&:to_f) -strs.each { |s| h[s] = s } -50.times { strs.each { |s| h[s] } } diff --git a/test/testdata/ruby_benchmark/hash_ident_num.rb b/test/testdata/ruby_benchmark/hash_ident_num.rb deleted file mode 100644 index 1a5758dab1..0000000000 --- a/test/testdata/ruby_benchmark/hash_ident_num.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true -# typed: strict -# compiled: true -h = {}.compare_by_identity -nums = (1..26).to_a -nums.each { |n| h[n] = n } -200_000.times { nums.each { |n| h[n] } } diff --git a/test/testdata/ruby_benchmark/hash_ident_obj.rb b/test/testdata/ruby_benchmark/hash_ident_obj.rb deleted file mode 100644 index 7c5a0311fd..0000000000 --- a/test/testdata/ruby_benchmark/hash_ident_obj.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true -# typed: strict -# compiled: true -h = {}.compare_by_identity -objs = 26.times.map { Object.new } -objs.each { |o| h[o] = o } -200_000.times { objs.each { |o| h[o] } } diff --git a/test/testdata/ruby_benchmark/hash_ident_str.rb b/test/testdata/ruby_benchmark/hash_ident_str.rb deleted file mode 100644 index ecfedb2d0f..0000000000 --- a/test/testdata/ruby_benchmark/hash_ident_str.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true -# typed: strict -# compiled: true -h = {}.compare_by_identity -strs = ('a'..'z').to_a -strs.each { |s| h[s] = s } -200_000.times { strs.each { |s| h[s] } } diff --git a/test/testdata/ruby_benchmark/hash_ident_sym.rb b/test/testdata/ruby_benchmark/hash_ident_sym.rb deleted file mode 100644 index fd3315bd56..0000000000 --- a/test/testdata/ruby_benchmark/hash_ident_sym.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true -# typed: strict -# compiled: true -h = {}.compare_by_identity -syms = ('a'..'z').to_a.map(&:to_sym) -syms.each { |s| h[s] = s } -200_000.times { syms.each { |s| h[s] } } diff --git a/test/testdata/ruby_benchmark/hash_keys.rb b/test/testdata/ruby_benchmark/hash_keys.rb deleted file mode 100644 index d1d2da503d..0000000000 --- a/test/testdata/ruby_benchmark/hash_keys.rb +++ /dev/null @@ -1,12 +0,0 @@ -# frozen_string_literal: true -# typed: strong -# compiled: true -h = {} - -10000.times do |i| - h[i] = nil -end - -5000.times do - h.keys -end diff --git a/test/testdata/ruby_benchmark/hash_literal_small2.rb b/test/testdata/ruby_benchmark/hash_literal_small2.rb deleted file mode 100644 index 41746cbe00..0000000000 --- a/test/testdata/ruby_benchmark/hash_literal_small2.rb +++ /dev/null @@ -1,6 +0,0 @@ -# frozen_string_literal: true -# typed: strong -# compiled: true -# frozen_string_literal: true - -1_000_000.times.map { { "foo" => "bar", "bar" => "baz" } } diff --git a/test/testdata/ruby_benchmark/hash_literal_small4.rb b/test/testdata/ruby_benchmark/hash_literal_small4.rb deleted file mode 100644 index f66f85d4b7..0000000000 --- a/test/testdata/ruby_benchmark/hash_literal_small4.rb +++ /dev/null @@ -1,6 +0,0 @@ -# frozen_string_literal: true -# typed: strong -# compiled: true -# frozen_string_literal: true - -1_000_000.times.map { { "foo" => "bar", "bar" => "baz", "baz" => "lol", "lol" => "lgtm" } } diff --git a/test/testdata/ruby_benchmark/hash_literal_small8.rb b/test/testdata/ruby_benchmark/hash_literal_small8.rb deleted file mode 100644 index c6f0700d1b..0000000000 --- a/test/testdata/ruby_benchmark/hash_literal_small8.rb +++ /dev/null @@ -1,6 +0,0 @@ -# frozen_string_literal: true -# typed: strong -# compiled: true -# frozen_string_literal: true - -1_000_000.times.map { { "foo" => "bar", "bar" => "baz", "baz" => "lol", "lol" => "lgtm", "lgtm" => "nope", "nope" => "ok", "ok" => "again", "again" => "wait" } } diff --git a/test/testdata/ruby_benchmark/hash_shift.rb b/test/testdata/ruby_benchmark/hash_shift.rb deleted file mode 100644 index 6f93c95c41..0000000000 --- a/test/testdata/ruby_benchmark/hash_shift.rb +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true -# typed: strict -# compiled: true -h = {} - -10000.times do |i| - h[i] = nil -end - -50000.times do - k, v = h.shift - h[k] = v -end diff --git a/test/testdata/ruby_benchmark/hash_shift_u16.rb b/test/testdata/ruby_benchmark/hash_shift_u16.rb deleted file mode 100644 index 07eb1f900b..0000000000 --- a/test/testdata/ruby_benchmark/hash_shift_u16.rb +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true -# typed: strict -# compiled: true -h = {} - -(16384..65536).each do |i| - h[i] = nil -end - -300000.times do - k, v = h.shift - h[k] = v -end diff --git a/test/testdata/ruby_benchmark/hash_shift_u24.rb b/test/testdata/ruby_benchmark/hash_shift_u24.rb deleted file mode 100644 index 51985bd4b2..0000000000 --- a/test/testdata/ruby_benchmark/hash_shift_u24.rb +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true -# typed: strict -# compiled: true -h = {} - -(0xff4000..0xffffff).each do |i| - h[i] = nil -end - -300000.times do - k, v = h.shift - h[k] = v -end diff --git a/test/testdata/ruby_benchmark/hash_shift_u32.rb b/test/testdata/ruby_benchmark/hash_shift_u32.rb deleted file mode 100644 index 790f4c11ae..0000000000 --- a/test/testdata/ruby_benchmark/hash_shift_u32.rb +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true -# typed: strict -# compiled: true -h = {} - -(0xffff4000..0xffffffff).each do |i| - h[i] = nil -end - -300000.times do - k, v = h.shift - h[k] = v -end diff --git a/test/testdata/ruby_benchmark/hash_small2.rb b/test/testdata/ruby_benchmark/hash_small2.rb deleted file mode 100644 index 80cd90de1f..0000000000 --- a/test/testdata/ruby_benchmark/hash_small2.rb +++ /dev/null @@ -1,4 +0,0 @@ -# frozen_string_literal: true -# typed: strong -# compiled: true -1000000.times.map{|i| a={}; 2.times{|j| a[j]=j}; a} diff --git a/test/testdata/ruby_benchmark/hash_small4.rb b/test/testdata/ruby_benchmark/hash_small4.rb deleted file mode 100644 index bc794164e7..0000000000 --- a/test/testdata/ruby_benchmark/hash_small4.rb +++ /dev/null @@ -1,4 +0,0 @@ -# frozen_string_literal: true -# typed: strong -# compiled: true -1000000.times.map{|i| a={}; 4.times{|j| a[j]=j}; a} diff --git a/test/testdata/ruby_benchmark/hash_small8.rb b/test/testdata/ruby_benchmark/hash_small8.rb deleted file mode 100644 index 3815aec730..0000000000 --- a/test/testdata/ruby_benchmark/hash_small8.rb +++ /dev/null @@ -1,4 +0,0 @@ -# frozen_string_literal: true -# typed: strong -# compiled: true -1000000.times.map{|i| a={}; 8.times{|j| a[j]=j}; a} diff --git a/test/testdata/ruby_benchmark/hash_to_proc.rb b/test/testdata/ruby_benchmark/hash_to_proc.rb deleted file mode 100644 index 97cbef7851..0000000000 --- a/test/testdata/ruby_benchmark/hash_to_proc.rb +++ /dev/null @@ -1,12 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -h = {} - -10000.times do |i| - h[i] = nil -end - -5000.times do |i| - [i].map(&h) -end diff --git a/test/testdata/ruby_benchmark/hash_values.rb b/test/testdata/ruby_benchmark/hash_values.rb deleted file mode 100644 index c3ca8ffc72..0000000000 --- a/test/testdata/ruby_benchmark/hash_values.rb +++ /dev/null @@ -1,12 +0,0 @@ -# frozen_string_literal: true -# typed: strong -# compiled: true -h = {} - -10000.times do |i| - h[i] = nil -end - -5000.times do - h.values -end diff --git a/test/testdata/ruby_benchmark/int_quo.rb b/test/testdata/ruby_benchmark/int_quo.rb deleted file mode 100644 index 760fd1ae9e..0000000000 --- a/test/testdata/ruby_benchmark/int_quo.rb +++ /dev/null @@ -1,4 +0,0 @@ -# frozen_string_literal: true -# typed: strong -# compiled: true -5000000.times { 42.quo(3) } diff --git a/test/testdata/ruby_benchmark/io_file_read.rb b/test/testdata/ruby_benchmark/io_file_read.rb deleted file mode 100644 index 6b8293e8df..0000000000 --- a/test/testdata/ruby_benchmark/io_file_read.rb +++ /dev/null @@ -1,18 +0,0 @@ -# frozen_string_literal: true -# typed: strong -# compiled: true -# -# Seek and Read file. -# - -require 'tempfile' - -max = 200_000 -str = "Hello world! " * 1000 -f = Tempfile.new('yarv-benchmark') -f.write str - -max.times{ - f.seek 0 - f.read -} diff --git a/test/testdata/ruby_benchmark/io_file_write.rb b/test/testdata/ruby_benchmark/io_file_write.rb deleted file mode 100644 index 33f81e9b9f..0000000000 --- a/test/testdata/ruby_benchmark/io_file_write.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true -# typed: strong -# compiled: true -# -# Seek and Write file. -# - -require 'tempfile' - -max = 200_000 -str = "Hello world! " * 1000 -f = Tempfile.new('yarv-benchmark') - -max.times{ - f.seek 0 - f.write str -} diff --git a/test/testdata/ruby_benchmark/io_pipe_rw.rb b/test/testdata/ruby_benchmark/io_pipe_rw.rb deleted file mode 100644 index 64d6a2c7dc..0000000000 --- a/test/testdata/ruby_benchmark/io_pipe_rw.rb +++ /dev/null @@ -1,16 +0,0 @@ -# frozen_string_literal: true -# typed: strict -# compiled: true -# Measure uncontended GVL performance via read/write with 1:1 threading -# If we switch to M:N threading, this will benchmark something else... -r, w = IO.pipe -src = '0'.freeze -dst = String.new -i = 0 -while i < 1_000_000 - i += 1 - w.write(src) - r.read(1, dst) -end -w.close -r.close diff --git a/test/testdata/ruby_benchmark/io_select.rb b/test/testdata/ruby_benchmark/io_select.rb deleted file mode 100644 index 8981d008ac..0000000000 --- a/test/testdata/ruby_benchmark/io_select.rb +++ /dev/null @@ -1,12 +0,0 @@ -# frozen_string_literal: true -# typed: strong -# compiled: true -# IO.select performance - -w = [ IO.pipe[1] ]; - -nr = 1000000 -nr.times { - IO.select nil, w -} - diff --git a/test/testdata/ruby_benchmark/irb_color.yml b/test/testdata/ruby_benchmark/irb_color.yml deleted file mode 100644 index ebdc8d7e8b..0000000000 --- a/test/testdata/ruby_benchmark/irb_color.yml +++ /dev/null @@ -1,13 +0,0 @@ -prelude: | - require 'irb/color' - code = <<~'CODE' - def self.foo # bar - :"erb #{ERB.new("<%= self %>", trim_mode: ?-).result}" - end - CODE -benchmark: - irb_color_complete: | - IRB::Color.colorize_code(code, complete: true) - irb_color_incomplete: | - IRB::Color.colorize_code(code, complete: false) -loop_count: 2000000 diff --git a/test/testdata/ruby_benchmark/irb_exec.yml b/test/testdata/ruby_benchmark/irb_exec.yml deleted file mode 100644 index 28933f8b38..0000000000 --- a/test/testdata/ruby_benchmark/irb_exec.yml +++ /dev/null @@ -1,10 +0,0 @@ -prelude: | - # frozen_string_literal: true - require 'rbconfig' - irb_f = [File.join(File.dirname(RbConfig.ruby), 'irb'), '-f'] -benchmark: - irb_exec: | - IO.popen(irb_f, 'w') do |io| - io.write('exit') - end -loop_count: 30 diff --git a/test/testdata/ruby_benchmark/ivar_get.rb b/test/testdata/ruby_benchmark/ivar_get.rb deleted file mode 100644 index fa76705af4..0000000000 --- a/test/testdata/ruby_benchmark/ivar_get.rb +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -class A - def initialize - @a = 5 - end - - def foo - @a - end -end - -obj = A.new -i = 0 - -while i < 1_000_000 - obj.foo - i += 1 -end - -p i diff --git a/test/testdata/ruby_benchmark/loop_for.rb b/test/testdata/ruby_benchmark/loop_for.rb deleted file mode 100644 index 027b40b4a3..0000000000 --- a/test/testdata/ruby_benchmark/loop_for.rb +++ /dev/null @@ -1,6 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -for i in 1..30_000_000 - # -end diff --git a/test/testdata/ruby_benchmark/loop_times.rb b/test/testdata/ruby_benchmark/loop_times.rb deleted file mode 100644 index 3555d7dfa0..0000000000 --- a/test/testdata/ruby_benchmark/loop_times.rb +++ /dev/null @@ -1,4 +0,0 @@ -# frozen_string_literal: true -# typed: strong -# compiled: true -30_000_000.times{|e|} diff --git a/test/testdata/ruby_benchmark/loop_whileloop.rb b/test/testdata/ruby_benchmark/loop_whileloop.rb deleted file mode 100644 index 36153de6db..0000000000 --- a/test/testdata/ruby_benchmark/loop_whileloop.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true -# typed: strong -# compiled: true -i = 0 -while i<30_000_000 # benchmark loop 1 - i += 1 -end diff --git a/test/testdata/ruby_benchmark/loop_whileloop2.rb b/test/testdata/ruby_benchmark/loop_whileloop2.rb deleted file mode 100644 index 4a2ff3b4dd..0000000000 --- a/test/testdata/ruby_benchmark/loop_whileloop2.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true -# typed: strong -# compiled: true -i = 0 -while i< 6_000_000 # benchmark loop 2 - i += 1 -end diff --git a/test/testdata/ruby_benchmark/marshal_dump_flo.rb b/test/testdata/ruby_benchmark/marshal_dump_flo.rb deleted file mode 100644 index 5fc62b05ba..0000000000 --- a/test/testdata/ruby_benchmark/marshal_dump_flo.rb +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true -# typed: strong -# compiled: true -bug10761 = 10000.times.map { |x| x.to_f } -100.times { Marshal.dump(bug10761) } diff --git a/test/testdata/ruby_benchmark/marshal_dump_load_time.rb b/test/testdata/ruby_benchmark/marshal_dump_load_time.rb deleted file mode 100644 index e9943d0cf1..0000000000 --- a/test/testdata/ruby_benchmark/marshal_dump_load_time.rb +++ /dev/null @@ -1,4 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -100000.times { Marshal.load(Marshal.dump(Time.now)) } diff --git a/test/testdata/ruby_benchmark/match_gt4.llo.exp b/test/testdata/ruby_benchmark/match_gt4.llo.exp deleted file mode 100644 index 783f794a25..0000000000 --- a/test/testdata/ruby_benchmark/match_gt4.llo.exp +++ /dev/null @@ -1,442 +0,0 @@ -; ModuleID = 'payload' -source_filename = "llvm-link" -target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } -%struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } -%struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } -%struct.anon.0 = type { i16, [2 x i8] } -%struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } -%struct.rb_iseq_location_struct = type { i64, i64, i64, i64, i32, %struct.rb_code_location_struct } -%struct.rb_code_location_struct = type { %struct.rb_code_position_struct, %struct.rb_code_position_struct } -%struct.rb_code_position_struct = type { i32, i32 } -%struct.iseq_insn_info = type { %struct.iseq_insn_info_entry*, i32*, i32, %struct.succ_index_table* } -%struct.iseq_insn_info_entry = type opaque -%struct.succ_index_table = type opaque -%struct.iseq_catch_table = type opaque -%union.iseq_inline_storage_entry = type { %struct.iseq_inline_cache_entry } -%struct.iseq_inline_cache_entry = type { i64, %struct.rb_cref_struct*, i64 } -%struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } -%struct.rb_scope_visi_struct = type { i8, [3 x i8] } -%struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } -%struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } -%union.anon.13 = type { %struct.rb_method_cfunc_struct } -%struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%struct.rb_calling_info = type { i64, i64, i32, i32 } -%union.anon.15 = type { i32 } -%struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.16 = type { i64, i64, i64, i64* } -%union.anon.17 = type { %struct.anon.18 } -%struct.anon.18 = type { i64, i32 } -%struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } -%struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } -%struct.rb_fiber_struct = type opaque -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } -%struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } -%struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.__pthread_cond_s } -%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } -%union.anon = type { i64 } -%struct.anon.5 = type { [65 x i64] } -%struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } -%struct.rb_event_hook_struct = type opaque -%struct.rb_postponed_job_struct = type opaque -%struct.list_head = type { %struct.list_node } -%struct.rb_objspace = type opaque -%struct.rb_at_exit_list = type { void (%struct.rb_vm_struct*)*, %struct.rb_at_exit_list* } -%struct.rb_builtin_function = type opaque -%struct.anon.6 = type { i64, i64, i64, i64 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } -%union.anon.9 = type { %union.pthread_cond_t } -%union.pthread_mutex_t = type { %struct.__pthread_mutex_s } -%struct.__pthread_mutex_s = type { i32, i32, i32, i32, i32, i16, i16, %struct.__pthread_internal_list } -%struct.__pthread_internal_list = type { %struct.__pthread_internal_list*, %struct.__pthread_internal_list* } -%struct.rb_unblock_callback = type { void (i8*)*, i8* } -%struct.rb_mutex_struct = type opaque -%struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.10 = type { %struct.anon.11 } -%struct.anon.11 = type { i64, i64, i32 } -%struct.st_table = type { i8, i8, i8, i32, %struct.st_hash_type*, i64, i64*, i64, i64, %struct.st_table_entry* } -%struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } -%struct.st_table_entry = type opaque -%struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } -%struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } -%struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } -%struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } -%struct.__sigset_t = type { [16 x i64] } -%struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } -%struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } -%struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } -%struct.rb_call_info_with_kwarg = type { %struct.rb_call_info, %struct.rb_call_info_kw_arg* } -%struct.rb_call_info_kw_arg = type { i32, [1 x i64] } -%struct.rb_captured_block = type { i64, i64*, %union.anon.20 } -%union.anon.20 = type { %struct.rb_iseq_struct* } -%struct.vm_ifunc = type { i64, i64, i64 (i64, i64, i32, i64*, i64)*, i8*, %struct.rb_code_position_struct } -%struct.sorbet_inlineIntrinsicEnv = type { i64, i64, i32, i64*, i64 } - -@ruby_current_execution_context_ptr = external local_unnamed_addr global %struct.rb_execution_context_struct*, align 8 -@rb_eRuntimeError = external local_unnamed_addr global i64, align 8 -@.str.9 = private unnamed_addr constant [95 x i8] c"sorbet_getBuildSCMRevision: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@.str.10 = private unnamed_addr constant [93 x i8] c"sorbet_getIsReleaseBuild: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@"stackFramePrecomputed_func_.17$152" = unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"rubyIdPrecomputed_" = internal unnamed_addr global i64 0, align 8 -@"str_" = private unnamed_addr constant [17 x i8] c"\00", align 1 -@"rubyStrFrozen_" = internal unnamed_addr global i64 0, align 8 -@"rubyStrFrozen_test/testdata/ruby_benchmark/match_gt4.rb" = internal unnamed_addr global i64 0, align 8 -@"str_test/testdata/ruby_benchmark/match_gt4.rb" = private unnamed_addr constant [42 x i8] c"test/testdata/ruby_benchmark/match_gt4.rb\00", align 1 -@iseqEncodedArray = internal global [5 x i64] zeroinitializer -@fileLineNumberInfo = internal global %struct.SorbetLineNumberInfo zeroinitializer -@"stackFramePrecomputed_func_.17$152$block_1" = unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"rubyIdPrecomputed_block in " = internal unnamed_addr global i64 0, align 8 -@"str_block in " = private unnamed_addr constant [26 x i8] c"block in \00", align 1 -@rubyIdPrecomputed_times = internal unnamed_addr global i64 0, align 8 -@str_times = private unnamed_addr constant [6 x i8] c"times\00", align 1 -@"str_(.)(.)(\\d+)(\\d)" = private unnamed_addr constant [16 x i8] c"(.)(.)(\\d+)(\\d)\00", align 1 -@"rubyRegexpFrozen_(.)(.)(\\d+)(\\d)" = internal unnamed_addr global i64 0, align 8 -@ic_new = internal global %struct.FunctionInlineCache zeroinitializer -@rubyIdPrecomputed_new = internal unnamed_addr global i64 0, align 8 -@str_new = private unnamed_addr constant [4 x i8] c"new\00", align 1 -@rubyStrFrozen_THX1138. = internal unnamed_addr global i64 0, align 8 -@str_THX1138. = private unnamed_addr constant [9 x i8] c"THX1138.\00", align 1 -@ic_match = internal global %struct.FunctionInlineCache zeroinitializer -@rubyIdPrecomputed_match = internal unnamed_addr global i64 0, align 8 -@str_match = private unnamed_addr constant [6 x i8] c"match\00", align 1 - -declare %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64, i64, i64, i64, %struct.rb_iseq_struct*, i32, i32, %struct.SorbetLineNumberInfo*, i64*, i32, i32) local_unnamed_addr #0 - -declare void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo*, i64*, i32) local_unnamed_addr #0 - -declare i64 @sorbet_readRealpath() local_unnamed_addr #0 - -declare void @sorbet_pushBlockFrame(%struct.rb_captured_block*) local_unnamed_addr #0 - -declare void @sorbet_popFrame() local_unnamed_addr #0 - -declare void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache*, i64, i32, i32, i32, i64*) local_unnamed_addr #0 - -declare i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache*, i64) local_unnamed_addr #0 - -declare void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_iseq_struct*) local_unnamed_addr #0 - -declare i64 @sorbet_vm_fstring_new(i8*, i64) local_unnamed_addr #0 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1 - -declare i64 @rb_intern2(i8*, i64) local_unnamed_addr #0 - -declare void @rb_gc_register_mark_object(i64) local_unnamed_addr #0 - -declare i64 @rb_reg_new(i8*, i64, i32) local_unnamed_addr #0 - -; Function Attrs: noreturn -declare void @rb_raise(i64, i8*, ...) local_unnamed_addr #2 - -declare i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct*, i32) local_unnamed_addr #0 - -declare %struct.vm_ifunc* @rb_vm_ifunc_new(i64 (i64, i64, i32, i64*, i64)*, i8*, i32, i32) local_unnamed_addr #0 - -; Function Attrs: nounwind ssp uwtable -define weak i32 @sorbet_getIsReleaseBuild() local_unnamed_addr #3 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([93 x i8], [93 x i8]* @.str.10, i64 0, i64 0)) #8 - unreachable -} - -; Function Attrs: nounwind ssp uwtable -define weak i8* @sorbet_getBuildSCMRevision() local_unnamed_addr #3 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([95 x i8], [95 x i8]* @.str.9, i64 0, i64 0)) #8 - unreachable -} - -; Function Attrs: ssp -define internal i64 @"func_.17$152$block_1"(i64 %firstYieldArgRaw, i64 %localsOffset, i32 %argc, i64* nocapture nofree readnone %argArray, i64 %blockArg) #4 !dbg !10 { -fastSymCallIntrinsic_Regexp_new: - %0 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !15 - %1 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %0, i64 0, i32 2 - %2 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %1, align 8, !tbaa !17 - %stackFrame = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.17$152$block_1", align 8 - %3 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame, %struct.rb_iseq_struct** %3, align 8, !tbaa !21 - %4 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 4 - %5 = load i64*, i64** %4, align 8, !tbaa !23 - %6 = load i64, i64* %5, align 8, !tbaa !6 - %7 = and i64 %6, -129 - store i64 %7, i64* %5, align 8, !tbaa !6 - %8 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 0 - store i64* getelementptr inbounds ([5 x i64], [5 x i64]* @iseqEncodedArray, i64 0, i64 4), i64** %8, align 8, !tbaa !15 - %"rubyRegexp_(.)(.)(\\d+)(\\d)" = load i64, i64* @"rubyRegexpFrozen_(.)(.)(\\d+)(\\d)", align 8, !dbg !24 - %9 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !24, !tbaa !15 - %10 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %9, i64 0, i32 5, !dbg !24 - %11 = load i32, i32* %10, align 8, !dbg !24, !tbaa !25 - %12 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %9, i64 0, i32 6, !dbg !24 - %13 = load i32, i32* %12, align 4, !dbg !24, !tbaa !26 - %14 = xor i32 %13, -1, !dbg !24 - %15 = and i32 %14, %11, !dbg !24 - %16 = icmp eq i32 %15, 0, !dbg !24 - br i1 %16, label %afterSend, label %21, !dbg !24, !prof !27 - -afterSend: ; preds = %21, %fastSymCallIntrinsic_Regexp_new - %rubyStr_THX1138. = load i64, i64* @rubyStrFrozen_THX1138., align 8, !dbg !28 - %17 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 1, !dbg !24 - %18 = load i64*, i64** %17, align 8, !dbg !24 - store i64 %"rubyRegexp_(.)(.)(\\d+)(\\d)", i64* %18, align 8, !dbg !24, !tbaa !6 - %19 = getelementptr inbounds i64, i64* %18, i64 1, !dbg !24 - store i64 %rubyStr_THX1138., i64* %19, align 8, !dbg !24, !tbaa !6 - %20 = getelementptr inbounds i64, i64* %19, i64 1, !dbg !24 - store i64* %20, i64** %17, align 8, !dbg !24 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_match, i64 0), !dbg !24 - ret i64 %send, !dbg !29 - -21: ; preds = %fastSymCallIntrinsic_Regexp_new - %22 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %9, i64 0, i32 8, !dbg !24 - %23 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %22, align 8, !dbg !24, !tbaa !30 - %24 = tail call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %23, i32 noundef 0) #9, !dbg !24 - br label %afterSend, !dbg !24 -} - -; Function Attrs: sspreq -define void @Init_match_gt4() local_unnamed_addr #5 { -entry: - %0 = alloca %struct.sorbet_inlineIntrinsicEnv, align 8 - %locals.i.i = alloca i64, i32 0, align 8 - %realpath = tail call i64 @sorbet_readRealpath() - %1 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([17 x i8], [17 x i8]* @"str_", i64 0, i64 0), i64 noundef 16) #9 - store i64 %1, i64* @"rubyIdPrecomputed_", align 8 - %2 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([26 x i8], [26 x i8]* @"str_block in ", i64 0, i64 0), i64 noundef 25) #9 - store i64 %2, i64* @"rubyIdPrecomputed_block in ", align 8 - %3 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([6 x i8], [6 x i8]* @str_times, i64 0, i64 0), i64 noundef 5) #9 - store i64 %3, i64* @rubyIdPrecomputed_times, align 8 - %4 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([4 x i8], [4 x i8]* @str_new, i64 0, i64 0), i64 noundef 3) #9 - store i64 %4, i64* @rubyIdPrecomputed_new, align 8 - %5 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([6 x i8], [6 x i8]* @str_match, i64 0, i64 0), i64 noundef 5) #9 - store i64 %5, i64* @rubyIdPrecomputed_match, align 8 - %6 = tail call i64 @sorbet_vm_fstring_new(i8* noundef getelementptr inbounds ([17 x i8], [17 x i8]* @"str_", i64 0, i64 0), i64 noundef 16) #9 - tail call void @rb_gc_register_mark_object(i64 %6) #9 - store i64 %6, i64* @"rubyStrFrozen_", align 8 - %7 = tail call i64 @sorbet_vm_fstring_new(i8* noundef getelementptr inbounds ([42 x i8], [42 x i8]* @"str_test/testdata/ruby_benchmark/match_gt4.rb", i64 0, i64 0), i64 noundef 41) #9 - tail call void @rb_gc_register_mark_object(i64 %7) #9 - store i64 %7, i64* @"rubyStrFrozen_test/testdata/ruby_benchmark/match_gt4.rb", align 8 - tail call void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef getelementptr inbounds ([5 x i64], [5 x i64]* @iseqEncodedArray, i32 0, i32 0), i32 noundef 5) - %"rubyId_.i.i" = load i64, i64* @"rubyIdPrecomputed_", align 8 - %"rubyStr_.i.i" = load i64, i64* @"rubyStrFrozen_", align 8 - %"rubyStr_test/testdata/ruby_benchmark/match_gt4.rb.i.i" = load i64, i64* @"rubyStrFrozen_test/testdata/ruby_benchmark/match_gt4.rb", align 8 - %8 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_.i.i", i64 %"rubyId_.i.i", i64 %"rubyStr_test/testdata/ruby_benchmark/match_gt4.rb.i.i", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 0, i32 noundef 4, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals.i.i, i32 noundef 0, i32 noundef 3) - store %struct.rb_iseq_struct* %8, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.17$152", align 8 - %9 = call i64 @sorbet_vm_fstring_new(i8* noundef getelementptr inbounds ([26 x i8], [26 x i8]* @"str_block in ", i64 0, i64 0), i64 noundef 25) #9 - call void @rb_gc_register_mark_object(i64 %9) #9 - %stackFrame.i.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.17$152", align 8 - %"rubyId_block in .i.i" = load i64, i64* @"rubyIdPrecomputed_block in ", align 8 - %"rubyStr_test/testdata/ruby_benchmark/match_gt4.rb.i3.i" = load i64, i64* @"rubyStrFrozen_test/testdata/ruby_benchmark/match_gt4.rb", align 8 - %10 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %9, i64 %"rubyId_block in .i.i", i64 %"rubyStr_test/testdata/ruby_benchmark/match_gt4.rb.i3.i", i64 %realpath, %struct.rb_iseq_struct* %stackFrame.i.i, i32 noundef 2, i32 noundef 4, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef null, i32 noundef 0, i32 noundef 3) - store %struct.rb_iseq_struct* %10, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.17$152$block_1", align 8 - %11 = call i64 @sorbet_vm_fstring_new(i8* noundef getelementptr inbounds ([16 x i8], [16 x i8]* @"str_(.)(.)(\\d+)(\\d)", i64 0, i64 0), i64 noundef 15) #9 - call void @rb_gc_register_mark_object(i64 %11) #9 - %12 = call i64 @rb_reg_new(i8* noundef getelementptr inbounds ([16 x i8], [16 x i8]* @"str_(.)(.)(\\d+)(\\d)", i64 0, i64 0), i64 noundef 15, i32 noundef 0) #9 - call void @rb_gc_register_mark_object(i64 %12) #9 - store i64 %12, i64* @"rubyRegexpFrozen_(.)(.)(\\d+)(\\d)", align 8 - %rubyId_new.i = load i64, i64* @rubyIdPrecomputed_new, align 8, !dbg !24 - call void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_new, i64 %rubyId_new.i, i32 noundef 16, i32 noundef 2, i32 noundef 0, i64* noundef null), !dbg !24 - %13 = call i64 @sorbet_vm_fstring_new(i8* noundef getelementptr inbounds ([9 x i8], [9 x i8]* @str_THX1138., i64 0, i64 0), i64 noundef 8) #9 - call void @rb_gc_register_mark_object(i64 %13) #9 - store i64 %13, i64* @rubyStrFrozen_THX1138., align 8 - %rubyId_match.i = load i64, i64* @rubyIdPrecomputed_match, align 8, !dbg !24 - call void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_match, i64 %rubyId_match.i, i32 noundef 16, i32 noundef 1, i32 noundef 0, i64* noundef null), !dbg !24 - %14 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !15 - %15 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %14, i64 0, i32 2 - %16 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %15, align 8, !tbaa !17 - %stackFrame.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.17$152", align 8 - %17 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %16, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame.i, %struct.rb_iseq_struct** %17, align 8, !tbaa !21 - %18 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %16, i64 0, i32 4 - %19 = load i64*, i64** %18, align 8, !tbaa !23 - %20 = load i64, i64* %19, align 8, !tbaa !6 - %21 = and i64 %20, -33 - store i64 %21, i64* %19, align 8, !tbaa !6 - call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %14, %struct.rb_control_frame_struct* %16, %struct.rb_iseq_struct* %stackFrame.i) #9 - %22 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %16, i64 0, i32 0 - store i64* getelementptr inbounds ([5 x i64], [5 x i64]* @iseqEncodedArray, i64 0, i64 4), i64** %22, align 8, !dbg !31, !tbaa !15 - %rubyId_times.i = load i64, i64* @rubyIdPrecomputed_times, align 8, !dbg !32 - %23 = bitcast %struct.sorbet_inlineIntrinsicEnv* %0 to i8*, !dbg !32 - call void @llvm.lifetime.start.p0i8(i64 noundef 40, i8* noundef nonnull %23) #9, !dbg !32 - %24 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %0, i64 0, i32 0, !dbg !32 - store i64 2000001, i64* %24, align 8, !dbg !32, !tbaa !33 - %25 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %0, i64 0, i32 1, !dbg !32 - store i64 %rubyId_times.i, i64* %25, align 8, !dbg !32, !tbaa !35 - %26 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %0, i64 0, i32 2, !dbg !32 - store i32 0, i32* %26, align 8, !dbg !32, !tbaa !36 - %27 = getelementptr inbounds %struct.sorbet_inlineIntrinsicEnv, %struct.sorbet_inlineIntrinsicEnv* %0, i64 0, i32 3, !dbg !32 - %28 = bitcast i64** %27 to i8*, !dbg !32 - call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %28, i8 0, i64 16, i1 false) #9, !dbg !32 - %29 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !32, !tbaa !15 - %30 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %29, i64 0, i32 2, !dbg !32 - %31 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %30, align 8, !dbg !32, !tbaa !17 - %32 = call %struct.vm_ifunc* @rb_vm_ifunc_new(i64 (i64, i64, i32, i64*, i64)* noundef @"func_.17$152$block_1", i8* noundef null, i32 noundef 0, i32 noundef 0) #9, !dbg !32 - %33 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %31, i64 0, i32 3, !dbg !32 - %34 = bitcast i64* %33 to %struct.rb_captured_block*, !dbg !32 - %35 = getelementptr inbounds i64, i64* %33, i64 2, !dbg !32 - %36 = bitcast i64* %35 to %struct.vm_ifunc**, !dbg !32 - store %struct.vm_ifunc* %32, %struct.vm_ifunc** %36, align 8, !dbg !32, !tbaa !37 - call void @llvm.experimental.noalias.scope.decl(metadata !38) #9, !dbg !32 - %37 = ptrtoint %struct.rb_captured_block* %34 to i64, !dbg !32 - %38 = or i64 %37, 3, !dbg !32 - %39 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %29, i64 0, i32 17, !dbg !32 - store i64 %38, i64* %39, align 8, !dbg !32, !tbaa !41 - %40 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !42, !tbaa !15 - %41 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %40, i64 0, i32 17, !dbg !42 - %42 = load i64, i64* %41, align 8, !dbg !42, !tbaa !41 - %43 = and i64 %42, -4, !dbg !42 - %44 = inttoptr i64 %43 to %struct.rb_captured_block*, !dbg !42 - store i64 0, i64* %41, align 8, !dbg !42, !tbaa !41 - call void @sorbet_pushBlockFrame(%struct.rb_captured_block* %44) #9, !dbg !42 - br label %45, !dbg !42 - -45: ; preds = %"func_.17$152$block_1.exit4.i.i", %entry - %46 = phi i64 [ 0, %entry ], [ %72, %"func_.17$152$block_1.exit4.i.i" ], !dbg !42 - %47 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !32, !tbaa !15 - %48 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %47, i64 0, i32 2, !dbg !32 - %49 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %48, align 8, !dbg !32, !tbaa !17 - %stackFrame.i1.i.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.17$152$block_1", align 8, !dbg !32 - %50 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %49, i64 0, i32 2, !dbg !32 - store %struct.rb_iseq_struct* %stackFrame.i1.i.i, %struct.rb_iseq_struct** %50, align 8, !dbg !32, !tbaa !21 - %51 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %49, i64 0, i32 4, !dbg !32 - %52 = load i64*, i64** %51, align 8, !dbg !32, !tbaa !23 - %53 = load i64, i64* %52, align 8, !dbg !32, !tbaa !6 - %54 = and i64 %53, -129, !dbg !32 - store i64 %54, i64* %52, align 8, !dbg !32, !tbaa !6 - %55 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %49, i64 0, i32 0, !dbg !32 - store i64* getelementptr inbounds ([5 x i64], [5 x i64]* @iseqEncodedArray, i64 0, i64 4), i64** %55, align 8, !dbg !32, !tbaa !15 - %"rubyRegexp_(.)(.)(\\d+)(\\d).i2.i.i" = load i64, i64* @"rubyRegexpFrozen_(.)(.)(\\d+)(\\d)", align 8, !dbg !44 - %56 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !44, !tbaa !15 - %57 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %56, i64 0, i32 5, !dbg !44 - %58 = load i32, i32* %57, align 8, !dbg !44, !tbaa !25 - %59 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %56, i64 0, i32 6, !dbg !44 - %60 = load i32, i32* %59, align 4, !dbg !44, !tbaa !26 - %61 = xor i32 %60, -1, !dbg !44 - %62 = and i32 %61, %58, !dbg !44 - %63 = icmp eq i32 %62, 0, !dbg !44 - br i1 %63, label %"func_.17$152$block_1.exit4.i.i", label %64, !dbg !44, !prof !27 - -64: ; preds = %45 - %65 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %56, i64 0, i32 8, !dbg !44 - %66 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %65, align 8, !dbg !44, !tbaa !30 - %67 = call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %66, i32 noundef 0) #9, !dbg !44 - br label %"func_.17$152$block_1.exit4.i.i", !dbg !44 - -"func_.17$152$block_1.exit4.i.i": ; preds = %64, %45 - %rubyStr_THX1138..i3.i.i = load i64, i64* @rubyStrFrozen_THX1138., align 8, !dbg !46 - %68 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %49, i64 0, i32 1, !dbg !44 - %69 = load i64*, i64** %68, align 8, !dbg !44 - store i64 %"rubyRegexp_(.)(.)(\\d+)(\\d).i2.i.i", i64* %69, align 8, !dbg !44, !tbaa !6 - %70 = getelementptr inbounds i64, i64* %69, i64 1, !dbg !44 - store i64 %rubyStr_THX1138..i3.i.i, i64* %70, align 8, !dbg !44, !tbaa !6 - %71 = getelementptr inbounds i64, i64* %70, i64 1, !dbg !44 - store i64* %71, i64** %68, align 8, !dbg !44 - %send = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_match, i64 0), !dbg !44 - %72 = add nuw nsw i64 %46, 1, !dbg !42 - %73 = icmp eq i64 %72, 1000000, !dbg !42 - br i1 %73, label %forward_sorbet_rb_int_dotimes_withBlock.exit.i, label %45, !dbg !42, !llvm.loop !47 - -forward_sorbet_rb_int_dotimes_withBlock.exit.i: ; preds = %"func_.17$152$block_1.exit4.i.i" - call void @sorbet_popFrame() #9, !dbg !42 - call void @llvm.lifetime.end.p0i8(i64 noundef 40, i8* noundef nonnull %23) #9, !dbg !32 - %74 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !32, !tbaa !15 - %75 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %74, i64 0, i32 5, !dbg !32 - %76 = load i32, i32* %75, align 8, !dbg !32, !tbaa !25 - %77 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %74, i64 0, i32 6, !dbg !32 - %78 = load i32, i32* %77, align 4, !dbg !32, !tbaa !26 - %79 = xor i32 %78, -1, !dbg !32 - %80 = and i32 %79, %76, !dbg !32 - %81 = icmp eq i32 %80, 0, !dbg !32 - br i1 %81, label %"func_.17$152.exit", label %82, !dbg !32, !prof !27 - -82: ; preds = %forward_sorbet_rb_int_dotimes_withBlock.exit.i - %83 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %74, i64 0, i32 8, !dbg !32 - %84 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %83, align 8, !dbg !32, !tbaa !30 - %85 = call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %84, i32 noundef 0) #9, !dbg !32 - br label %"func_.17$152.exit", !dbg !32 - -"func_.17$152.exit": ; preds = %forward_sorbet_rb_int_dotimes_withBlock.exit.i, %82 - store i64* getelementptr inbounds ([5 x i64], [5 x i64]* @iseqEncodedArray, i64 0, i64 4), i64** %22, align 8, !tbaa !15 - ret void -} - -; Function Attrs: inaccessiblememonly nofree nosync nounwind willreturn -declare void @llvm.experimental.noalias.scope.decl(metadata) #6 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly -declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #7 - -attributes #0 = { "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #1 = { argmemonly nofree nosync nounwind willreturn } -attributes #2 = { noreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #3 = { nounwind ssp uwtable "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #4 = { ssp } -attributes #5 = { sspreq } -attributes #6 = { inaccessiblememonly nofree nosync nounwind willreturn } -attributes #7 = { argmemonly nofree nosync nounwind willreturn writeonly } -attributes #8 = { noreturn nounwind } -attributes #9 = { nounwind } - -!llvm.module.flags = !{!0, !1, !2} -!llvm.dbg.cu = !{!3} - -!0 = !{i32 2, !"Debug Info Version", i32 3} -!1 = !{i32 4, !"cf-protection-return", i32 1} -!2 = !{i32 4, !"cf-protection-branch", i32 1} -!3 = distinct !DICompileUnit(language: DW_LANG_C, file: !4, producer: "Sorbet LLVM", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !5) -!4 = !DIFile(filename: "test/testdata/ruby_benchmark/match_gt4.rb", directory: ".") -!5 = !{} -!6 = !{!7, !7, i64 0} -!7 = !{!"long", !8, i64 0} -!8 = !{!"omnipotent char", !9, i64 0} -!9 = !{!"Simple C/C++ TBAA"} -!10 = distinct !DISubprogram(name: ".", linkageName: "func_.17$152$block_1", scope: !11, file: !4, line: 4, type: !12, scopeLine: 4, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!11 = distinct !DISubprogram(name: ".", linkageName: "func_.17$152", scope: null, file: !4, line: 4, type: !12, scopeLine: 4, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!12 = !DISubroutineType(types: !13) -!13 = !{!14} -!14 = !DIBasicType(name: "VALUE", size: 64, encoding: DW_ATE_signed) -!15 = !{!16, !16, i64 0} -!16 = !{!"any pointer", !8, i64 0} -!17 = !{!18, !16, i64 16} -!18 = !{!"rb_execution_context_struct", !16, i64 0, !7, i64 8, !16, i64 16, !16, i64 24, !16, i64 32, !19, i64 40, !19, i64 44, !16, i64 48, !16, i64 56, !16, i64 64, !7, i64 72, !7, i64 80, !16, i64 88, !7, i64 96, !16, i64 104, !16, i64 112, !7, i64 120, !7, i64 128, !8, i64 136, !8, i64 137, !7, i64 144, !20, i64 152} -!19 = !{!"int", !8, i64 0} -!20 = !{!"", !16, i64 0, !16, i64 8, !7, i64 16, !8, i64 24} -!21 = !{!22, !16, i64 16} -!22 = !{!"rb_control_frame_struct", !16, i64 0, !16, i64 8, !16, i64 16, !7, i64 24, !16, i64 32, !16, i64 40, !16, i64 48} -!23 = !{!22, !16, i64 32} -!24 = !DILocation(line: 4, column: 17, scope: !10) -!25 = !{!18, !19, i64 40} -!26 = !{!18, !19, i64 44} -!27 = !{!"branch_weights", i32 2000, i32 1} -!28 = !DILocation(line: 4, column: 41, scope: !10) -!29 = !DILocation(line: 4, column: 1, scope: !10) -!30 = !{!18, !16, i64 56} -!31 = !DILocation(line: 0, scope: !11) -!32 = !DILocation(line: 4, column: 1, scope: !11) -!33 = !{!34, !7, i64 0} -!34 = !{!"sorbet_inlineIntrinsicEnv", !7, i64 0, !7, i64 8, !19, i64 16, !16, i64 24, !7, i64 32} -!35 = !{!34, !7, i64 8} -!36 = !{!34, !19, i64 16} -!37 = !{!8, !8, i64 0} -!38 = !{!39} -!39 = distinct !{!39, !40, !"VM_BH_FROM_IFUNC_BLOCK: argument 0"} -!40 = distinct !{!40, !"VM_BH_FROM_IFUNC_BLOCK"} -!41 = !{!18, !7, i64 128} -!42 = !DILocation(line: 4, column: 1, scope: !11, inlinedAt: !43) -!43 = distinct !DILocation(line: 4, column: 1, scope: !11) -!44 = !DILocation(line: 4, column: 17, scope: !10, inlinedAt: !45) -!45 = distinct !DILocation(line: 4, column: 1, scope: !11, inlinedAt: !43) -!46 = !DILocation(line: 4, column: 41, scope: !10, inlinedAt: !45) -!47 = distinct !{!47, !48} -!48 = !{!"llvm.loop.unroll.disable"} diff --git a/test/testdata/ruby_benchmark/match_gt4.rb b/test/testdata/ruby_benchmark/match_gt4.rb deleted file mode 100644 index d332922183..0000000000 --- a/test/testdata/ruby_benchmark/match_gt4.rb +++ /dev/null @@ -1,4 +0,0 @@ -# frozen_string_literal: true -# typed: strong -# compiled: true -1000000.times { /(.)(.)(\d+)(\d)/.match("THX1138.") } diff --git a/test/testdata/ruby_benchmark/match_small.rb b/test/testdata/ruby_benchmark/match_small.rb deleted file mode 100644 index 33cf16c37f..0000000000 --- a/test/testdata/ruby_benchmark/match_small.rb +++ /dev/null @@ -1,4 +0,0 @@ -# frozen_string_literal: true -# typed: strong -# compiled: true -1000000.times { 'haystack'.match(/hay/) } diff --git a/test/testdata/ruby_benchmark/other-lang/ack.pl b/test/testdata/ruby_benchmark/other-lang/ack.pl deleted file mode 100644 index 201e22ddfa..0000000000 --- a/test/testdata/ruby_benchmark/other-lang/ack.pl +++ /dev/null @@ -1,11 +0,0 @@ -use integer; - -sub Ack { - return $_[0] ? ($_[1] ? Ack($_[0]-1, Ack($_[0], $_[1]-1)) - : Ack($_[0]-1, 1)) - : $_[1]+1; -} - -my $NUM = 9; -$NUM = 1 if ($NUM < 1); -my $ack = Ack(3, $NUM); diff --git a/test/testdata/ruby_benchmark/other-lang/ack.py b/test/testdata/ruby_benchmark/other-lang/ack.py deleted file mode 100644 index 9968e7cfcf..0000000000 --- a/test/testdata/ruby_benchmark/other-lang/ack.py +++ /dev/null @@ -1,16 +0,0 @@ -import sys -sys.setrecursionlimit(5000000) - -def Ack(M, N): - if (not M): - return( N + 1 ) - if (not N): - return( Ack(M-1, 1) ) - return( Ack(M-1, Ack(M, N-1)) ) - -def main(): - NUM = 9 - sys.setrecursionlimit(10000) - Ack(3, NUM) - -main() diff --git a/test/testdata/ruby_benchmark/other-lang/ack.rb b/test/testdata/ruby_benchmark/other-lang/ack.rb deleted file mode 100644 index 270a0b42c4..0000000000 --- a/test/testdata/ruby_benchmark/other-lang/ack.rb +++ /dev/null @@ -1,15 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -def ack(m, n) - if m == 0 then - n + 1 - elsif n == 0 then - ack(m - 1, 1) - else - ack(m - 1, ack(m, n - 1)) - end -end - -NUM = 9 -ack(3, NUM) diff --git a/test/testdata/ruby_benchmark/other-lang/ack.scm b/test/testdata/ruby_benchmark/other-lang/ack.scm deleted file mode 100644 index a80b73ba55..0000000000 --- a/test/testdata/ruby_benchmark/other-lang/ack.scm +++ /dev/null @@ -1,7 +0,0 @@ -(define (ack m n) - (cond ((zero? m) (+ n 1)) - ((zero? n) (ack (- m 1) 1)) - (else (ack (- m 1) (ack m (- n 1)))))) - -(ack 3 9) - diff --git a/test/testdata/ruby_benchmark/other-lang/fact.pl b/test/testdata/ruby_benchmark/other-lang/fact.pl deleted file mode 100644 index a9b0b69cdf..0000000000 --- a/test/testdata/ruby_benchmark/other-lang/fact.pl +++ /dev/null @@ -1,13 +0,0 @@ -sub fact{ - my $n = @_[0]; - if($n < 2){ - return 1; - } - else{ - return $n * fact($n-1); - } -} - -for($i=0; $i<10000; $i++){ - &fact(100); -} diff --git a/test/testdata/ruby_benchmark/other-lang/fact.py b/test/testdata/ruby_benchmark/other-lang/fact.py deleted file mode 100644 index 01593965d9..0000000000 --- a/test/testdata/ruby_benchmark/other-lang/fact.py +++ /dev/null @@ -1,18 +0,0 @@ -#import sys -#sys.setrecursionlimit(1000) - -def factL(n): - r = 1 - for x in range(2, n): - r *= x - return r - -def factR(n): - if n < 2: - return 1 - else: - return n * factR(n-1) - -for i in range(10000): - factR(100) - diff --git a/test/testdata/ruby_benchmark/other-lang/fact.rb b/test/testdata/ruby_benchmark/other-lang/fact.rb deleted file mode 100644 index de1f814fcb..0000000000 --- a/test/testdata/ruby_benchmark/other-lang/fact.rb +++ /dev/null @@ -1,16 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -def fact(n) - if n < 2 - 1 - else - n * fact(n-1) - end -end - -i = 0 -while i<10000 - i += 1 - fact(100) -end diff --git a/test/testdata/ruby_benchmark/other-lang/fact.scm b/test/testdata/ruby_benchmark/other-lang/fact.scm deleted file mode 100644 index c98a7fedd3..0000000000 --- a/test/testdata/ruby_benchmark/other-lang/fact.scm +++ /dev/null @@ -1,8 +0,0 @@ -(define (fact n) - (if (< n 2) - 1 - (* n (fact (- n 1))))) - -(dotimes (i 10000) - (fact 100)) - diff --git a/test/testdata/ruby_benchmark/other-lang/fib.pl b/test/testdata/ruby_benchmark/other-lang/fib.pl deleted file mode 100644 index a46f666d1e..0000000000 --- a/test/testdata/ruby_benchmark/other-lang/fib.pl +++ /dev/null @@ -1,11 +0,0 @@ -sub fib{ - my $n = $_[0]; - if($n < 3){ - return 1; - } - else{ - return fib($n-1) + fib($n-2); - } -}; - -&fib(34); diff --git a/test/testdata/ruby_benchmark/other-lang/fib.py b/test/testdata/ruby_benchmark/other-lang/fib.py deleted file mode 100644 index 45f2bceb8d..0000000000 --- a/test/testdata/ruby_benchmark/other-lang/fib.py +++ /dev/null @@ -1,7 +0,0 @@ -def fib(n): - if n < 3: - return 1 - else: - return fib(n-1) + fib(n-2) - -fib(34) diff --git a/test/testdata/ruby_benchmark/other-lang/fib.rb b/test/testdata/ruby_benchmark/other-lang/fib.rb deleted file mode 100644 index 61cbb12b42..0000000000 --- a/test/testdata/ruby_benchmark/other-lang/fib.rb +++ /dev/null @@ -1,12 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -def fib n - if n < 3 - 1 - else - fib(n-1) + fib(n-2) - end -end - -fib(34) diff --git a/test/testdata/ruby_benchmark/other-lang/fib.scm b/test/testdata/ruby_benchmark/other-lang/fib.scm deleted file mode 100644 index 2fc4e225bd..0000000000 --- a/test/testdata/ruby_benchmark/other-lang/fib.scm +++ /dev/null @@ -1,7 +0,0 @@ -(define (fib n) - (if (< n 3) - 1 - (+ (fib (- n 1)) (fib (- n 2))))) - -(fib 34) - diff --git a/test/testdata/ruby_benchmark/other-lang/loop.pl b/test/testdata/ruby_benchmark/other-lang/loop.pl deleted file mode 100644 index 2777490aaa..0000000000 --- a/test/testdata/ruby_benchmark/other-lang/loop.pl +++ /dev/null @@ -1,3 +0,0 @@ -for($i=0; $i<30000000; $i++){ -} - diff --git a/test/testdata/ruby_benchmark/other-lang/loop.py b/test/testdata/ruby_benchmark/other-lang/loop.py deleted file mode 100644 index 003749bf3a..0000000000 --- a/test/testdata/ruby_benchmark/other-lang/loop.py +++ /dev/null @@ -1,2 +0,0 @@ -for i in xrange(30000000): - pass diff --git a/test/testdata/ruby_benchmark/other-lang/loop.rb b/test/testdata/ruby_benchmark/other-lang/loop.rb deleted file mode 100644 index 647a0dfd41..0000000000 --- a/test/testdata/ruby_benchmark/other-lang/loop.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true -# typed: strong -# compiled: true -i = 0 -while i<30000000 - i += 1 -end diff --git a/test/testdata/ruby_benchmark/other-lang/loop.scm b/test/testdata/ruby_benchmark/other-lang/loop.scm deleted file mode 100644 index 3364f7e679..0000000000 --- a/test/testdata/ruby_benchmark/other-lang/loop.scm +++ /dev/null @@ -1 +0,0 @@ -(dotimes (x 30000000)) diff --git a/test/testdata/ruby_benchmark/other-lang/loop2.rb b/test/testdata/ruby_benchmark/other-lang/loop2.rb deleted file mode 100644 index b2dc56f53a..0000000000 --- a/test/testdata/ruby_benchmark/other-lang/loop2.rb +++ /dev/null @@ -1,4 +0,0 @@ -# frozen_string_literal: true -# typed: strong -# compiled: true -30000000.times{} diff --git a/test/testdata/ruby_benchmark/other-lang/tak.pl b/test/testdata/ruby_benchmark/other-lang/tak.pl deleted file mode 100644 index 7e748a67c6..0000000000 --- a/test/testdata/ruby_benchmark/other-lang/tak.pl +++ /dev/null @@ -1,11 +0,0 @@ -sub tak { - local($x, $y, $z) = @_; - if (!($y < $x)) { - return $z; - } else { - return &tak(&tak($x - 1, $y, $z), - &tak($y - 1, $z, $x), - &tak($z - 1, $x, $y)); - } -} -&tak(18, 9, 0); diff --git a/test/testdata/ruby_benchmark/other-lang/tak.py b/test/testdata/ruby_benchmark/other-lang/tak.py deleted file mode 100644 index 04f3f6829c..0000000000 --- a/test/testdata/ruby_benchmark/other-lang/tak.py +++ /dev/null @@ -1,8 +0,0 @@ -def tak(x, y, z): - if not(y - Date: 1996/04/28 - Message-Id: <4lv7bc$oh@news.ycc.yale.edu> - References: <317C405E.5DFA@panix.com> <4lk6vl$gde@ns.oar.net> - To: 75176.2330@compuserve.com - Content-Type: text/plain; charset=us-ascii - Organization: Yale University - X-Url: news:4lk6vl$gde@ns.oar.net - Mime-Version: 1.0 - Newsgroups: rec.games.roguelike.nethack - X-Mailer: Mozilla 1.1N (Macintosh; I; 68K) - - Hello there, Izchak Miller was my father. When I was younger I spent - many a night, hunched over the keyboard with a cup of tea, playing - nethack with him and my brother. my dad was a philosopher with a strong - weakness for fantasy/sci fi. I remember when he started to get involved - with the Nethack team- my brother's Dungeons and Dragons monster book - found a regular place beside my dad's desk. it's nice to see him living - on in the game he loved so much :-). - Tamar Miller - - The following is a really long word of 5000 characters: - - wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww - EOS - - # prepare 'wc.input' - - def prepare_wc_input(wcbase) - wcinput = File.join(File.dirname($0), 'wc.input') - unless FileTest.exist?(wcinput) - data = wcbase.dup - 13.times{ - data << data - } - open(wcinput, 'w'){|f| f.write data} - end - end - - prepare_wc_input(wc_input_base) - -benchmark: - so_count_words: | - # $Id: wc-ruby.code,v 1.4 2004/11/13 07:43:32 bfulgham Exp $ - # http://www.bagley.org/~doug/shootout/ - # with help from Paul Brannan - input = open(File.join(File.dirname($0), 'wc.input'), 'rb') - - nl = nw = nc = 0 - while true - tmp = input.read(4096) or break - data = tmp << (input.gets || "") - nc += data.length - nl += data.count("\n") - ((data.strip! || data).tr!("\n", " ") || data).squeeze! - nw += data.count(" ") + 1 - end - # STDERR.puts "#{nl} #{nw} #{nc}" - -loop_count: 1 diff --git a/test/testdata/ruby_benchmark/so_exception.rb b/test/testdata/ruby_benchmark/so_exception.rb deleted file mode 100644 index b7ef2dfb7c..0000000000 --- a/test/testdata/ruby_benchmark/so_exception.rb +++ /dev/null @@ -1,64 +0,0 @@ -# frozen_string_literal: true -#!/usr/bin/ruby -# typed: true -# compiled: true -# -*- Ruby -*- -# $Id: except-ruby.code,v 1.4 2004/11/13 07:41:33 bfulgham Exp $ -# http://www.bagley.org/~doug/shootout/ - -$HI = 0 -$LO = 0 -NUM = 250000 # Integer(ARGV[0] || 1) - - -class Lo_Exception < Exception - def initialize(num) - @value = num - end -end - -class Hi_Exception < Exception - def initialize(num) - @value = num - end -end - -def some_function(num) - begin - hi_function(num) - rescue - print "We shouldn't get here, exception is: #{$!.type}\n" - end -end - -def hi_function(num) - begin - lo_function(num) - rescue Hi_Exception - $HI = $HI + 1 - end -end - -def lo_function(num) - begin - blowup(num) - rescue Lo_Exception - $LO = $LO + 1 - end -end - -def blowup(num) - if num % 2 == 0 - raise Lo_Exception.new(num) - else - raise Hi_Exception.new(num) - end -end - - -i = 1 -max = NUM+1 -while i < max - i += 1 - some_function(i+1) -end diff --git a/test/testdata/ruby_benchmark/so_k_nucleotide.yml b/test/testdata/ruby_benchmark/so_k_nucleotide.yml deleted file mode 100644 index d7df086c39..0000000000 --- a/test/testdata/ruby_benchmark/so_k_nucleotide.yml +++ /dev/null @@ -1,155 +0,0 @@ -prelude: | - bm_so_fasta = <<'EOS' - # The Computer Language Shootout - # http://shootout.alioth.debian.org/ - # Contributed by Sokolov Yura - - $last = 42.0 - def gen_random(max, im=139968, ia=3877, ic=29573) - (max * ($last = ($last * ia + ic) % im)) / im - end - - alu = - "GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGG"+ - "GAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGA"+ - "CCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAAT"+ - "ACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCA"+ - "GCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGG"+ - "AGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCC"+ - "AGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAA" - - iub = [ - ["a", 0.27], - ["c", 0.12], - ["g", 0.12], - ["t", 0.27], - - ["B", 0.02], - ["D", 0.02], - ["H", 0.02], - ["K", 0.02], - ["M", 0.02], - ["N", 0.02], - ["R", 0.02], - ["S", 0.02], - ["V", 0.02], - ["W", 0.02], - ["Y", 0.02], - ] - homosapiens = [ - ["a", 0.3029549426680], - ["c", 0.1979883004921], - ["g", 0.1975473066391], - ["t", 0.3015094502008], - ] - - def make_repeat_fasta(id, desc, src, n) - puts ">#{id} #{desc}" - v = nil - width = 60 - l = src.length - s = src * ((n / l) + 1) - s.slice!(n, l) - puts(s.scan(/.{1,#{width}}/).join("\n")) - end - - def make_random_fasta(id, desc, table, n) - puts ">#{id} #{desc}" - rand, v = nil,nil - width = 60 - chunk = 1 * width - prob = 0.0 - table.each{|v| v[1]= (prob += v[1])} - for i in 1..(n/width) - puts((1..width).collect{ - rand = gen_random(1.0) - table.find{|v| v[1]>rand}[0] - }.join) - end - if n%width != 0 - puts((1..(n%width)).collect{ - rand = gen_random(1.0) - table.find{|v| v[1]>rand}[0] - }.join) - end - end - - - n = (ARGV[0] or 250_000).to_i - - make_repeat_fasta('ONE', 'Homo sapiens alu', alu, n*2) - make_random_fasta('TWO', 'IUB ambiguity codes', iub, n*3) - make_random_fasta('THREE', 'Homo sapiens frequency', homosapiens, n*5) - EOS -benchmark: - - name: so_k_nucleotide - prelude: | - script = File.join(File.dirname($0), 'bm_so_fasta.rb') - File.write(script, bm_so_fasta) - - def prepare_fasta_output n - filebase = File.join(File.dirname($0), 'fasta.output') - script = File.join(File.dirname($0), 'bm_so_fasta.rb') - file = "#{filebase}.#{n}" - - unless FileTest.exist?(file) - STDERR.puts "preparing #{file}" - - open(file, 'w'){|f| - ARGV[0] = n - $stdout = f - load script - $stdout = STDOUT - } - end - end - prepare_fasta_output(100_000) - script: | - # The Computer Language Shootout - # http://shootout.alioth.debian.org - # - # contributed by jose fco. gonzalez - # modified by Sokolov Yura - - seq = String.new - - def frecuency( seq,length ) - n, table = seq.length - length + 1, Hash.new(0) - f, i = nil, nil - (0 ... length).each do |f| - (f ... n).step(length) do |i| - table[seq[i,length]] += 1 - end - end - [n,table] - - end - - def sort_by_freq( seq,length ) - n,table = frecuency( seq,length ) - a, b, v = nil, nil, nil - table.sort{|a,b| b[1] <=> a[1]}.each do |v| - puts "%s %.3f" % [v[0].upcase,((v[1]*100).to_f/n)] - end - puts - end - - def find_seq( seq,s ) - n,table = frecuency( seq,s.length ) - puts "#{table[s].to_s}\t#{s.upcase}" - end - - input = open(File.join(File.dirname($0), 'fasta.output.100000'), 'rb') - - line = input.gets while line !~ /^>THREE/ - line = input.gets - - while (line !~ /^>/) & line do - seq << line.chomp - line = input.gets - end - - [1,2].each {|i| sort_by_freq( seq,i ) } - - %w(ggt ggta ggtatt ggtattttaatt ggtattttaatttatagt).each{|s| find_seq( seq,s) } - loop_count: 1 diff --git a/test/testdata/ruby_benchmark/so_nested_loop.rb b/test/testdata/ruby_benchmark/so_nested_loop.rb deleted file mode 100644 index 9c7a2cdf5c..0000000000 --- a/test/testdata/ruby_benchmark/so_nested_loop.rb +++ /dev/null @@ -1,27 +0,0 @@ -# frozen_string_literal: true -#!/usr/bin/ruby -# typed: strong -# compiled: true -# -*- Ruby -*- -# $Id: nestedloop-ruby.code,v 1.4 2004/11/13 07:42:22 bfulgham Exp $ -# http://www.bagley.org/~doug/shootout/ -# from Avi Bryant - -n = 16 # Integer(ARGV.shift || 1) -x = 0 -n.times do - n.times do - n.times do - n.times do - n.times do - n.times do - x += 1 - end - end - end - end - end -end -# puts x - - diff --git a/test/testdata/ruby_benchmark/so_object.rb b/test/testdata/ruby_benchmark/so_object.rb deleted file mode 100644 index 94d3b2fc6d..0000000000 --- a/test/testdata/ruby_benchmark/so_object.rb +++ /dev/null @@ -1,59 +0,0 @@ -# frozen_string_literal: true -#!/usr/bin/ruby -# typed: true -# compiled: true -# -*- Ruby -*- -# $Id: objinst-ruby.code,v 1.4 2004/11/13 07:42:25 bfulgham Exp $ -# http://www.bagley.org/~doug/shootout/ -# with help from Aristarkh Zagorodnikov - -class Toggle - def initialize(start_state) - @bool = start_state - end - - def value - @bool - end - - def activate - @bool = !@bool - self - end -end - -class NthToggle < Toggle - def initialize(start_state, max_counter) - super start_state - @count_max = max_counter - @counter = 0 - end - - def activate - @counter += 1 - if @counter >= @count_max - @bool = !@bool - @counter = 0 - end - self - end -end - -n = 1500000 # (ARGV.shift || 1).to_i - -toggle = Toggle.new 1 -5.times do - toggle.activate.value ? 'true' : 'false' -end -n.times do - toggle = Toggle.new 1 -end - -ntoggle = NthToggle.new 1, 3 -8.times do - ntoggle.activate.value ? 'true' : 'false' -end -n.times do - ntoggle = NthToggle.new 1, 3 -end - diff --git a/test/testdata/ruby_benchmark/so_random.rb b/test/testdata/ruby_benchmark/so_random.rb deleted file mode 100644 index a39a3f1d82..0000000000 --- a/test/testdata/ruby_benchmark/so_random.rb +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -# from http://www.bagley.org/~doug/shootout/bench/random/random.ruby - -IM = 139968.0 -IA = 3877.0 -IC = 29573.0 - -$last = 42.0 - -def gen_random(max) - (max * ($last = ($last * IA + IC) % IM)) / IM -end - -N = 3_000_000 - -i = 0 -while i#{id} #{desc}" - v = nil - width = 60 - l = src.length - s = src * ((n / l) + 1) - s.slice!(n, l) - puts(s.scan(/.{1,#{width}}/).join("\n")) - end - - def make_random_fasta(id, desc, table, n) - puts ">#{id} #{desc}" - rand, v = nil,nil - width = 60 - chunk = 1 * width - prob = 0.0 - table.each{|v| v[1]= (prob += v[1])} - for i in 1..(n/width) - puts((1..width).collect{ - rand = gen_random(1.0) - table.find{|v| v[1]>rand}[0] - }.join) - end - if n%width != 0 - puts((1..(n%width)).collect{ - rand = gen_random(1.0) - table.find{|v| v[1]>rand}[0] - }.join) - end - end - - - n = (ARGV[0] or 250_000).to_i - - make_repeat_fasta('ONE', 'Homo sapiens alu', alu, n*2) - make_random_fasta('TWO', 'IUB ambiguity codes', iub, n*3) - make_random_fasta('THREE', 'Homo sapiens frequency', homosapiens, n*5) - EOS -benchmark: - - name: so_reverse_complement - prelude: | - script = File.join(File.dirname($0), 'bm_so_fasta.rb') - File.write(script, bm_so_fasta) - - def prepare_fasta_output n - filebase = File.join(File.dirname($0), 'fasta.output') - script = File.join(File.dirname($0), 'bm_so_fasta.rb') - file = "#{filebase}.#{n}" - - unless FileTest.exist?(file) - STDERR.puts "preparing #{file}" - - open(file, 'w'){|f| - ARGV[0] = n - $stdout = f - load script - $stdout = STDOUT - } - end - end - prepare_fasta_output(2_500_000) - script: | - # The Great Computer Language Shootout - # http://shootout.alioth.debian.org/ - # - # Contributed by Peter Bjarke Olsen - # Modified by Doug King - - seq=Array.new - - def revcomp(seq) - seq.reverse!.tr!('wsatugcyrkmbdhvnATUGCYRKMBDHVN','WSTAACGRYMKVHDBNTAACGRYMKVHDBN') - stringlen=seq.length - 0.step(stringlen-1,60) {|x| print seq.slice(x,60) , "\n"} - end - - input = open(File.join(File.dirname($0), 'fasta.output.2500000'), 'rb') - - while input.gets - if $_ =~ />/ - if seq.length != 0 - revcomp(seq.join) - seq=Array.new - end - puts $_ - else - $_.sub(/\n/,'') - seq.push $_ - end - end - revcomp(seq.join) - loop_count: 1 diff --git a/test/testdata/ruby_benchmark/so_sieve.rb b/test/testdata/ruby_benchmark/so_sieve.rb deleted file mode 100644 index 81ec9ffb29..0000000000 --- a/test/testdata/ruby_benchmark/so_sieve.rb +++ /dev/null @@ -1,27 +0,0 @@ -# frozen_string_literal: true -# typed: strict -# compiled: true -# from http://www.bagley.org/~doug/shootout/bench/sieve/sieve.ruby -num = 500 -count = i = j = 0 -flags0 = Array.new(8192,1) -k = 0 -while k < num - k += 1 - count = 0 - flags = flags0.dup - i = 2 - while i<8192 - i += 1 - if flags[i] - # remove all multiples of prime: i - j = i*i - while j < 8192 - j += i - flags[j] = nil - end - count += 1 - end - end -end -count diff --git a/test/testdata/ruby_benchmark/string_capitalize.yml b/test/testdata/ruby_benchmark/string_capitalize.yml deleted file mode 100644 index 7d23fd3d35..0000000000 --- a/test/testdata/ruby_benchmark/string_capitalize.yml +++ /dev/null @@ -1,10 +0,0 @@ -prelude: | - str1 = [*"a".."m",*"N".."Z",*"0".."9"].join("") - str10 = str1 * 10 - str100 = str10 * 10 - str1000 = str100 * 10 -benchmark: - capitalize-1: str1.capitalize - capitalize-10: str10.capitalize - capitalize-100: str100.capitalize - capitalize-1000: str1000.capitalize diff --git a/test/testdata/ruby_benchmark/string_downcase.yml b/test/testdata/ruby_benchmark/string_downcase.yml deleted file mode 100644 index a31c3ac712..0000000000 --- a/test/testdata/ruby_benchmark/string_downcase.yml +++ /dev/null @@ -1,10 +0,0 @@ -prelude: | - str1 = [*"A".."Z",*"0".."9"].join("") - str10 = str1 * 10 - str100 = str10 * 10 - str1000 = str100 * 10 -benchmark: - downcase-1: str1.upcase - downcase-10: str10.upcase - downcase-100: str100.upcase - downcase-1000: str1000.upcase diff --git a/test/testdata/ruby_benchmark/string_index.rb b/test/testdata/ruby_benchmark/string_index.rb deleted file mode 100644 index f40059f75f..0000000000 --- a/test/testdata/ruby_benchmark/string_index.rb +++ /dev/null @@ -1,6 +0,0 @@ -# frozen_string_literal: true -# typed: strong -# compiled: true -str1 = "あ" * 1024 + "い" # not single byte optimizable -str2 = "い" -100_000.times { str1.index(str2) } diff --git a/test/testdata/ruby_benchmark/string_scan_re.rb b/test/testdata/ruby_benchmark/string_scan_re.rb deleted file mode 100644 index 159a1a5aa5..0000000000 --- a/test/testdata/ruby_benchmark/string_scan_re.rb +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true -# typed: strong -# compiled: true -str = Array.new(1_000, 'abc').join(',') -1_000.times { str.scan(/abc/) } diff --git a/test/testdata/ruby_benchmark/string_scan_str.rb b/test/testdata/ruby_benchmark/string_scan_str.rb deleted file mode 100644 index ab9ee186aa..0000000000 --- a/test/testdata/ruby_benchmark/string_scan_str.rb +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true -# typed: strong -# compiled: true -str = Array.new(1_000, 'abc').join(',') -1_000.times { str.scan('abc') } diff --git a/test/testdata/ruby_benchmark/string_split.yml b/test/testdata/ruby_benchmark/string_split.yml deleted file mode 100644 index 84ffe8f6a7..0000000000 --- a/test/testdata/ruby_benchmark/string_split.yml +++ /dev/null @@ -1,7 +0,0 @@ -prelude: | - str0 = [*0..9].join("") -benchmark: - to_chars-1: str0.split('') - to_chars-10: (str0 * 10).split('') - to_chars-100: (str0 * 100).split('') - to_chars-1000: (str0 * 1000).split('') diff --git a/test/testdata/ruby_benchmark/string_swapcase.yml b/test/testdata/ruby_benchmark/string_swapcase.yml deleted file mode 100644 index afaae3f696..0000000000 --- a/test/testdata/ruby_benchmark/string_swapcase.yml +++ /dev/null @@ -1,10 +0,0 @@ -prelude: | - str1 = [*"A".."M",*"n".."z",*"0".."9"].join("") - str10 = str1 * 10 - str100 = str10 * 10 - str1000 = str100 * 10 -benchmark: - swapcase-1: str1.swapcase - swapcase-10: str10.swapcase - swapcase-100: str100.swapcase - swapcase-1000: str1000.swapcase diff --git a/test/testdata/ruby_benchmark/string_upcase.yml b/test/testdata/ruby_benchmark/string_upcase.yml deleted file mode 100644 index 456d213c74..0000000000 --- a/test/testdata/ruby_benchmark/string_upcase.yml +++ /dev/null @@ -1,10 +0,0 @@ -prelude: | - str1 = [*"a".."z",*"0".."9"].join("") - str10 = str1 * 10 - str100 = str10 * 10 - str1000 = str100 * 10 -benchmark: - upcase-1: str1.upcase - upcase-10: str10.upcase - upcase-100: str100.upcase - upcase-1000: str1000.upcase diff --git a/test/testdata/ruby_benchmark/stripe/0_times_do.rb b/test/testdata/ruby_benchmark/stripe/0_times_do.rb deleted file mode 100644 index 7dfacf057f..0000000000 --- a/test/testdata/ruby_benchmark/stripe/0_times_do.rb +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true -# typed: strict -# compiled: true - -i = 0 -while i < 10_000_000 - - 0.times do - end - - i += 1 -end - -puts i diff --git a/test/testdata/ruby_benchmark/stripe/1_times_do.rb b/test/testdata/ruby_benchmark/stripe/1_times_do.rb deleted file mode 100644 index 2aa5ef94f1..0000000000 --- a/test/testdata/ruby_benchmark/stripe/1_times_do.rb +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true -# typed: strict -# compiled: true - -i = 0 -while i < 10_000_000 - - 1.times do - end - - i += 1 -end - -puts i diff --git a/test/testdata/ruby_benchmark/stripe/array_all.rb b/test/testdata/ruby_benchmark/stripe/array_all.rb deleted file mode 100644 index d4a0228640..0000000000 --- a/test/testdata/ruby_benchmark/stripe/array_all.rb +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -arr = [1, 2, 3] -i = 0 -while i < 1_000_000 - arr.all? do |x| - x > 0 - end - i += 1 -end -puts i diff --git a/test/testdata/ruby_benchmark/stripe/array_any.rb b/test/testdata/ruby_benchmark/stripe/array_any.rb deleted file mode 100644 index ed78084240..0000000000 --- a/test/testdata/ruby_benchmark/stripe/array_any.rb +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -arr = [1, 2, 3] -i = 0 -while i < 1_000_000 - arr.any? do |x| - x > 3 - end - i += 1 -end -puts i diff --git a/test/testdata/ruby_benchmark/stripe/array_compact.rb b/test/testdata/ruby_benchmark/stripe/array_compact.rb deleted file mode 100644 index d3b4daa0fe..0000000000 --- a/test/testdata/ruby_benchmark/stripe/array_compact.rb +++ /dev/null @@ -1,14 +0,0 @@ -# typed: true -# frozen_string_literal: true -# compiled: true - -x = T.let((1..10).map{|a| [a,nil]}.flatten.to_a.freeze, T::Array[T.nilable(Integer)]) - -i = 0 -while i < 1_000_000 - i += 1 - - x.compact -end - -puts i diff --git a/test/testdata/ruby_benchmark/stripe/array_find.rb b/test/testdata/ruby_benchmark/stripe/array_find.rb deleted file mode 100644 index 2d4836efbd..0000000000 --- a/test/testdata/ruby_benchmark/stripe/array_find.rb +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -arr = [1, 2, 3] -i = 0 -while i < 1_000_000 - arr.find { |x| x == 3 } - i += 1 -end -puts i diff --git a/test/testdata/ruby_benchmark/stripe/array_reject_bang.rb b/test/testdata/ruby_benchmark/stripe/array_reject_bang.rb deleted file mode 100644 index 871d1ec58a..0000000000 --- a/test/testdata/ruby_benchmark/stripe/array_reject_bang.rb +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -arr = (1..10_000_000).to_a - -arr.reject! do |x| - x%2 == 0 -end - -p arr.length diff --git a/test/testdata/ruby_benchmark/stripe/array_reject_bang_baseline.rb b/test/testdata/ruby_benchmark/stripe/array_reject_bang_baseline.rb deleted file mode 100644 index ec044739e4..0000000000 --- a/test/testdata/ruby_benchmark/stripe/array_reject_bang_baseline.rb +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -arr = (1..10_000_000).to_a diff --git a/test/testdata/ruby_benchmark/stripe/array_select.rb b/test/testdata/ruby_benchmark/stripe/array_select.rb deleted file mode 100644 index 297a4c6490..0000000000 --- a/test/testdata/ruby_benchmark/stripe/array_select.rb +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -arr = [1, 2, 3] -i = 0 -while i < 1_000_000 - arr.filter { |x| x.even? } - i += 1 -end -puts i diff --git a/test/testdata/ruby_benchmark/stripe/array_to_h.rb b/test/testdata/ruby_benchmark/stripe/array_to_h.rb deleted file mode 100644 index 6b3e0fc29c..0000000000 --- a/test/testdata/ruby_benchmark/stripe/array_to_h.rb +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -xs = T.let((0..10).map{|x| [x,x]}.to_a, T::Array[[Integer, Integer]]) - -i = 0 -while i < 1_000_000 - i += 1 - - xs.to_h -end - -puts i diff --git a/test/testdata/ruby_benchmark/stripe/attr_is_null.rb b/test/testdata/ruby_benchmark/stripe/attr_is_null.rb deleted file mode 100644 index 65327d77e5..0000000000 --- a/test/testdata/ruby_benchmark/stripe/attr_is_null.rb +++ /dev/null @@ -1,49 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -# ::Boolean copied from extn/boolean.rb - -module ::Boolean - extend T::Helpers - sealed! -end - -class ::FalseClass - include ::Boolean -end - -class ::TrueClass - include ::Boolean -end - -# End code copied from extn/boolean.rb - -# Thing#has_some_attr? is analogous to Opus::APIErrors::Code#documented? -class Thing - extend T::Sig - - sig {returns(T.nilable(String))} - attr_reader :some_attr - - sig {params(some_attr: T.nilable(String)).void} - def initialize(some_attr:) - @some_attr = some_attr - end - - sig {returns(T::Boolean)} - def has_some_attr? - !@some_attr.nil? - end -end - -e = Thing.new(some_attr: "hello") - -i = 0 -while i < 10_000_000 - e.has_some_attr? - - i += 1 -end - -puts i diff --git a/test/testdata/ruby_benchmark/stripe/attr_reader/manual_def.rb b/test/testdata/ruby_benchmark/stripe/attr_reader/manual_def.rb deleted file mode 100644 index f0d8d28a58..0000000000 --- a/test/testdata/ruby_benchmark/stripe/attr_reader/manual_def.rb +++ /dev/null @@ -1,27 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -class AttrReaderManualDef - extend T::Sig - sig {params(foo: Integer).void} - def initialize(foo) - @foo = foo - end - - # manual method definition, behaves like attr_reader - def foo; @foo; end -end - -x = AttrReaderManualDef.new(1248) - -i = 0 -while i < 10_000_000 - - x.foo - - i += 1 -end - -puts i -puts x.foo diff --git a/test/testdata/ruby_benchmark/stripe/attr_reader/no_sig.llo.exp b/test/testdata/ruby_benchmark/stripe/attr_reader/no_sig.llo.exp deleted file mode 100644 index de2156accb..0000000000 --- a/test/testdata/ruby_benchmark/stripe/attr_reader/no_sig.llo.exp +++ /dev/null @@ -1,934 +0,0 @@ -; ModuleID = 'payload' -source_filename = "llvm-link" -target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } -%struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.__pthread_cond_s } -%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } -%union.anon = type { i64 } -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } -%struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } -%struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } -%struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } -%struct.anon.0 = type { i16, [2 x i8] } -%struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } -%struct.rb_iseq_location_struct = type { i64, i64, i64, i64, i32, %struct.rb_code_location_struct } -%struct.rb_code_location_struct = type { %struct.rb_code_position_struct, %struct.rb_code_position_struct } -%struct.rb_code_position_struct = type { i32, i32 } -%struct.iseq_insn_info = type { %struct.iseq_insn_info_entry*, i32*, i32, %struct.succ_index_table* } -%struct.iseq_insn_info_entry = type opaque -%struct.succ_index_table = type opaque -%struct.iseq_catch_table = type opaque -%union.iseq_inline_storage_entry = type { %struct.iseq_inline_cache_entry } -%struct.iseq_inline_cache_entry = type { i64, %struct.rb_cref_struct*, i64 } -%struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } -%struct.rb_scope_visi_struct = type { i8, [3 x i8] } -%struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } -%struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } -%union.anon.13 = type { %struct.rb_method_cfunc_struct } -%struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%union.anon.15 = type { i32 } -%struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.16 = type { i64, i64, i64, i64* } -%union.anon.17 = type { %struct.anon.18 } -%struct.anon.18 = type { i64, i32 } -%struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } -%struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } -%struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } -%struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } -%struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } -%struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } -%struct.__sigset_t = type { [16 x i64] } -%struct.rb_calling_info = type { i64, i64, i32, i32 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } -%union.anon.9 = type { %union.pthread_cond_t } -%struct.rb_unblock_callback = type { void (i8*)*, i8* } -%struct.rb_mutex_struct = type opaque -%struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.10 = type { %struct.anon.11 } -%struct.anon.11 = type { i64, i64, i32 } -%struct.rb_fiber_struct = type opaque -%struct.anon.5 = type { [65 x i64] } -%struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } -%struct.rb_event_hook_struct = type opaque -%struct.rb_postponed_job_struct = type opaque -%struct.list_head = type { %struct.list_node } -%union.pthread_mutex_t = type { %struct.__pthread_mutex_s } -%struct.__pthread_mutex_s = type { i32, i32, i32, i32, i32, i16, i16, %struct.__pthread_internal_list } -%struct.__pthread_internal_list = type { %struct.__pthread_internal_list*, %struct.__pthread_internal_list* } -%struct.rb_objspace = type opaque -%struct.rb_at_exit_list = type { void (%struct.rb_vm_struct*)*, %struct.rb_at_exit_list* } -%struct.st_table = type { i8, i8, i8, i32, %struct.st_hash_type*, i64, i64*, i64, i64, %struct.st_table_entry* } -%struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } -%struct.st_table_entry = type opaque -%struct.rb_builtin_function = type opaque -%struct.anon.6 = type { i64, i64, i64, i64 } -%struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } -%struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } -%struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } -%struct.rb_call_info_with_kwarg = type { %struct.rb_call_info, %struct.rb_call_info_kw_arg* } -%struct.rb_call_info_kw_arg = type { i32, [1 x i64] } -%struct.iseq_inline_iv_cache_entry = type { i64, i64 } - -@ruby_current_vm_ptr = external local_unnamed_addr global %struct.rb_vm_struct*, align 8 -@ruby_current_execution_context_ptr = external local_unnamed_addr global %struct.rb_execution_context_struct*, align 8 -@sorbet_getVoidSingleton.name = internal constant [30 x i8] c"T::Private::Types::Void::VOID\00", align 16 -@ruby_vm_global_constant_state = external local_unnamed_addr global i64, align 8 -@.str = private unnamed_addr constant [6 x i8] c"@%li\0B\00", align 1 -@rb_eRuntimeError = external local_unnamed_addr global i64, align 8 -@.str.9 = private unnamed_addr constant [95 x i8] c"sorbet_getBuildSCMRevision: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@.str.10 = private unnamed_addr constant [93 x i8] c"sorbet_getIsReleaseBuild: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@"stackFramePrecomputed_func_.17$152" = unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"rubyIdPrecomputed_" = internal unnamed_addr global i64 0, align 8 -@"str_" = private unnamed_addr constant [17 x i8] c"\00", align 1 -@"rubyStrFrozen_" = internal unnamed_addr global i64 0, align 8 -@"rubyStrFrozen_test/testdata/ruby_benchmark/stripe/attr_reader/no_sig.rb" = internal unnamed_addr global i64 0, align 8 -@"str_test/testdata/ruby_benchmark/stripe/attr_reader/no_sig.rb" = private unnamed_addr constant [58 x i8] c"test/testdata/ruby_benchmark/stripe/attr_reader/no_sig.rb\00", align 1 -@iseqEncodedArray = internal global [28 x i64] zeroinitializer -@fileLineNumberInfo = internal global %struct.SorbetLineNumberInfo zeroinitializer -@str_AttrReaderNoSig = private unnamed_addr constant [16 x i8] c"AttrReaderNoSig\00", align 1 -@ic_new = internal global %struct.FunctionInlineCache zeroinitializer -@rubyIdPrecomputed_new = internal unnamed_addr global i64 0, align 8 -@str_new = private unnamed_addr constant [4 x i8] c"new\00", align 1 -@"rubyIdPrecomputed_<" = internal unnamed_addr global i64 0, align 8 -@"str_<" = private unnamed_addr constant [2 x i8] c"<\00", align 1 -@"ic_<" = internal global %struct.FunctionInlineCache zeroinitializer -@ic_foo = internal global %struct.FunctionInlineCache zeroinitializer -@rubyIdPrecomputed_foo = internal unnamed_addr global i64 0, align 8 -@str_foo = private unnamed_addr constant [4 x i8] c"foo\00", align 1 -@"rubyIdPrecomputed_+" = internal unnamed_addr global i64 0, align 8 -@"str_+" = private unnamed_addr constant [2 x i8] c"+\00", align 1 -@"ic_+" = internal global %struct.FunctionInlineCache zeroinitializer -@ic_puts = internal global %struct.FunctionInlineCache zeroinitializer -@rubyIdPrecomputed_puts = internal unnamed_addr global i64 0, align 8 -@str_puts = private unnamed_addr constant [5 x i8] c"puts\00", align 1 -@ic_foo.1 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_puts.2 = internal global %struct.FunctionInlineCache zeroinitializer -@"stackFramePrecomputed_func_AttrReaderNoSig#10initialize" = unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@rubyIdPrecomputed_initialize = internal unnamed_addr global i64 0, align 8 -@str_initialize = private unnamed_addr constant [11 x i8] c"initialize\00", align 1 -@str_sig = private unnamed_addr constant [4 x i8] c"sig\00", align 1 -@str_Integer = private unnamed_addr constant [8 x i8] c"Integer\00", align 1 -@"ivc_@foo" = internal global %struct.iseq_inline_iv_cache_entry zeroinitializer -@"rubyIdPrecomputed_@foo" = internal unnamed_addr global i64 0, align 8 -@"str_@foo" = private unnamed_addr constant [5 x i8] c"@foo\00", align 1 -@"ivc_@foo.3" = internal global %struct.iseq_inline_iv_cache_entry zeroinitializer -@"" = internal unnamed_addr global i64 0 -@"stackFramePrecomputed_func_AttrReaderNoSig.13" = unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"rubyIdPrecomputed_" = internal unnamed_addr global i64 0, align 8 -@"str_" = private unnamed_addr constant [24 x i8] c"\00", align 1 -@"stackFramePrecomputed_func_AttrReaderNoSig.13$block_1" = unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"rubyIdPrecomputed_block in " = internal unnamed_addr global i64 0, align 8 -@"str_block in " = private unnamed_addr constant [33 x i8] c"block in \00", align 1 -@ic_params = internal global %struct.FunctionInlineCache zeroinitializer -@rubyIdPrecomputed_params = internal unnamed_addr global i64 0, align 8 -@str_params = private unnamed_addr constant [7 x i8] c"params\00", align 1 -@ic_void = internal global %struct.FunctionInlineCache zeroinitializer -@rubyIdPrecomputed_void = internal unnamed_addr global i64 0, align 8 -@str_void = private unnamed_addr constant [5 x i8] c"void\00", align 1 -@"str_T::Sig" = private unnamed_addr constant [7 x i8] c"T::Sig\00", align 1 -@ic_extend = internal global %struct.FunctionInlineCache zeroinitializer -@rubyIdPrecomputed_extend = internal unnamed_addr global i64 0, align 8 -@str_extend = private unnamed_addr constant [7 x i8] c"extend\00", align 1 -@str_normal = private unnamed_addr constant [7 x i8] c"normal\00", align 1 -@str_attr_reader = private unnamed_addr constant [12 x i8] c"attr_reader\00", align 1 -@"guard_epoch_T::Sig" = linkonce local_unnamed_addr global i64 0 -@"guarded_const_T::Sig" = linkonce local_unnamed_addr global i64 0 -@guard_epoch_AttrReaderNoSig = linkonce local_unnamed_addr global i64 0 -@guarded_const_AttrReaderNoSig = linkonce local_unnamed_addr global i64 0 -@rb_cObject = external local_unnamed_addr constant i64 -@rb_cInteger = external local_unnamed_addr constant i64 - -; Function Attrs: nounwind readnone willreturn -declare i64 @rb_id2sym(i64) local_unnamed_addr #0 - -; Function Attrs: cold noreturn -declare void @sorbet_cast_failure(i64, i8*, i8*) local_unnamed_addr #1 - -; Function Attrs: noreturn -declare void @sorbet_raiseArity(i32, i32, i32) local_unnamed_addr #2 - -declare %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64, i64, i64, i64, %struct.rb_iseq_struct*, i32, i32, %struct.SorbetLineNumberInfo*, i64*, i32, i32) local_unnamed_addr #3 - -declare void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo*, i64*, i32) local_unnamed_addr #3 - -declare i64 @sorbet_getConstant(i8*, i64) local_unnamed_addr #3 - -declare i64 @sorbet_readRealpath() local_unnamed_addr #3 - -declare %struct.rb_control_frame_struct* @sorbet_pushStaticInitFrame(i64) local_unnamed_addr #3 - -declare void @sorbet_popFrame() local_unnamed_addr #3 - -declare void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache*, i64, i32, i32, i32, i64*) local_unnamed_addr #3 - -declare i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache*, i64) local_unnamed_addr #3 - -declare void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_iseq_struct*) local_unnamed_addr #3 - -declare i64 @sorbet_rb_int_plus_slowpath(i64, i64) local_unnamed_addr #3 - -declare i64 @sorbet_rb_int_lt_slowpath(i64, i64) local_unnamed_addr #3 - -declare i64 @sorbet_vm_getivar(i64, i64, %struct.iseq_inline_iv_cache_entry*) local_unnamed_addr #3 - -declare void @sorbet_vm_setivar(i64, i64, i64, %struct.iseq_inline_iv_cache_entry*) local_unnamed_addr #3 - -declare void @sorbet_vm_register_sig(i64, i64, i64, i64, i64 (i64, i64, i32, i64*, i64)*) local_unnamed_addr #3 - -declare void @sorbet_vm_define_method(i64, i8*, i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*)*, i8*, %struct.rb_iseq_struct*, i1 zeroext) local_unnamed_addr #3 - -declare i64 @sorbet_vm_fstring_new(i8*, i64) local_unnamed_addr #3 - -declare i64 @rb_define_class(i8*, i64) local_unnamed_addr #3 - -declare i64 @rb_intern(i8*) local_unnamed_addr #3 - -declare i64 @rb_id2str(i64) local_unnamed_addr #3 - -declare i64 @rb_sprintf(i8*, ...) local_unnamed_addr #3 - -declare i64 @rb_intern_str(i64) local_unnamed_addr #3 - -declare void @rb_add_method(i64, i64, i32, i8*, i32) local_unnamed_addr #3 - -declare i64 @rb_intern2(i8*, i64) local_unnamed_addr #3 - -declare void @rb_gc_register_mark_object(i64) local_unnamed_addr #3 - -; Function Attrs: noreturn -declare void @rb_raise(i64, i8*, ...) local_unnamed_addr #2 - -declare i64 @rb_int2big(i64) local_unnamed_addr #3 - -; Function Attrs: nofree nosync nounwind readnone speculatable willreturn -declare { i64, i1 } @llvm.sadd.with.overflow.i64(i64, i64) #4 - -declare i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct*, i32) local_unnamed_addr #3 - -; Function Attrs: allocsize(0,1) -declare noalias nonnull i8* @ruby_xcalloc(i64, i64) local_unnamed_addr #5 - -; Function Attrs: allocsize(0,1) -declare noalias nonnull i8* @ruby_xmalloc2(i64, i64) local_unnamed_addr #5 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) #6 - -; Function Attrs: nounwind ssp uwtable -define weak i32 @sorbet_getIsReleaseBuild() local_unnamed_addr #7 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([93 x i8], [93 x i8]* @.str.10, i64 0, i64 0)) #15 - unreachable -} - -; Function Attrs: nounwind ssp uwtable -define weak i8* @sorbet_getBuildSCMRevision() local_unnamed_addr #7 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([95 x i8], [95 x i8]* @.str.9, i64 0, i64 0)) #15 - unreachable -} - -; Function Attrs: nounwind sspreq uwtable -define internal fastcc void @"func_AttrReaderNoSig.13L62"(i64 %selfRaw, %struct.rb_control_frame_struct* %cfp) unnamed_addr #8 !dbg !10 { -functionEntryInitializers: - %stackFrame = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_AttrReaderNoSig.13", align 8 - %0 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !14 - %1 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %0, i64 0, i32 2 - %2 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %1, align 8, !tbaa !16 - %3 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame, %struct.rb_iseq_struct** %3, align 8, !tbaa !20 - %4 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 4 - %5 = load i64*, i64** %4, align 8, !tbaa !22 - %6 = load i64, i64* %5, align 8, !tbaa !6 - %7 = and i64 %6, -33 - store i64 %7, i64* %5, align 8, !tbaa !6 - tail call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %0, %struct.rb_control_frame_struct* %2, %struct.rb_iseq_struct* %stackFrame) #16 - %8 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 0 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 7), i64** %8, align 8, !dbg !23, !tbaa !14 - %rubyId_initialize = load i64, i64* @rubyIdPrecomputed_initialize, align 8, !dbg !24 - %rawSym = tail call i64 @rb_id2sym(i64 %rubyId_initialize), !dbg !24 - tail call void @sorbet_vm_register_sig(i64 noundef 0, i64 %rawSym, i64 %selfRaw, i64 noundef 8, i64 (i64, i64, i32, i64*, i64)* noundef @"func_AttrReaderNoSig.13L62$block_1"), !dbg !24 - %9 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !24, !tbaa !14 - %10 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %9, i64 0, i32 5, !dbg !24 - %11 = load i32, i32* %10, align 8, !dbg !24, !tbaa !25 - %12 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %9, i64 0, i32 6, !dbg !24 - %13 = load i32, i32* %12, align 4, !dbg !24, !tbaa !26 - %14 = xor i32 %13, -1, !dbg !24 - %15 = and i32 %14, %11, !dbg !24 - %16 = icmp eq i32 %15, 0, !dbg !24 - br i1 %16, label %rb_vm_check_ints.exit2, label %17, !dbg !24, !prof !27 - -17: ; preds = %functionEntryInitializers - %18 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %9, i64 0, i32 8, !dbg !24 - %19 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %18, align 8, !dbg !24, !tbaa !28 - %20 = tail call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %19, i32 noundef 0) #16, !dbg !24 - br label %rb_vm_check_ints.exit2, !dbg !24 - -rb_vm_check_ints.exit2: ; preds = %functionEntryInitializers, %17 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 6), i64** %8, align 8, !dbg !24, !tbaa !14 - %21 = load i64, i64* @"guard_epoch_T::Sig", align 8, !dbg !29 - %22 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !29, !tbaa !30 - %needTakeSlowPath = icmp ne i64 %21, %22, !dbg !29 - br i1 %needTakeSlowPath, label %23, label %24, !dbg !29, !prof !32 - -23: ; preds = %rb_vm_check_ints.exit2 - tail call void @"const_recompute_T::Sig"(), !dbg !29 - br label %24, !dbg !29 - -24: ; preds = %rb_vm_check_ints.exit2, %23 - %25 = load i64, i64* @"guarded_const_T::Sig", align 8, !dbg !29 - %26 = load i64, i64* @"guard_epoch_T::Sig", align 8, !dbg !29 - %27 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !29, !tbaa !30 - %guardUpdated = icmp eq i64 %26, %27, !dbg !29 - tail call void @llvm.assume(i1 %guardUpdated), !dbg !29 - %28 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 1, !dbg !29 - %29 = load i64*, i64** %28, align 8, !dbg !29 - store i64 %selfRaw, i64* %29, align 8, !dbg !29, !tbaa !6 - %30 = getelementptr inbounds i64, i64* %29, i64 1, !dbg !29 - store i64 %25, i64* %30, align 8, !dbg !29, !tbaa !6 - %31 = getelementptr inbounds i64, i64* %30, i64 1, !dbg !29 - store i64* %31, i64** %28, align 8, !dbg !29 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_extend, i64 0), !dbg !29 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 8), i64** %8, align 8, !dbg !29, !tbaa !14 - %32 = load i64, i64* @guard_epoch_AttrReaderNoSig, align 8, !dbg !33 - %33 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !33, !tbaa !30 - %needTakeSlowPath3 = icmp ne i64 %32, %33, !dbg !33 - br i1 %needTakeSlowPath3, label %34, label %35, !dbg !33, !prof !32 - -34: ; preds = %24 - tail call void @const_recompute_AttrReaderNoSig(), !dbg !33 - br label %35, !dbg !33 - -35: ; preds = %24, %34 - %36 = load i64, i64* @guarded_const_AttrReaderNoSig, align 8, !dbg !33 - %37 = load i64, i64* @guard_epoch_AttrReaderNoSig, align 8, !dbg !33 - %38 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !33, !tbaa !30 - %guardUpdated4 = icmp eq i64 %37, %38, !dbg !33 - tail call void @llvm.assume(i1 %guardUpdated4), !dbg !33 - %stackFrame51 = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_AttrReaderNoSig#10initialize", align 8, !dbg !33 - %39 = tail call noalias nonnull i8* @ruby_xcalloc(i64 noundef 1, i64 noundef 64) #17, !dbg !33 - %40 = bitcast i8* %39 to i16*, !dbg !33 - %41 = load i16, i16* %40, align 8, !dbg !33 - %42 = and i16 %41, -384, !dbg !33 - %43 = or i16 %42, 1, !dbg !33 - store i16 %43, i16* %40, align 8, !dbg !33 - %44 = getelementptr inbounds i8, i8* %39, i64 8, !dbg !33 - %45 = bitcast i8* %44 to i32*, !dbg !33 - store i32 1, i32* %45, align 8, !dbg !33, !tbaa !34 - %46 = getelementptr inbounds i8, i8* %39, i64 12, !dbg !33 - %47 = bitcast i8* %46 to i32*, !dbg !33 - %48 = getelementptr inbounds i8, i8* %39, i64 4, !dbg !33 - %49 = bitcast i8* %48 to i32*, !dbg !33 - tail call void @llvm.memset.p0i8.i64(i8* nonnull align 4 %46, i8 0, i64 20, i1 false), !dbg !33 - store i32 1, i32* %49, align 4, !dbg !33, !tbaa !37 - %positional_table = alloca i64, align 8, !dbg !33 - %rubyId_foo = load i64, i64* @rubyIdPrecomputed_foo, align 8, !dbg !33 - store i64 %rubyId_foo, i64* %positional_table, align 8, !dbg !33 - %50 = tail call noalias nonnull i8* @ruby_xmalloc2(i64 noundef 1, i64 noundef 8) #17, !dbg !33 - %51 = bitcast i64* %positional_table to i8*, !dbg !33 - call void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture nonnull writeonly align 1 %50, i8* nocapture noundef nonnull readonly align 8 dereferenceable(8) %51, i64 noundef 8, i1 noundef false) #16, !dbg !33 - %52 = getelementptr inbounds i8, i8* %39, i64 32, !dbg !33 - %53 = bitcast i8* %52 to i8**, !dbg !33 - store i8* %50, i8** %53, align 8, !dbg !33, !tbaa !38 - tail call void @sorbet_vm_define_method(i64 %36, i8* noundef getelementptr inbounds ([11 x i8], [11 x i8]* @str_initialize, i64 0, i64 0), i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*)* noundef @"func_AttrReaderNoSig#10initialize", i8* nonnull %39, %struct.rb_iseq_struct* %stackFrame51, i1 noundef zeroext false) #16, !dbg !33 - %54 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !33, !tbaa !14 - %55 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %54, i64 0, i32 5, !dbg !33 - %56 = load i32, i32* %55, align 8, !dbg !33, !tbaa !25 - %57 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %54, i64 0, i32 6, !dbg !33 - %58 = load i32, i32* %57, align 4, !dbg !33, !tbaa !26 - %59 = xor i32 %58, -1, !dbg !33 - %60 = and i32 %59, %56, !dbg !33 - %61 = icmp eq i32 %60, 0, !dbg !33 - br i1 %61, label %rb_vm_check_ints.exit1, label %62, !dbg !33, !prof !27 - -62: ; preds = %35 - %63 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %54, i64 0, i32 8, !dbg !33 - %64 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %63, align 8, !dbg !33, !tbaa !28 - %65 = tail call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %64, i32 noundef 0) #16, !dbg !33 - br label %rb_vm_check_ints.exit1, !dbg !33 - -rb_vm_check_ints.exit1: ; preds = %35, %62 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 13), i64** %8, align 8, !dbg !33, !tbaa !14 - %66 = tail call i64 @rb_intern(i8* noundef getelementptr inbounds ([4 x i8], [4 x i8]* @str_foo, i64 0, i64 0)) #16, !dbg !39 - %67 = tail call i64 @rb_id2str(i64 %66) #16, !dbg !39 - %68 = tail call i64 (i8*, ...) @rb_sprintf(i8* noundef getelementptr inbounds ([6 x i8], [6 x i8]* @.str, i64 0, i64 0), i64 %67) #16, !dbg !39 - %69 = tail call i64 @rb_intern_str(i64 %68) #16, !dbg !39 - %70 = inttoptr i64 %69 to i8*, !dbg !39 - tail call void @rb_add_method(i64 %36, i64 %66, i32 noundef 4, i8* %70, i32 noundef 1) #16, !dbg !39 - %71 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !39, !tbaa !14 - %72 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %71, i64 0, i32 5, !dbg !39 - %73 = load i32, i32* %72, align 8, !dbg !39, !tbaa !25 - %74 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %71, i64 0, i32 6, !dbg !39 - %75 = load i32, i32* %74, align 4, !dbg !39, !tbaa !26 - %76 = xor i32 %75, -1, !dbg !39 - %77 = and i32 %76, %73, !dbg !39 - %78 = icmp eq i32 %77, 0, !dbg !39 - br i1 %78, label %rb_vm_check_ints.exit, label %79, !dbg !39, !prof !27 - -79: ; preds = %rb_vm_check_ints.exit1 - %80 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %71, i64 0, i32 8, !dbg !39 - %81 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %80, align 8, !dbg !39, !tbaa !28 - %82 = tail call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %81, i32 noundef 0) #16, !dbg !39 - br label %rb_vm_check_ints.exit, !dbg !39 - -rb_vm_check_ints.exit: ; preds = %rb_vm_check_ints.exit1, %79 - ret void -} - -; Function Attrs: sspreq -define void @Init_no_sig() local_unnamed_addr #9 { -entry: - %locals.i18.i = alloca i64, i32 0, align 8 - %locals.i16.i = alloca i64, i32 0, align 8 - %locals.i.i = alloca i64, i32 0, align 8 - %keywords.i = alloca i64, align 8, !dbg !40 - %realpath = tail call i64 @sorbet_readRealpath() - %0 = bitcast i64* %keywords.i to i8* - call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %0) - %1 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([17 x i8], [17 x i8]* @"str_", i64 0, i64 0), i64 noundef 16) #16 - store i64 %1, i64* @"rubyIdPrecomputed_", align 8 - %2 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([4 x i8], [4 x i8]* @str_new, i64 0, i64 0), i64 noundef 3) #16 - store i64 %2, i64* @rubyIdPrecomputed_new, align 8 - %3 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([2 x i8], [2 x i8]* @"str_<", i64 0, i64 0), i64 noundef 1) #16 - store i64 %3, i64* @"rubyIdPrecomputed_<", align 8 - %4 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([4 x i8], [4 x i8]* @str_foo, i64 0, i64 0), i64 noundef 3) #16 - store i64 %4, i64* @rubyIdPrecomputed_foo, align 8 - %5 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([2 x i8], [2 x i8]* @"str_+", i64 0, i64 0), i64 noundef 1) #16 - store i64 %5, i64* @"rubyIdPrecomputed_+", align 8 - %6 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([5 x i8], [5 x i8]* @str_puts, i64 0, i64 0), i64 noundef 4) #16 - store i64 %6, i64* @rubyIdPrecomputed_puts, align 8 - %7 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([11 x i8], [11 x i8]* @str_initialize, i64 0, i64 0), i64 noundef 10) #16 - store i64 %7, i64* @rubyIdPrecomputed_initialize, align 8 - %8 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([5 x i8], [5 x i8]* @"str_@foo", i64 0, i64 0), i64 noundef 4) #16 - store i64 %8, i64* @"rubyIdPrecomputed_@foo", align 8 - %9 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([24 x i8], [24 x i8]* @"str_", i64 0, i64 0), i64 noundef 23) #16 - store i64 %9, i64* @"rubyIdPrecomputed_", align 8 - %10 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([33 x i8], [33 x i8]* @"str_block in ", i64 0, i64 0), i64 noundef 32) #16 - store i64 %10, i64* @"rubyIdPrecomputed_block in ", align 8 - %11 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([7 x i8], [7 x i8]* @str_params, i64 0, i64 0), i64 noundef 6) #16 - store i64 %11, i64* @rubyIdPrecomputed_params, align 8 - %12 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([5 x i8], [5 x i8]* @str_void, i64 0, i64 0), i64 noundef 4) #16 - store i64 %12, i64* @rubyIdPrecomputed_void, align 8 - %13 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([7 x i8], [7 x i8]* @str_extend, i64 0, i64 0), i64 noundef 6) #16 - store i64 %13, i64* @rubyIdPrecomputed_extend, align 8 - %14 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([7 x i8], [7 x i8]* @str_normal, i64 0, i64 0), i64 noundef 6) #16 - %15 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([12 x i8], [12 x i8]* @str_attr_reader, i64 0, i64 0), i64 noundef 11) #16 - %16 = tail call i64 @sorbet_vm_fstring_new(i8* noundef getelementptr inbounds ([17 x i8], [17 x i8]* @"str_", i64 0, i64 0), i64 noundef 16) #16 - tail call void @rb_gc_register_mark_object(i64 %16) #16 - store i64 %16, i64* @"rubyStrFrozen_", align 8 - %17 = tail call i64 @sorbet_vm_fstring_new(i8* noundef getelementptr inbounds ([58 x i8], [58 x i8]* @"str_test/testdata/ruby_benchmark/stripe/attr_reader/no_sig.rb", i64 0, i64 0), i64 noundef 57) #16 - tail call void @rb_gc_register_mark_object(i64 %17) #16 - store i64 %17, i64* @"rubyStrFrozen_test/testdata/ruby_benchmark/stripe/attr_reader/no_sig.rb", align 8 - tail call void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i32 0, i32 0), i32 noundef 28) - %"rubyId_.i.i" = load i64, i64* @"rubyIdPrecomputed_", align 8 - %"rubyStr_.i.i" = load i64, i64* @"rubyStrFrozen_", align 8 - %"rubyStr_test/testdata/ruby_benchmark/stripe/attr_reader/no_sig.rb.i.i" = load i64, i64* @"rubyStrFrozen_test/testdata/ruby_benchmark/stripe/attr_reader/no_sig.rb", align 8 - %18 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_.i.i", i64 %"rubyId_.i.i", i64 %"rubyStr_test/testdata/ruby_benchmark/stripe/attr_reader/no_sig.rb.i.i", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 0, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals.i.i, i32 noundef 0, i32 noundef 2) - store %struct.rb_iseq_struct* %18, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.17$152", align 8 - %rubyId_new.i = load i64, i64* @rubyIdPrecomputed_new, align 8, !dbg !42 - call void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_new, i64 %rubyId_new.i, i32 noundef 16, i32 noundef 1, i32 noundef 0, i64* noundef null), !dbg !42 - %"rubyId_<.i" = load i64, i64* @"rubyIdPrecomputed_<", align 8, !dbg !44 - call void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @"ic_<", i64 %"rubyId_<.i", i32 noundef 16, i32 noundef 1, i32 noundef 0, i64* noundef null), !dbg !44 - %rubyId_foo.i = load i64, i64* @rubyIdPrecomputed_foo, align 8, !dbg !45 - call void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_foo, i64 %rubyId_foo.i, i32 noundef 16, i32 noundef 0, i32 noundef 0, i64* noundef null), !dbg !45 - %"rubyId_+.i" = load i64, i64* @"rubyIdPrecomputed_+", align 8, !dbg !46 - call void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @"ic_+", i64 %"rubyId_+.i", i32 noundef 16, i32 noundef 1, i32 noundef 0, i64* noundef null), !dbg !46 - %rubyId_puts.i = load i64, i64* @rubyIdPrecomputed_puts, align 8, !dbg !47 - call void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts, i64 %rubyId_puts.i, i32 noundef 20, i32 noundef 1, i32 noundef 0, i64* noundef null), !dbg !47 - %rubyId_foo5.i = load i64, i64* @rubyIdPrecomputed_foo, align 8, !dbg !48 - call void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_foo.1, i64 %rubyId_foo5.i, i32 noundef 16, i32 noundef 0, i32 noundef 0, i64* noundef null), !dbg !48 - %rubyId_puts7.i = load i64, i64* @rubyIdPrecomputed_puts, align 8, !dbg !49 - call void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts.2, i64 %rubyId_puts7.i, i32 noundef 20, i32 noundef 1, i32 noundef 0, i64* noundef null), !dbg !49 - %19 = call i64 @sorbet_vm_fstring_new(i8* noundef getelementptr inbounds ([11 x i8], [11 x i8]* @str_initialize, i64 0, i64 0), i64 noundef 10) #16 - call void @rb_gc_register_mark_object(i64 %19) #16 - %rubyId_initialize.i.i = load i64, i64* @rubyIdPrecomputed_initialize, align 8 - %"rubyStr_test/testdata/ruby_benchmark/stripe/attr_reader/no_sig.rb.i15.i" = load i64, i64* @"rubyStrFrozen_test/testdata/ruby_benchmark/stripe/attr_reader/no_sig.rb", align 8 - %20 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %19, i64 %rubyId_initialize.i.i, i64 %"rubyStr_test/testdata/ruby_benchmark/stripe/attr_reader/no_sig.rb.i15.i", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 1, i32 noundef 8, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals.i16.i, i32 noundef 0, i32 noundef 0) - store %struct.rb_iseq_struct* %20, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_AttrReaderNoSig#10initialize", align 8 - %21 = call i64 @sorbet_getConstant(i8* noundef getelementptr inbounds ([30 x i8], [30 x i8]* @sorbet_getVoidSingleton.name, i64 0, i64 0), i64 noundef 30) #16 - store i64 %21, i64* @"", align 8 - %22 = call i64 @sorbet_vm_fstring_new(i8* noundef getelementptr inbounds ([24 x i8], [24 x i8]* @"str_", i64 0, i64 0), i64 noundef 23) #16 - call void @rb_gc_register_mark_object(i64 %22) #16 - %"rubyId_.i.i" = load i64, i64* @"rubyIdPrecomputed_", align 8 - %"rubyStr_test/testdata/ruby_benchmark/stripe/attr_reader/no_sig.rb.i17.i" = load i64, i64* @"rubyStrFrozen_test/testdata/ruby_benchmark/stripe/attr_reader/no_sig.rb", align 8 - %23 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %22, i64 %"rubyId_.i.i", i64 %"rubyStr_test/testdata/ruby_benchmark/stripe/attr_reader/no_sig.rb.i17.i", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 3, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals.i18.i, i32 noundef 0, i32 noundef 4) - store %struct.rb_iseq_struct* %23, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_AttrReaderNoSig.13", align 8 - %24 = call i64 @sorbet_vm_fstring_new(i8* noundef getelementptr inbounds ([33 x i8], [33 x i8]* @"str_block in ", i64 0, i64 0), i64 noundef 32) #16 - call void @rb_gc_register_mark_object(i64 %24) #16 - %stackFrame.i.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_AttrReaderNoSig.13", align 8 - %"rubyId_block in .i.i" = load i64, i64* @"rubyIdPrecomputed_block in ", align 8 - %"rubyStr_test/testdata/ruby_benchmark/stripe/attr_reader/no_sig.rb.i19.i" = load i64, i64* @"rubyStrFrozen_test/testdata/ruby_benchmark/stripe/attr_reader/no_sig.rb", align 8 - %25 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %24, i64 %"rubyId_block in .i.i", i64 %"rubyStr_test/testdata/ruby_benchmark/stripe/attr_reader/no_sig.rb.i19.i", i64 %realpath, %struct.rb_iseq_struct* %stackFrame.i.i, i32 noundef 2, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef null, i32 noundef 0, i32 noundef 4) - store %struct.rb_iseq_struct* %25, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_AttrReaderNoSig.13$block_1", align 8 - %rubyId_params.i = load i64, i64* @rubyIdPrecomputed_params, align 8, !dbg !40 - %rubyId_foo10.i = load i64, i64* @rubyIdPrecomputed_foo, align 8, !dbg !40 - %26 = call i64 @rb_id2sym(i64 %rubyId_foo10.i) #18, !dbg !40 - store i64 %26, i64* %keywords.i, align 8, !dbg !40 - call void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_params, i64 %rubyId_params.i, i32 noundef 68, i32 noundef 1, i32 noundef 1, i64* noundef nonnull align 8 %keywords.i), !dbg !40 - %rubyId_void.i = load i64, i64* @rubyIdPrecomputed_void, align 8, !dbg !40 - call void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_void, i64 %rubyId_void.i, i32 noundef 16, i32 noundef 0, i32 noundef 0, i64* noundef null), !dbg !40 - %rubyId_extend.i = load i64, i64* @rubyIdPrecomputed_extend, align 8, !dbg !29 - call void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_extend, i64 %rubyId_extend.i, i32 noundef 20, i32 noundef 1, i32 noundef 0, i64* noundef null), !dbg !29 - call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %0) - %27 = load %struct.rb_vm_struct*, %struct.rb_vm_struct** @ruby_current_vm_ptr, align 8, !tbaa !14 - %28 = getelementptr inbounds %struct.rb_vm_struct, %struct.rb_vm_struct* %27, i64 0, i32 18 - %29 = load i64, i64* %28, align 8, !tbaa !50 - %30 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !14 - %31 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %30, i64 0, i32 2 - %32 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %31, align 8, !tbaa !16 - %stackFrame.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.17$152", align 8 - %33 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %32, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame.i, %struct.rb_iseq_struct** %33, align 8, !tbaa !20 - %34 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %32, i64 0, i32 4 - %35 = load i64*, i64** %34, align 8, !tbaa !22 - %36 = load i64, i64* %35, align 8, !tbaa !6 - %37 = and i64 %36, -33 - store i64 %37, i64* %35, align 8, !tbaa !6 - call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %30, %struct.rb_control_frame_struct* %32, %struct.rb_iseq_struct* %stackFrame.i) #16 - %38 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %32, i64 0, i32 0 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %38, align 8, !dbg !58, !tbaa !14 - %39 = load i64, i64* @rb_cObject, align 8, !dbg !59 - %40 = call i64 @rb_define_class(i8* noundef getelementptr inbounds ([16 x i8], [16 x i8]* @str_AttrReaderNoSig, i64 0, i64 0), i64 %39) #16, !dbg !59 - %41 = call %struct.rb_control_frame_struct* @sorbet_pushStaticInitFrame(i64 %40) #16, !dbg !59 - call fastcc void @"func_AttrReaderNoSig.13L62"(i64 %40, %struct.rb_control_frame_struct* %41) #16, !dbg !59 - call void @sorbet_popFrame() #16, !dbg !59 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 16), i64** %38, align 8, !dbg !59, !tbaa !14 - %42 = load i64, i64* @guard_epoch_AttrReaderNoSig, align 8, !dbg !42 - %43 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !42, !tbaa !30 - %needTakeSlowPath = icmp ne i64 %42, %43, !dbg !42 - br i1 %needTakeSlowPath, label %44, label %45, !dbg !42, !prof !32 - -44: ; preds = %entry - call void @const_recompute_AttrReaderNoSig(), !dbg !42 - br label %45, !dbg !42 - -45: ; preds = %entry, %44 - %46 = load i64, i64* @guarded_const_AttrReaderNoSig, align 8, !dbg !42 - %47 = load i64, i64* @guard_epoch_AttrReaderNoSig, align 8, !dbg !42 - %48 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !42, !tbaa !30 - %guardUpdated = icmp eq i64 %47, %48, !dbg !42 - call void @llvm.assume(i1 %guardUpdated), !dbg !42 - %49 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %32, i64 0, i32 1, !dbg !42 - %50 = load i64*, i64** %49, align 8, !dbg !42 - store i64 %46, i64* %50, align 8, !dbg !42, !tbaa !6 - %51 = getelementptr inbounds i64, i64* %50, i64 1, !dbg !42 - store i64 2497, i64* %51, align 8, !dbg !42, !tbaa !6 - %52 = getelementptr inbounds i64, i64* %51, i64 1, !dbg !42 - store i64* %52, i64** %49, align 8, !dbg !42 - %send = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_new, i64 0), !dbg !42 - br label %BB2.i, !dbg !60 - -BB2.i: ; preds = %BB2.i.backedge, %45 - %i.sroa.0.0.i = phi i64 [ 1, %45 ], [ %i.sroa.0.0.i.be, %BB2.i.backedge ], !dbg !58 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 19), i64** %38, align 8, !tbaa !14 - %53 = and i64 %i.sroa.0.0.i, 1, !dbg !44 - %54 = icmp eq i64 %53, 0, !dbg !44 - br i1 %54, label %55, label %"fastSymCallIntrinsic_Integer_<.i", !dbg !44, !prof !61 - -55: ; preds = %BB2.i - %56 = and i64 %i.sroa.0.0.i, 7, !dbg !44 - %57 = icmp ne i64 %56, 0, !dbg !44 - %58 = and i64 %i.sroa.0.0.i, -9, !dbg !44 - %59 = icmp eq i64 %58, 0, !dbg !44 - %60 = or i1 %57, %59, !dbg !44 - br i1 %60, label %"alternativeCallIntrinsic_Integer_<.i", label %sorbet_isa_Integer.exit, !dbg !44, !prof !62 - -sorbet_isa_Integer.exit: ; preds = %55 - %61 = inttoptr i64 %i.sroa.0.0.i to %struct.iseq_inline_iv_cache_entry*, !dbg !44 - %62 = getelementptr inbounds %struct.iseq_inline_iv_cache_entry, %struct.iseq_inline_iv_cache_entry* %61, i64 0, i32 0, !dbg !44 - %63 = load i64, i64* %62, align 8, !dbg !44, !tbaa !63 - %64 = and i64 %63, 31, !dbg !44 - %65 = icmp eq i64 %64, 10, !dbg !44 - br i1 %65, label %"fastSymCallIntrinsic_Integer_<.i", label %"alternativeCallIntrinsic_Integer_<.i", !dbg !44, !prof !27 - -BB5.i: ; preds = %afterSend37.i - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 21), i64** %38, align 8, !tbaa !14 - %66 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %32, i64 0, i32 1, !dbg !45 - %67 = load i64*, i64** %66, align 8, !dbg !45 - store i64 %send, i64* %67, align 8, !dbg !45, !tbaa !6 - %68 = getelementptr inbounds i64, i64* %67, i64 1, !dbg !45 - store i64* %68, i64** %66, align 8, !dbg !45 - %send2 = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_foo, i64 0), !dbg !45 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 23), i64** %38, align 8, !dbg !45, !tbaa !14 - br i1 %69, label %"fastSymCallIntrinsic_Integer_+.i", label %"alternativeCallIntrinsic_Integer_+.i", !dbg !46 - -afterSend37.i: ; preds = %94, %sorbet_rb_int_lt.exit.i, %"alternativeCallIntrinsic_Integer_<.i" - %69 = phi i1 [ %72, %"alternativeCallIntrinsic_Integer_<.i" ], [ %77, %sorbet_rb_int_lt.exit.i ], [ %77, %94 ] - %"symIntrinsicRawPhi_<.i" = phi i64 [ %send4, %"alternativeCallIntrinsic_Integer_<.i" ], [ %rawSendResult89.i, %sorbet_rb_int_lt.exit.i ], [ %rawSendResult89.i, %94 ], !dbg !44 - %70 = and i64 %"symIntrinsicRawPhi_<.i", -9, !dbg !44 - %71 = icmp ne i64 %70, 0, !dbg !44 - br i1 %71, label %BB5.i, label %"func_.17$152.exit", !dbg !44 - -"alternativeCallIntrinsic_Integer_<.i": ; preds = %55, %sorbet_isa_Integer.exit - %72 = phi i1 [ %65, %sorbet_isa_Integer.exit ], [ false, %55 ] - %73 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %32, i64 0, i32 1, !dbg !44 - %74 = load i64*, i64** %73, align 8, !dbg !44 - store i64 %i.sroa.0.0.i, i64* %74, align 8, !dbg !44, !tbaa !6 - %75 = getelementptr inbounds i64, i64* %74, i64 1, !dbg !44 - store i64 20000001, i64* %75, align 8, !dbg !44, !tbaa !6 - %76 = getelementptr inbounds i64, i64* %75, i64 1, !dbg !44 - store i64* %76, i64** %73, align 8, !dbg !44 - %send4 = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @"ic_<", i64 0), !dbg !44 - br label %afterSend37.i, !dbg !44 - -"fastSymCallIntrinsic_Integer_<.i": ; preds = %BB2.i, %sorbet_isa_Integer.exit - %77 = phi i1 [ %65, %sorbet_isa_Integer.exit ], [ true, %BB2.i ] - call void @llvm.experimental.noalias.scope.decl(metadata !65) #16, !dbg !44 - %78 = and i64 %i.sroa.0.0.i, 1, !dbg !44 - %79 = icmp eq i64 %78, 0, !dbg !44 - br i1 %79, label %84, label %80, !dbg !44, !prof !68 - -80: ; preds = %"fastSymCallIntrinsic_Integer_<.i" - %81 = ashr i64 %i.sroa.0.0.i, 1, !dbg !44 - %82 = icmp slt i64 %81, 10000000, !dbg !44 - %83 = select i1 %82, i64 20, i64 0, !dbg !44 - br label %sorbet_rb_int_lt.exit.i, !dbg !44 - -84: ; preds = %"fastSymCallIntrinsic_Integer_<.i" - %85 = call i64 @sorbet_rb_int_lt_slowpath(i64 %i.sroa.0.0.i, i64 noundef 20000001) #16, !dbg !44, !noalias !65 - br label %sorbet_rb_int_lt.exit.i, !dbg !44 - -sorbet_rb_int_lt.exit.i: ; preds = %84, %80 - %rawSendResult89.i = phi i64 [ %83, %80 ], [ %85, %84 ] - %86 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !44, !tbaa !14 - %87 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %86, i64 0, i32 5, !dbg !44 - %88 = load i32, i32* %87, align 8, !dbg !44, !tbaa !25 - %89 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %86, i64 0, i32 6, !dbg !44 - %90 = load i32, i32* %89, align 4, !dbg !44, !tbaa !26 - %91 = xor i32 %90, -1, !dbg !44 - %92 = and i32 %91, %88, !dbg !44 - %93 = icmp eq i32 %92, 0, !dbg !44 - br i1 %93, label %afterSend37.i, label %94, !dbg !44, !prof !27 - -94: ; preds = %sorbet_rb_int_lt.exit.i - %95 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %86, i64 0, i32 8, !dbg !44 - %96 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %95, align 8, !dbg !44, !tbaa !28 - %97 = call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %96, i32 noundef 0) #16, !dbg !44 - br label %afterSend37.i, !dbg !44 - -"alternativeCallIntrinsic_Integer_+.i": ; preds = %BB5.i - %98 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %32, i64 0, i32 1, !dbg !46 - %99 = load i64*, i64** %98, align 8, !dbg !46 - store i64 %i.sroa.0.0.i, i64* %99, align 8, !dbg !46, !tbaa !6 - %100 = getelementptr inbounds i64, i64* %99, i64 1, !dbg !46 - store i64 3, i64* %100, align 8, !dbg !46, !tbaa !6 - %101 = getelementptr inbounds i64, i64* %100, i64 1, !dbg !46 - store i64* %101, i64** %98, align 8, !dbg !46 - %send6 = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @"ic_+", i64 0), !dbg !46 - br label %BB2.i.backedge, !dbg !46 - -"fastSymCallIntrinsic_Integer_+.i": ; preds = %BB5.i - call void @llvm.experimental.noalias.scope.decl(metadata !69) #16, !dbg !46 - %102 = and i64 %i.sroa.0.0.i, 1, !dbg !46 - %103 = icmp eq i64 %102, 0, !dbg !46 - br i1 %103, label %112, label %104, !dbg !46, !prof !68 - -104: ; preds = %"fastSymCallIntrinsic_Integer_+.i" - %105 = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %i.sroa.0.0.i, i64 noundef 2) #19, !dbg !46 - %106 = extractvalue { i64, i1 } %105, 1, !dbg !46 - %107 = extractvalue { i64, i1 } %105, 0, !dbg !46 - br i1 %106, label %108, label %sorbet_rb_int_plus.exit.i, !dbg !46 - -108: ; preds = %104 - %109 = ashr i64 %107, 1, !dbg !46 - %110 = xor i64 %109, -9223372036854775808, !dbg !46 - %111 = call i64 @rb_int2big(i64 %110) #16, !dbg !46 - br label %sorbet_rb_int_plus.exit.i, !dbg !46 - -112: ; preds = %"fastSymCallIntrinsic_Integer_+.i" - %113 = call i64 @sorbet_rb_int_plus_slowpath(i64 %i.sroa.0.0.i, i64 noundef 3) #16, !dbg !46, !noalias !69 - br label %sorbet_rb_int_plus.exit.i, !dbg !46 - -sorbet_rb_int_plus.exit.i: ; preds = %112, %108, %104 - %114 = phi i64 [ %113, %112 ], [ %111, %108 ], [ %107, %104 ], !dbg !46 - %115 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !46, !tbaa !14 - %116 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %115, i64 0, i32 5, !dbg !46 - %117 = load i32, i32* %116, align 8, !dbg !46, !tbaa !25 - %118 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %115, i64 0, i32 6, !dbg !46 - %119 = load i32, i32* %118, align 4, !dbg !46, !tbaa !26 - %120 = xor i32 %119, -1, !dbg !46 - %121 = and i32 %120, %117, !dbg !46 - %122 = icmp eq i32 %121, 0, !dbg !46 - br i1 %122, label %BB2.i.backedge, label %123, !dbg !46, !prof !27 - -123: ; preds = %sorbet_rb_int_plus.exit.i - %124 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %115, i64 0, i32 8, !dbg !46 - %125 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %124, align 8, !dbg !46, !tbaa !28 - %126 = call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %125, i32 noundef 0) #16, !dbg !46 - br label %BB2.i.backedge, !dbg !46 - -BB2.i.backedge: ; preds = %123, %sorbet_rb_int_plus.exit.i, %"alternativeCallIntrinsic_Integer_+.i" - %i.sroa.0.0.i.be = phi i64 [ %send6, %"alternativeCallIntrinsic_Integer_+.i" ], [ %114, %sorbet_rb_int_plus.exit.i ], [ %114, %123 ] - br label %BB2.i - -"func_.17$152.exit": ; preds = %afterSend37.i - %i.sroa.0.0.i.lcssa = phi i64 [ %i.sroa.0.0.i, %afterSend37.i ], !dbg !58 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 26), i64** %38, align 8, !tbaa !14 - %127 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %32, i64 0, i32 1, !dbg !47 - %128 = load i64*, i64** %127, align 8, !dbg !47 - store i64 %29, i64* %128, align 8, !dbg !47, !tbaa !6 - %129 = getelementptr inbounds i64, i64* %128, i64 1, !dbg !47 - store i64 %i.sroa.0.0.i.lcssa, i64* %129, align 8, !dbg !47, !tbaa !6 - %130 = getelementptr inbounds i64, i64* %129, i64 1, !dbg !47 - store i64* %130, i64** %127, align 8, !dbg !47 - %send8 = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts, i64 0), !dbg !47 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 27), i64** %38, align 8, !dbg !47, !tbaa !14 - %131 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %32, i64 0, i32 1, !dbg !48 - %132 = load i64*, i64** %131, align 8, !dbg !48 - store i64 %send, i64* %132, align 8, !dbg !48, !tbaa !6 - %133 = getelementptr inbounds i64, i64* %132, i64 1, !dbg !48 - store i64* %133, i64** %131, align 8, !dbg !48 - %send10 = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_foo.1, i64 0), !dbg !48 - %134 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %32, i64 0, i32 1, !dbg !49 - %135 = load i64*, i64** %134, align 8, !dbg !49 - store i64 %29, i64* %135, align 8, !dbg !49, !tbaa !6 - %136 = getelementptr inbounds i64, i64* %135, i64 1, !dbg !49 - store i64 %send10, i64* %136, align 8, !dbg !49, !tbaa !6 - %137 = getelementptr inbounds i64, i64* %136, i64 1, !dbg !49 - store i64* %137, i64** %134, align 8, !dbg !49 - %send12 = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts.2, i64 0), !dbg !49 - ret void -} - -; Function Attrs: nounwind sspreq uwtable -define i64 @"func_AttrReaderNoSig#10initialize"(i32 %argc, i64* nocapture readonly %argArray, i64 %selfRaw, %struct.rb_control_frame_struct* nocapture nonnull writeonly align 8 dereferenceable(8) %cfp, i8* nocapture nofree readnone %0) #8 !dbg !72 { -functionEntryInitializers: - %1 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 0 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 8), i64** %1, align 8, !tbaa !14 - %tooManyArgs = icmp ugt i32 %argc, 1, !dbg !73 - %tooFewArgs = icmp ult i32 %argc, 1, !dbg !73 - %or.cond = or i1 %tooManyArgs, %tooFewArgs, !dbg !73 - br i1 %or.cond, label %argCountFailBlock, label %fillRequiredArgs, !dbg !73, !prof !68 - -argCountFailBlock: ; preds = %functionEntryInitializers - tail call void @sorbet_raiseArity(i32 %argc, i32 noundef 1, i32 noundef 1) #20, !dbg !73 - unreachable, !dbg !73 - -fillRequiredArgs: ; preds = %functionEntryInitializers - %rawArg_foo = load i64, i64* %argArray, align 8, !dbg !73 - %2 = and i64 %rawArg_foo, 1, !dbg !74 - %3 = icmp eq i64 %2, 0, !dbg !74 - br i1 %3, label %4, label %typeTestSuccess, !dbg !74, !prof !61 - -4: ; preds = %fillRequiredArgs - %5 = and i64 %rawArg_foo, 7, !dbg !74 - %6 = icmp ne i64 %5, 0, !dbg !74 - %7 = and i64 %rawArg_foo, -9, !dbg !74 - %8 = icmp eq i64 %7, 0, !dbg !74 - %9 = or i1 %6, %8, !dbg !74 - br i1 %9, label %codeRepl, label %sorbet_isa_Integer.exit, !dbg !74 - -sorbet_isa_Integer.exit: ; preds = %4 - %10 = inttoptr i64 %rawArg_foo to %struct.iseq_inline_iv_cache_entry*, !dbg !74 - %11 = getelementptr inbounds %struct.iseq_inline_iv_cache_entry, %struct.iseq_inline_iv_cache_entry* %10, i64 0, i32 0, !dbg !74 - %12 = load i64, i64* %11, align 8, !dbg !74, !tbaa !63 - %13 = and i64 %12, 31, !dbg !74 - %14 = icmp eq i64 %13, 10, !dbg !74 - br i1 %14, label %typeTestSuccess, label %codeRepl, !dbg !74, !prof !27 - -typeTestSuccess: ; preds = %fillRequiredArgs, %sorbet_isa_Integer.exit - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 9), i64** %1, align 8, !dbg !74, !tbaa !14 - %"rubyId_@foo" = load i64, i64* @"rubyIdPrecomputed_@foo", align 8, !dbg !75 - tail call void @sorbet_vm_setivar(i64 %selfRaw, i64 %"rubyId_@foo", i64 %rawArg_foo, %struct.iseq_inline_iv_cache_entry* noundef @"ivc_@foo") #16, !dbg !75 - %"rubyId_@foo13" = load i64, i64* @"rubyIdPrecomputed_@foo", align 8, !dbg !76 - %15 = tail call i64 @sorbet_vm_getivar(i64 %selfRaw, i64 %"rubyId_@foo13", %struct.iseq_inline_iv_cache_entry* noundef @"ivc_@foo.3") #16, !dbg !76 - %"" = load i64, i64* @"", align 8 - ret i64 %"" - -codeRepl: ; preds = %4, %sorbet_isa_Integer.exit - tail call fastcc void @"func_AttrReaderNoSig#10initialize.cold.1"(i64 %rawArg_foo) #21, !dbg !74 - unreachable -} - -; Function Attrs: ssp -define internal i64 @"func_AttrReaderNoSig.13L62$block_1"(i64 %firstYieldArgRaw, i64 %localsOffset, i32 %argc, i64* nocapture nofree readnone %argArray, i64 %blockArg) #10 !dbg !41 { -functionEntryInitializers: - %0 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !14 - %1 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %0, i64 0, i32 2 - %2 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %1, align 8, !tbaa !16 - %3 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 3 - %4 = load i64, i64* %3, align 8, !tbaa !77 - %stackFrame = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_AttrReaderNoSig.13$block_1", align 8 - %5 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame, %struct.rb_iseq_struct** %5, align 8, !tbaa !20 - %6 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 4 - %7 = load i64*, i64** %6, align 8, !tbaa !22 - %8 = load i64, i64* %7, align 8, !tbaa !6 - %9 = and i64 %8, -129 - store i64 %9, i64* %7, align 8, !tbaa !6 - %10 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 0 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 7), i64** %10, align 8, !tbaa !14 - %11 = load i64, i64* @rb_cInteger, align 8, !dbg !40 - %12 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 1, !dbg !40 - %13 = load i64*, i64** %12, align 8, !dbg !40 - store i64 %4, i64* %13, align 8, !dbg !40, !tbaa !6 - %14 = getelementptr inbounds i64, i64* %13, i64 1, !dbg !40 - store i64 %11, i64* %14, align 8, !dbg !40, !tbaa !6 - %15 = getelementptr inbounds i64, i64* %14, i64 1, !dbg !40 - store i64* %15, i64** %12, align 8, !dbg !40 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_params, i64 0), !dbg !40 - %16 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 1, !dbg !40 - %17 = load i64*, i64** %16, align 8, !dbg !40 - store i64 %send, i64* %17, align 8, !dbg !40, !tbaa !6 - %18 = getelementptr inbounds i64, i64* %17, i64 1, !dbg !40 - store i64* %18, i64** %16, align 8, !dbg !40 - %send17 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_void, i64 0), !dbg !40 - ret i64 %send17, !dbg !78 -} - -; Function Attrs: inaccessiblememonly nofree nosync nounwind willreturn -declare void @llvm.experimental.noalias.scope.decl(metadata) #11 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly -declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #12 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #6 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #6 - -; Function Attrs: cold minsize noreturn nounwind sspreq uwtable -define internal fastcc void @"func_AttrReaderNoSig#10initialize.cold.1"(i64 %rawArg_foo) unnamed_addr #13 !dbg !79 { -newFuncRoot: - tail call void @sorbet_cast_failure(i64 %rawArg_foo, i8* noundef getelementptr inbounds ([4 x i8], [4 x i8]* @str_sig, i64 0, i64 0), i8* noundef getelementptr inbounds ([8 x i8], [8 x i8]* @str_Integer, i64 0, i64 0)) #20, !dbg !81 - unreachable, !dbg !81 -} - -; Function Attrs: nofree nosync nounwind willreturn -declare void @llvm.assume(i1 noundef) #14 - -; Function Attrs: ssp -define linkonce void @"const_recompute_T::Sig"() local_unnamed_addr #10 { - %1 = tail call i64 @sorbet_getConstant(i8* getelementptr inbounds ([7 x i8], [7 x i8]* @"str_T::Sig", i64 0, i64 0), i64 6) - store i64 %1, i64* @"guarded_const_T::Sig", align 8 - %2 = load i64, i64* @ruby_vm_global_constant_state, align 8, !tbaa !30 - store i64 %2, i64* @"guard_epoch_T::Sig", align 8 - ret void -} - -; Function Attrs: ssp -define linkonce void @const_recompute_AttrReaderNoSig() local_unnamed_addr #10 { - %1 = tail call i64 @sorbet_getConstant(i8* getelementptr inbounds ([16 x i8], [16 x i8]* @str_AttrReaderNoSig, i64 0, i64 0), i64 15) - store i64 %1, i64* @guarded_const_AttrReaderNoSig, align 8 - %2 = load i64, i64* @ruby_vm_global_constant_state, align 8, !tbaa !30 - store i64 %2, i64* @guard_epoch_AttrReaderNoSig, align 8 - ret void -} - -attributes #0 = { nounwind readnone willreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #1 = { cold noreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #2 = { noreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #3 = { "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #4 = { nofree nosync nounwind readnone speculatable willreturn } -attributes #5 = { allocsize(0,1) "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #6 = { argmemonly nofree nosync nounwind willreturn } -attributes #7 = { nounwind ssp uwtable "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #8 = { nounwind sspreq uwtable } -attributes #9 = { sspreq } -attributes #10 = { ssp } -attributes #11 = { inaccessiblememonly nofree nosync nounwind willreturn } -attributes #12 = { argmemonly nofree nosync nounwind willreturn writeonly } -attributes #13 = { cold minsize noreturn nounwind sspreq uwtable } -attributes #14 = { nofree nosync nounwind willreturn } -attributes #15 = { noreturn nounwind } -attributes #16 = { nounwind } -attributes #17 = { nounwind allocsize(0,1) } -attributes #18 = { nounwind readnone willreturn } -attributes #19 = { nounwind willreturn } -attributes #20 = { noreturn } -attributes #21 = { noinline } - -!llvm.module.flags = !{!0, !1, !2} -!llvm.dbg.cu = !{!3} - -!0 = !{i32 2, !"Debug Info Version", i32 3} -!1 = !{i32 4, !"cf-protection-return", i32 1} -!2 = !{i32 4, !"cf-protection-branch", i32 1} -!3 = distinct !DICompileUnit(language: DW_LANG_C, file: !4, producer: "Sorbet LLVM", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !5) -!4 = !DIFile(filename: "test/testdata/ruby_benchmark/stripe/attr_reader/no_sig.rb", directory: ".") -!5 = !{} -!6 = !{!7, !7, i64 0} -!7 = !{!"long", !8, i64 0} -!8 = !{!"omnipotent char", !9, i64 0} -!9 = !{!"Simple C/C++ TBAA"} -!10 = distinct !DISubprogram(name: "AttrReaderNoSig.", linkageName: "func_AttrReaderNoSig.13L62", scope: null, file: !4, line: 5, type: !11, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!11 = !DISubroutineType(types: !12) -!12 = !{!13} -!13 = !DIBasicType(name: "VALUE", size: 64, encoding: DW_ATE_signed) -!14 = !{!15, !15, i64 0} -!15 = !{!"any pointer", !8, i64 0} -!16 = !{!17, !15, i64 16} -!17 = !{!"rb_execution_context_struct", !15, i64 0, !7, i64 8, !15, i64 16, !15, i64 24, !15, i64 32, !18, i64 40, !18, i64 44, !15, i64 48, !15, i64 56, !15, i64 64, !7, i64 72, !7, i64 80, !15, i64 88, !7, i64 96, !15, i64 104, !15, i64 112, !7, i64 120, !7, i64 128, !8, i64 136, !8, i64 137, !7, i64 144, !19, i64 152} -!18 = !{!"int", !8, i64 0} -!19 = !{!"", !15, i64 0, !15, i64 8, !7, i64 16, !8, i64 24} -!20 = !{!21, !15, i64 16} -!21 = !{!"rb_control_frame_struct", !15, i64 0, !15, i64 8, !15, i64 16, !7, i64 24, !15, i64 32, !15, i64 40, !15, i64 48} -!22 = !{!21, !15, i64 32} -!23 = !DILocation(line: 0, scope: !10) -!24 = !DILocation(line: 7, column: 3, scope: !10) -!25 = !{!17, !18, i64 40} -!26 = !{!17, !18, i64 44} -!27 = !{!"branch_weights", i32 2000, i32 1} -!28 = !{!17, !15, i64 56} -!29 = !DILocation(line: 6, column: 3, scope: !10) -!30 = !{!31, !31, i64 0} -!31 = !{!"long long", !8, i64 0} -!32 = !{!"branch_weights", i32 1, i32 10000} -!33 = !DILocation(line: 8, column: 3, scope: !10) -!34 = !{!35, !18, i64 8} -!35 = !{!"rb_sorbet_param_struct", !36, i64 0, !18, i64 4, !18, i64 8, !18, i64 12, !18, i64 16, !18, i64 20, !18, i64 24, !18, i64 28, !15, i64 32, !18, i64 40, !18, i64 44, !18, i64 48, !18, i64 52, !15, i64 56} -!36 = !{!"", !18, i64 0, !18, i64 0, !18, i64 0, !18, i64 0, !18, i64 0, !18, i64 0, !18, i64 0, !18, i64 0, !18, i64 1, !18, i64 1} -!37 = !{!35, !18, i64 4} -!38 = !{!35, !15, i64 32} -!39 = !DILocation(line: 13, column: 3, scope: !10) -!40 = !DILocation(line: 7, column: 8, scope: !41) -!41 = distinct !DISubprogram(name: "AttrReaderNoSig.", linkageName: "func_AttrReaderNoSig.13L62$block_1", scope: !10, file: !4, line: 5, type: !11, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!42 = !DILocation(line: 16, column: 5, scope: !43) -!43 = distinct !DISubprogram(name: ".", linkageName: "func_.17$152", scope: null, file: !4, line: 5, type: !11, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!44 = !DILocation(line: 19, column: 7, scope: !43) -!45 = !DILocation(line: 21, column: 3, scope: !43) -!46 = !DILocation(line: 23, column: 3, scope: !43) -!47 = !DILocation(line: 26, column: 1, scope: !43) -!48 = !DILocation(line: 27, column: 6, scope: !43) -!49 = !DILocation(line: 27, column: 1, scope: !43) -!50 = !{!51, !7, i64 400} -!51 = !{!"rb_vm_struct", !7, i64 0, !52, i64 8, !15, i64 192, !15, i64 200, !15, i64 208, !31, i64 216, !8, i64 224, !53, i64 264, !53, i64 280, !53, i64 296, !53, i64 312, !7, i64 328, !18, i64 336, !18, i64 340, !18, i64 344, !18, i64 344, !18, i64 344, !18, i64 344, !18, i64 348, !7, i64 352, !8, i64 360, !7, i64 400, !7, i64 408, !7, i64 416, !7, i64 424, !7, i64 432, !7, i64 440, !7, i64 448, !15, i64 456, !15, i64 464, !55, i64 472, !56, i64 992, !15, i64 1016, !15, i64 1024, !18, i64 1032, !18, i64 1036, !53, i64 1040, !8, i64 1056, !7, i64 1096, !7, i64 1104, !7, i64 1112, !7, i64 1120, !7, i64 1128, !18, i64 1136, !15, i64 1144, !15, i64 1152, !15, i64 1160, !15, i64 1168, !15, i64 1176, !15, i64 1184, !18, i64 1192, !57, i64 1200, !8, i64 1232} -!52 = !{!"rb_global_vm_lock_struct", !15, i64 0, !8, i64 8, !53, i64 48, !15, i64 64, !18, i64 72, !8, i64 80, !8, i64 128, !18, i64 176, !18, i64 180} -!53 = !{!"list_head", !54, i64 0} -!54 = !{!"list_node", !15, i64 0, !15, i64 8} -!55 = !{!"", !8, i64 0} -!56 = !{!"rb_hook_list_struct", !15, i64 0, !18, i64 8, !18, i64 12, !18, i64 16} -!57 = !{!"", !7, i64 0, !7, i64 8, !7, i64 16, !7, i64 24} -!58 = !DILocation(line: 0, scope: !43) -!59 = !DILocation(line: 5, column: 1, scope: !43) -!60 = !DILocation(line: 18, column: 5, scope: !43) -!61 = !{!"branch_weights", i32 1, i32 2000} -!62 = !{!"branch_weights", i32 1073205, i32 2146410443} -!63 = !{!64, !7, i64 0} -!64 = !{!"RBasic", !7, i64 0, !7, i64 8} -!65 = !{!66} -!66 = distinct !{!66, !67, !"sorbet_rb_int_lt: argument 0"} -!67 = distinct !{!67, !"sorbet_rb_int_lt"} -!68 = !{!"branch_weights", i32 4001, i32 4000000} -!69 = !{!70} -!70 = distinct !{!70, !71, !"sorbet_rb_int_plus: argument 0"} -!71 = distinct !{!71, !"sorbet_rb_int_plus"} -!72 = distinct !DISubprogram(name: "AttrReaderNoSig#initialize", linkageName: "func_AttrReaderNoSig#10initialize", scope: null, file: !4, line: 8, type: !11, scopeLine: 8, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!73 = !DILocation(line: 8, column: 3, scope: !72) -!74 = !DILocation(line: 8, column: 18, scope: !72) -!75 = !DILocation(line: 9, column: 12, scope: !72) -!76 = !DILocation(line: 9, column: 5, scope: !72) -!77 = !{!21, !7, i64 24} -!78 = !DILocation(line: 7, column: 3, scope: !41) -!79 = distinct !DISubprogram(name: "func_AttrReaderNoSig#10initialize.cold.1", linkageName: "func_AttrReaderNoSig#10initialize.cold.1", scope: null, file: !4, type: !80, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !3, retainedNodes: !5) -!80 = !DISubroutineType(types: !5) -!81 = !DILocation(line: 8, column: 18, scope: !79) diff --git a/test/testdata/ruby_benchmark/stripe/attr_reader/no_sig.rb b/test/testdata/ruby_benchmark/stripe/attr_reader/no_sig.rb deleted file mode 100644 index 7366208f56..0000000000 --- a/test/testdata/ruby_benchmark/stripe/attr_reader/no_sig.rb +++ /dev/null @@ -1,27 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -class AttrReaderNoSig - extend T::Sig - sig {params(foo: Integer).void} - def initialize(foo) - @foo = foo - end - - # no sig - attr_reader :foo -end - -x = AttrReaderNoSig.new(1248) - -i = 0 -while i < 10_000_000 - - x.foo - - i += 1 -end - -puts i -puts x.foo diff --git a/test/testdata/ruby_benchmark/stripe/attr_reader/no_sig_force_ivar.rb b/test/testdata/ruby_benchmark/stripe/attr_reader/no_sig_force_ivar.rb deleted file mode 100644 index 4dd9db4d7c..0000000000 --- a/test/testdata/ruby_benchmark/stripe/attr_reader/no_sig_force_ivar.rb +++ /dev/null @@ -1,31 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -class AttrReaderNoSigForceIVAR - extend T::Sig - sig {params(foo: Integer).void} - def initialize(foo) - @foo = foo - end - - # no sig - attr_reader :foo - - # This is here to trick the compiler into redefining the method using - # `attr_reader`, which the VM special cases. - self.send(:attr_reader, :foo) -end - -x = AttrReaderNoSigForceIVAR.new(1248) - -i = 0 -while i < 10_000_000 - - x.foo - - i += 1 -end - -puts i -puts x.foo diff --git a/test/testdata/ruby_benchmark/stripe/attr_reader/sig_checked.llo.exp b/test/testdata/ruby_benchmark/stripe/attr_reader/sig_checked.llo.exp deleted file mode 100644 index a65631e7a2..0000000000 --- a/test/testdata/ruby_benchmark/stripe/attr_reader/sig_checked.llo.exp +++ /dev/null @@ -1,1070 +0,0 @@ -; ModuleID = 'payload' -source_filename = "llvm-link" -target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } -%struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.__pthread_cond_s } -%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } -%union.anon = type { i64 } -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } -%struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } -%struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } -%struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } -%struct.anon.0 = type { i16, [2 x i8] } -%struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } -%struct.rb_iseq_location_struct = type { i64, i64, i64, i64, i32, %struct.rb_code_location_struct } -%struct.rb_code_location_struct = type { %struct.rb_code_position_struct, %struct.rb_code_position_struct } -%struct.rb_code_position_struct = type { i32, i32 } -%struct.iseq_insn_info = type { %struct.iseq_insn_info_entry*, i32*, i32, %struct.succ_index_table* } -%struct.iseq_insn_info_entry = type opaque -%struct.succ_index_table = type opaque -%struct.iseq_catch_table = type opaque -%union.iseq_inline_storage_entry = type { %struct.iseq_inline_cache_entry } -%struct.iseq_inline_cache_entry = type { i64, %struct.rb_cref_struct*, i64 } -%struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } -%struct.rb_scope_visi_struct = type { i8, [3 x i8] } -%struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } -%struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } -%union.anon.13 = type { %struct.rb_method_cfunc_struct } -%struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%union.anon.15 = type { i32 } -%struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.16 = type { i64, i64, i64, i64* } -%union.anon.17 = type { %struct.anon.18 } -%struct.anon.18 = type { i64, i32 } -%struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } -%struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } -%struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } -%struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } -%struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } -%struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } -%struct.__sigset_t = type { [16 x i64] } -%struct.rb_calling_info = type { i64, i64, i32, i32 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } -%union.anon.9 = type { %union.pthread_cond_t } -%struct.rb_unblock_callback = type { void (i8*)*, i8* } -%struct.rb_mutex_struct = type opaque -%struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.10 = type { %struct.anon.11 } -%struct.anon.11 = type { i64, i64, i32 } -%struct.rb_fiber_struct = type opaque -%struct.anon.5 = type { [65 x i64] } -%struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } -%struct.rb_event_hook_struct = type opaque -%struct.rb_postponed_job_struct = type opaque -%struct.list_head = type { %struct.list_node } -%union.pthread_mutex_t = type { %struct.__pthread_mutex_s } -%struct.__pthread_mutex_s = type { i32, i32, i32, i32, i32, i16, i16, %struct.__pthread_internal_list } -%struct.__pthread_internal_list = type { %struct.__pthread_internal_list*, %struct.__pthread_internal_list* } -%struct.rb_objspace = type opaque -%struct.rb_at_exit_list = type { void (%struct.rb_vm_struct*)*, %struct.rb_at_exit_list* } -%struct.st_table = type { i8, i8, i8, i32, %struct.st_hash_type*, i64, i64*, i64, i64, %struct.st_table_entry* } -%struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } -%struct.st_table_entry = type opaque -%struct.rb_builtin_function = type opaque -%struct.anon.6 = type { i64, i64, i64, i64 } -%struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } -%struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } -%struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } -%struct.rb_call_info_with_kwarg = type { %struct.rb_call_info, %struct.rb_call_info_kw_arg* } -%struct.rb_call_info_kw_arg = type { i32, [1 x i64] } -%struct.iseq_inline_iv_cache_entry = type { i64, i64 } - -@ruby_current_vm_ptr = external local_unnamed_addr global %struct.rb_vm_struct*, align 8 -@ruby_current_execution_context_ptr = external local_unnamed_addr global %struct.rb_execution_context_struct*, align 8 -@sorbet_getVoidSingleton.name = internal constant [30 x i8] c"T::Private::Types::Void::VOID\00", align 16 -@ruby_vm_global_constant_state = external local_unnamed_addr global i64, align 8 -@rb_eRuntimeError = external local_unnamed_addr global i64, align 8 -@.str.9 = private unnamed_addr constant [95 x i8] c"sorbet_getBuildSCMRevision: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@.str.10 = private unnamed_addr constant [93 x i8] c"sorbet_getIsReleaseBuild: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@"stackFramePrecomputed_func_.17$152" = unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"rubyIdPrecomputed_" = internal unnamed_addr global i64 0, align 8 -@"str_" = private unnamed_addr constant [17 x i8] c"\00", align 1 -@"rubyStrFrozen_" = internal unnamed_addr global i64 0, align 8 -@"rubyStrFrozen_test/testdata/ruby_benchmark/stripe/attr_reader/sig_checked.rb" = internal unnamed_addr global i64 0, align 8 -@"str_test/testdata/ruby_benchmark/stripe/attr_reader/sig_checked.rb" = private unnamed_addr constant [63 x i8] c"test/testdata/ruby_benchmark/stripe/attr_reader/sig_checked.rb\00", align 1 -@iseqEncodedArray = internal global [28 x i64] zeroinitializer -@fileLineNumberInfo = internal global %struct.SorbetLineNumberInfo zeroinitializer -@str_AttrReaderSigChecked = private unnamed_addr constant [21 x i8] c"AttrReaderSigChecked\00", align 1 -@ic_new = internal global %struct.FunctionInlineCache zeroinitializer -@rubyIdPrecomputed_new = internal unnamed_addr global i64 0, align 8 -@str_new = private unnamed_addr constant [4 x i8] c"new\00", align 1 -@"rubyIdPrecomputed_<" = internal unnamed_addr global i64 0, align 8 -@"str_<" = private unnamed_addr constant [2 x i8] c"<\00", align 1 -@"ic_<" = internal global %struct.FunctionInlineCache zeroinitializer -@ic_foo = internal global %struct.FunctionInlineCache zeroinitializer -@rubyIdPrecomputed_foo = internal unnamed_addr global i64 0, align 8 -@str_foo = private unnamed_addr constant [4 x i8] c"foo\00", align 1 -@"rubyIdPrecomputed_+" = internal unnamed_addr global i64 0, align 8 -@"str_+" = private unnamed_addr constant [2 x i8] c"+\00", align 1 -@"ic_+" = internal global %struct.FunctionInlineCache zeroinitializer -@ic_puts = internal global %struct.FunctionInlineCache zeroinitializer -@rubyIdPrecomputed_puts = internal unnamed_addr global i64 0, align 8 -@str_puts = private unnamed_addr constant [5 x i8] c"puts\00", align 1 -@ic_foo.1 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_puts.2 = internal global %struct.FunctionInlineCache zeroinitializer -@"stackFramePrecomputed_func_AttrReaderSigChecked#10initialize" = unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@rubyIdPrecomputed_initialize = internal unnamed_addr global i64 0, align 8 -@str_initialize = private unnamed_addr constant [11 x i8] c"initialize\00", align 1 -@str_sig = private unnamed_addr constant [4 x i8] c"sig\00", align 1 -@str_Integer = private unnamed_addr constant [8 x i8] c"Integer\00", align 1 -@"ivc_@foo" = internal global %struct.iseq_inline_iv_cache_entry zeroinitializer -@"rubyIdPrecomputed_@foo" = internal unnamed_addr global i64 0, align 8 -@"str_@foo" = private unnamed_addr constant [5 x i8] c"@foo\00", align 1 -@"ivc_@foo.3" = internal global %struct.iseq_inline_iv_cache_entry zeroinitializer -@"" = internal unnamed_addr global i64 0 -@"stackFramePrecomputed_func_AttrReaderSigChecked#3foo" = unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"ivc_@foo.4" = internal global %struct.iseq_inline_iv_cache_entry zeroinitializer -@"str_Return value" = private unnamed_addr constant [13 x i8] c"Return value\00", align 1 -@"stackFramePrecomputed_func_AttrReaderSigChecked.13" = unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"rubyIdPrecomputed_" = internal unnamed_addr global i64 0, align 8 -@"str_" = private unnamed_addr constant [29 x i8] c"\00", align 1 -@"rubyIdPrecomputed_" = internal unnamed_addr global i64 0, align 8 -@"str_" = private unnamed_addr constant [13 x i8] c"\00", align 1 -@"stackFramePrecomputed_func_AttrReaderSigChecked.13$block_1" = unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"rubyIdPrecomputed_block in " = internal unnamed_addr global i64 0, align 8 -@"str_block in " = private unnamed_addr constant [38 x i8] c"block in \00", align 1 -@"rubyStrFrozen_block in " = internal unnamed_addr global i64 0, align 8 -@"stackFramePrecomputed_func_AttrReaderSigChecked.13$block_2" = unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@ic_params = internal global %struct.FunctionInlineCache zeroinitializer -@rubyIdPrecomputed_params = internal unnamed_addr global i64 0, align 8 -@str_params = private unnamed_addr constant [7 x i8] c"params\00", align 1 -@ic_void = internal global %struct.FunctionInlineCache zeroinitializer -@rubyIdPrecomputed_void = internal unnamed_addr global i64 0, align 8 -@str_void = private unnamed_addr constant [5 x i8] c"void\00", align 1 -@ic_returns = internal global %struct.FunctionInlineCache zeroinitializer -@rubyIdPrecomputed_returns = internal unnamed_addr global i64 0, align 8 -@str_returns = private unnamed_addr constant [8 x i8] c"returns\00", align 1 -@"str_T::Sig" = private unnamed_addr constant [7 x i8] c"T::Sig\00", align 1 -@ic_extend = internal global %struct.FunctionInlineCache zeroinitializer -@rubyIdPrecomputed_extend = internal unnamed_addr global i64 0, align 8 -@str_extend = private unnamed_addr constant [7 x i8] c"extend\00", align 1 -@str_normal = private unnamed_addr constant [7 x i8] c"normal\00", align 1 -@"guard_epoch_T::Sig" = linkonce local_unnamed_addr global i64 0 -@"guarded_const_T::Sig" = linkonce local_unnamed_addr global i64 0 -@guard_epoch_AttrReaderSigChecked = linkonce local_unnamed_addr global i64 0 -@guarded_const_AttrReaderSigChecked = linkonce local_unnamed_addr global i64 0 -@rb_cObject = external local_unnamed_addr constant i64 -@rb_cInteger = external local_unnamed_addr constant i64 - -; Function Attrs: nounwind readnone willreturn -declare i64 @rb_id2sym(i64) local_unnamed_addr #0 - -; Function Attrs: cold noreturn -declare void @sorbet_cast_failure(i64, i8*, i8*) local_unnamed_addr #1 - -; Function Attrs: noreturn -declare void @sorbet_raiseArity(i32, i32, i32) local_unnamed_addr #2 - -declare %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64, i64, i64, i64, %struct.rb_iseq_struct*, i32, i32, %struct.SorbetLineNumberInfo*, i64*, i32, i32) local_unnamed_addr #3 - -declare void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo*, i64*, i32) local_unnamed_addr #3 - -declare i64 @sorbet_getConstant(i8*, i64) local_unnamed_addr #3 - -declare i64 @sorbet_readRealpath() local_unnamed_addr #3 - -declare %struct.rb_control_frame_struct* @sorbet_pushStaticInitFrame(i64) local_unnamed_addr #3 - -declare void @sorbet_popFrame() local_unnamed_addr #3 - -declare void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache*, i64, i32, i32, i32, i64*) local_unnamed_addr #3 - -declare i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache*, i64) local_unnamed_addr #3 - -declare void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_iseq_struct*) local_unnamed_addr #3 - -declare i64 @sorbet_rb_int_plus_slowpath(i64, i64) local_unnamed_addr #3 - -declare i64 @sorbet_rb_int_lt_slowpath(i64, i64) local_unnamed_addr #3 - -declare i64 @sorbet_vm_getivar(i64, i64, %struct.iseq_inline_iv_cache_entry*) local_unnamed_addr #3 - -declare void @sorbet_vm_setivar(i64, i64, i64, %struct.iseq_inline_iv_cache_entry*) local_unnamed_addr #3 - -declare void @sorbet_vm_register_sig(i64, i64, i64, i64, i64 (i64, i64, i32, i64*, i64)*) local_unnamed_addr #3 - -declare void @sorbet_vm_define_method(i64, i8*, i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*)*, i8*, %struct.rb_iseq_struct*, i1 zeroext) local_unnamed_addr #3 - -declare i64 @sorbet_vm_fstring_new(i8*, i64) local_unnamed_addr #3 - -declare i64 @rb_define_class(i8*, i64) local_unnamed_addr #3 - -declare i64 @rb_intern2(i8*, i64) local_unnamed_addr #3 - -declare void @rb_gc_register_mark_object(i64) local_unnamed_addr #3 - -; Function Attrs: noreturn -declare void @rb_raise(i64, i8*, ...) local_unnamed_addr #2 - -declare i64 @rb_int2big(i64) local_unnamed_addr #3 - -; Function Attrs: nofree nosync nounwind readnone speculatable willreturn -declare { i64, i1 } @llvm.sadd.with.overflow.i64(i64, i64) #4 - -declare i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct*, i32) local_unnamed_addr #3 - -; Function Attrs: allocsize(0,1) -declare noalias nonnull i8* @ruby_xcalloc(i64, i64) local_unnamed_addr #5 - -; Function Attrs: allocsize(0,1) -declare noalias nonnull i8* @ruby_xmalloc2(i64, i64) local_unnamed_addr #5 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) #6 - -; Function Attrs: nounwind ssp uwtable -define weak i32 @sorbet_getIsReleaseBuild() local_unnamed_addr #7 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([93 x i8], [93 x i8]* @.str.10, i64 0, i64 0)) #15 - unreachable -} - -; Function Attrs: nounwind ssp uwtable -define weak i8* @sorbet_getBuildSCMRevision() local_unnamed_addr #7 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([95 x i8], [95 x i8]* @.str.9, i64 0, i64 0)) #15 - unreachable -} - -; Function Attrs: nounwind sspreq uwtable -define internal fastcc void @"func_AttrReaderSigChecked.13L62"(i64 %selfRaw, %struct.rb_control_frame_struct* %cfp) unnamed_addr #8 !dbg !10 { -functionEntryInitializers: - %stackFrame = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_AttrReaderSigChecked.13", align 8 - %0 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !14 - %1 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %0, i64 0, i32 2 - %2 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %1, align 8, !tbaa !16 - %3 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame, %struct.rb_iseq_struct** %3, align 8, !tbaa !20 - %4 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 4 - %5 = load i64*, i64** %4, align 8, !tbaa !22 - %6 = load i64, i64* %5, align 8, !tbaa !6 - %7 = and i64 %6, -33 - store i64 %7, i64* %5, align 8, !tbaa !6 - tail call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %0, %struct.rb_control_frame_struct* %2, %struct.rb_iseq_struct* %stackFrame) #16 - %8 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 0 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 7), i64** %8, align 8, !dbg !23, !tbaa !14 - %rubyId_initialize = load i64, i64* @rubyIdPrecomputed_initialize, align 8, !dbg !24 - %rawSym = tail call i64 @rb_id2sym(i64 %rubyId_initialize), !dbg !24 - tail call void @sorbet_vm_register_sig(i64 noundef 0, i64 %rawSym, i64 %selfRaw, i64 noundef 8, i64 (i64, i64, i32, i64*, i64)* noundef @"func_AttrReaderSigChecked.13L62$block_1"), !dbg !24 - %9 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !24, !tbaa !14 - %10 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %9, i64 0, i32 5, !dbg !24 - %11 = load i32, i32* %10, align 8, !dbg !24, !tbaa !25 - %12 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %9, i64 0, i32 6, !dbg !24 - %13 = load i32, i32* %12, align 4, !dbg !24, !tbaa !26 - %14 = xor i32 %13, -1, !dbg !24 - %15 = and i32 %14, %11, !dbg !24 - %16 = icmp eq i32 %15, 0, !dbg !24 - br i1 %16, label %rb_vm_check_ints.exit3, label %17, !dbg !24, !prof !27 - -17: ; preds = %functionEntryInitializers - %18 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %9, i64 0, i32 8, !dbg !24 - %19 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %18, align 8, !dbg !24, !tbaa !28 - %20 = tail call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %19, i32 noundef 0) #16, !dbg !24 - br label %rb_vm_check_ints.exit3, !dbg !24 - -rb_vm_check_ints.exit3: ; preds = %functionEntryInitializers, %17 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 12), i64** %8, align 8, !dbg !24, !tbaa !14 - %rubyId_foo = load i64, i64* @rubyIdPrecomputed_foo, align 8, !dbg !29 - %rawSym54 = tail call i64 @rb_id2sym(i64 %rubyId_foo), !dbg !29 - tail call void @sorbet_vm_register_sig(i64 noundef 0, i64 %rawSym54, i64 %selfRaw, i64 noundef 8, i64 (i64, i64, i32, i64*, i64)* noundef @"func_AttrReaderSigChecked.13L62$block_2"), !dbg !29 - %21 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !29, !tbaa !14 - %22 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %21, i64 0, i32 5, !dbg !29 - %23 = load i32, i32* %22, align 8, !dbg !29, !tbaa !25 - %24 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %21, i64 0, i32 6, !dbg !29 - %25 = load i32, i32* %24, align 4, !dbg !29, !tbaa !26 - %26 = xor i32 %25, -1, !dbg !29 - %27 = and i32 %26, %23, !dbg !29 - %28 = icmp eq i32 %27, 0, !dbg !29 - br i1 %28, label %rb_vm_check_ints.exit2, label %29, !dbg !29, !prof !27 - -29: ; preds = %rb_vm_check_ints.exit3 - %30 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %21, i64 0, i32 8, !dbg !29 - %31 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %30, align 8, !dbg !29, !tbaa !28 - %32 = tail call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %31, i32 noundef 0) #16, !dbg !29 - br label %rb_vm_check_ints.exit2, !dbg !29 - -rb_vm_check_ints.exit2: ; preds = %rb_vm_check_ints.exit3, %29 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 6), i64** %8, align 8, !dbg !29, !tbaa !14 - %33 = load i64, i64* @"guard_epoch_T::Sig", align 8, !dbg !30 - %34 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !30, !tbaa !31 - %needTakeSlowPath = icmp ne i64 %33, %34, !dbg !30 - br i1 %needTakeSlowPath, label %35, label %36, !dbg !30, !prof !33 - -35: ; preds = %rb_vm_check_ints.exit2 - tail call void @"const_recompute_T::Sig"(), !dbg !30 - br label %36, !dbg !30 - -36: ; preds = %rb_vm_check_ints.exit2, %35 - %37 = load i64, i64* @"guarded_const_T::Sig", align 8, !dbg !30 - %38 = load i64, i64* @"guard_epoch_T::Sig", align 8, !dbg !30 - %39 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !30, !tbaa !31 - %guardUpdated = icmp eq i64 %38, %39, !dbg !30 - tail call void @llvm.assume(i1 %guardUpdated), !dbg !30 - %40 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 1, !dbg !30 - %41 = load i64*, i64** %40, align 8, !dbg !30 - store i64 %selfRaw, i64* %41, align 8, !dbg !30, !tbaa !6 - %42 = getelementptr inbounds i64, i64* %41, i64 1, !dbg !30 - store i64 %37, i64* %42, align 8, !dbg !30, !tbaa !6 - %43 = getelementptr inbounds i64, i64* %42, i64 1, !dbg !30 - store i64* %43, i64** %40, align 8, !dbg !30 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_extend, i64 0), !dbg !30 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 8), i64** %8, align 8, !dbg !30, !tbaa !14 - %44 = load i64, i64* @guard_epoch_AttrReaderSigChecked, align 8, !dbg !34 - %45 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !34, !tbaa !31 - %needTakeSlowPath4 = icmp ne i64 %44, %45, !dbg !34 - br i1 %needTakeSlowPath4, label %46, label %47, !dbg !34, !prof !33 - -46: ; preds = %36 - tail call void @const_recompute_AttrReaderSigChecked(), !dbg !34 - br label %47, !dbg !34 - -47: ; preds = %36, %46 - %48 = load i64, i64* @guarded_const_AttrReaderSigChecked, align 8, !dbg !34 - %49 = load i64, i64* @guard_epoch_AttrReaderSigChecked, align 8, !dbg !34 - %50 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !34, !tbaa !31 - %guardUpdated5 = icmp eq i64 %49, %50, !dbg !34 - tail call void @llvm.assume(i1 %guardUpdated5), !dbg !34 - %stackFrame77 = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_AttrReaderSigChecked#10initialize", align 8, !dbg !34 - %51 = tail call noalias nonnull i8* @ruby_xcalloc(i64 noundef 1, i64 noundef 64) #17, !dbg !34 - %52 = bitcast i8* %51 to i16*, !dbg !34 - %53 = load i16, i16* %52, align 8, !dbg !34 - %54 = and i16 %53, -384, !dbg !34 - %55 = or i16 %54, 1, !dbg !34 - store i16 %55, i16* %52, align 8, !dbg !34 - %56 = getelementptr inbounds i8, i8* %51, i64 8, !dbg !34 - %57 = bitcast i8* %56 to i32*, !dbg !34 - store i32 1, i32* %57, align 8, !dbg !34, !tbaa !35 - %58 = getelementptr inbounds i8, i8* %51, i64 12, !dbg !34 - %59 = bitcast i8* %58 to i32*, !dbg !34 - %60 = getelementptr inbounds i8, i8* %51, i64 4, !dbg !34 - %61 = bitcast i8* %60 to i32*, !dbg !34 - tail call void @llvm.memset.p0i8.i64(i8* nonnull align 4 %58, i8 0, i64 20, i1 false), !dbg !34 - store i32 1, i32* %61, align 4, !dbg !34, !tbaa !38 - %positional_table = alloca i64, align 8, !dbg !34 - %rubyId_foo78 = load i64, i64* @rubyIdPrecomputed_foo, align 8, !dbg !34 - store i64 %rubyId_foo78, i64* %positional_table, align 8, !dbg !34 - %62 = tail call noalias nonnull i8* @ruby_xmalloc2(i64 noundef 1, i64 noundef 8) #17, !dbg !34 - %63 = bitcast i64* %positional_table to i8*, !dbg !34 - call void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture nonnull writeonly align 1 %62, i8* nocapture noundef nonnull readonly align 8 dereferenceable(8) %63, i64 noundef 8, i1 noundef false) #16, !dbg !34 - %64 = getelementptr inbounds i8, i8* %51, i64 32, !dbg !34 - %65 = bitcast i8* %64 to i8**, !dbg !34 - store i8* %62, i8** %65, align 8, !dbg !34, !tbaa !39 - tail call void @sorbet_vm_define_method(i64 %48, i8* noundef getelementptr inbounds ([11 x i8], [11 x i8]* @str_initialize, i64 0, i64 0), i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*)* noundef @"func_AttrReaderSigChecked#10initialize", i8* nonnull %51, %struct.rb_iseq_struct* %stackFrame77, i1 noundef zeroext false) #16, !dbg !34 - %66 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !34, !tbaa !14 - %67 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %66, i64 0, i32 5, !dbg !34 - %68 = load i32, i32* %67, align 8, !dbg !34, !tbaa !25 - %69 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %66, i64 0, i32 6, !dbg !34 - %70 = load i32, i32* %69, align 4, !dbg !34, !tbaa !26 - %71 = xor i32 %70, -1, !dbg !34 - %72 = and i32 %71, %68, !dbg !34 - %73 = icmp eq i32 %72, 0, !dbg !34 - br i1 %73, label %rb_vm_check_ints.exit1, label %74, !dbg !34, !prof !27 - -74: ; preds = %47 - %75 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %66, i64 0, i32 8, !dbg !34 - %76 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %75, align 8, !dbg !34, !tbaa !28 - %77 = tail call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %76, i32 noundef 0) #16, !dbg !34 - br label %rb_vm_check_ints.exit1, !dbg !34 - -rb_vm_check_ints.exit1: ; preds = %47, %74 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 13), i64** %8, align 8, !dbg !34, !tbaa !14 - %stackFrame88 = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_AttrReaderSigChecked#3foo", align 8, !dbg !40 - %78 = tail call noalias nonnull i8* @ruby_xcalloc(i64 noundef 1, i64 noundef 64) #17, !dbg !40 - %79 = bitcast i8* %78 to i16*, !dbg !40 - %80 = load i16, i16* %79, align 8, !dbg !40 - %81 = and i16 %80, -384, !dbg !40 - store i16 %81, i16* %79, align 8, !dbg !40 - %82 = getelementptr inbounds i8, i8* %78, i64 4, !dbg !40 - %83 = bitcast i8* %82 to i32*, !dbg !40 - tail call void @llvm.memset.p0i8.i64(i8* nonnull align 4 %82, i8 0, i64 28, i1 false), !dbg !40 - tail call void @sorbet_vm_define_method(i64 %48, i8* noundef getelementptr inbounds ([4 x i8], [4 x i8]* @str_foo, i64 0, i64 0), i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*)* noundef @"func_AttrReaderSigChecked#3foo", i8* nonnull %78, %struct.rb_iseq_struct* %stackFrame88, i1 noundef zeroext false) #16, !dbg !40 - %84 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !40, !tbaa !14 - %85 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %84, i64 0, i32 5, !dbg !40 - %86 = load i32, i32* %85, align 8, !dbg !40, !tbaa !25 - %87 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %84, i64 0, i32 6, !dbg !40 - %88 = load i32, i32* %87, align 4, !dbg !40, !tbaa !26 - %89 = xor i32 %88, -1, !dbg !40 - %90 = and i32 %89, %86, !dbg !40 - %91 = icmp eq i32 %90, 0, !dbg !40 - br i1 %91, label %rb_vm_check_ints.exit, label %92, !dbg !40, !prof !27 - -92: ; preds = %rb_vm_check_ints.exit1 - %93 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %84, i64 0, i32 8, !dbg !40 - %94 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %93, align 8, !dbg !40, !tbaa !28 - %95 = tail call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %94, i32 noundef 0) #16, !dbg !40 - br label %rb_vm_check_ints.exit, !dbg !40 - -rb_vm_check_ints.exit: ; preds = %rb_vm_check_ints.exit1, %92 - ret void -} - -; Function Attrs: sspreq -define void @Init_sig_checked() local_unnamed_addr #9 { -entry: - %locals.i22.i = alloca i64, align 8 - %locals.i20.i = alloca i64, i32 0, align 8 - %locals.i18.i = alloca i64, i32 0, align 8 - %locals.i.i = alloca i64, i32 0, align 8 - %keywords.i = alloca i64, align 8, !dbg !41 - %realpath = tail call i64 @sorbet_readRealpath() - %0 = bitcast i64* %keywords.i to i8* - call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %0) - %1 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([17 x i8], [17 x i8]* @"str_", i64 0, i64 0), i64 noundef 16) #16 - store i64 %1, i64* @"rubyIdPrecomputed_", align 8 - %2 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([4 x i8], [4 x i8]* @str_new, i64 0, i64 0), i64 noundef 3) #16 - store i64 %2, i64* @rubyIdPrecomputed_new, align 8 - %3 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([2 x i8], [2 x i8]* @"str_<", i64 0, i64 0), i64 noundef 1) #16 - store i64 %3, i64* @"rubyIdPrecomputed_<", align 8 - %4 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([4 x i8], [4 x i8]* @str_foo, i64 0, i64 0), i64 noundef 3) #16 - store i64 %4, i64* @rubyIdPrecomputed_foo, align 8 - %5 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([2 x i8], [2 x i8]* @"str_+", i64 0, i64 0), i64 noundef 1) #16 - store i64 %5, i64* @"rubyIdPrecomputed_+", align 8 - %6 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([5 x i8], [5 x i8]* @str_puts, i64 0, i64 0), i64 noundef 4) #16 - store i64 %6, i64* @rubyIdPrecomputed_puts, align 8 - %7 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([11 x i8], [11 x i8]* @str_initialize, i64 0, i64 0), i64 noundef 10) #16 - store i64 %7, i64* @rubyIdPrecomputed_initialize, align 8 - %8 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([5 x i8], [5 x i8]* @"str_@foo", i64 0, i64 0), i64 noundef 4) #16 - store i64 %8, i64* @"rubyIdPrecomputed_@foo", align 8 - %9 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([29 x i8], [29 x i8]* @"str_", i64 0, i64 0), i64 noundef 28) #16 - store i64 %9, i64* @"rubyIdPrecomputed_", align 8 - %10 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([13 x i8], [13 x i8]* @"str_", i64 0, i64 0), i64 noundef 12) #16 - store i64 %10, i64* @"rubyIdPrecomputed_", align 8 - %11 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([38 x i8], [38 x i8]* @"str_block in ", i64 0, i64 0), i64 noundef 37) #16 - store i64 %11, i64* @"rubyIdPrecomputed_block in ", align 8 - %12 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([7 x i8], [7 x i8]* @str_params, i64 0, i64 0), i64 noundef 6) #16 - store i64 %12, i64* @rubyIdPrecomputed_params, align 8 - %13 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([5 x i8], [5 x i8]* @str_void, i64 0, i64 0), i64 noundef 4) #16 - store i64 %13, i64* @rubyIdPrecomputed_void, align 8 - %14 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([8 x i8], [8 x i8]* @str_returns, i64 0, i64 0), i64 noundef 7) #16 - store i64 %14, i64* @rubyIdPrecomputed_returns, align 8 - %15 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([7 x i8], [7 x i8]* @str_extend, i64 0, i64 0), i64 noundef 6) #16 - store i64 %15, i64* @rubyIdPrecomputed_extend, align 8 - %16 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([7 x i8], [7 x i8]* @str_normal, i64 0, i64 0), i64 noundef 6) #16 - %17 = tail call i64 @sorbet_vm_fstring_new(i8* noundef getelementptr inbounds ([17 x i8], [17 x i8]* @"str_", i64 0, i64 0), i64 noundef 16) #16 - tail call void @rb_gc_register_mark_object(i64 %17) #16 - store i64 %17, i64* @"rubyStrFrozen_", align 8 - %18 = tail call i64 @sorbet_vm_fstring_new(i8* noundef getelementptr inbounds ([63 x i8], [63 x i8]* @"str_test/testdata/ruby_benchmark/stripe/attr_reader/sig_checked.rb", i64 0, i64 0), i64 noundef 62) #16 - tail call void @rb_gc_register_mark_object(i64 %18) #16 - store i64 %18, i64* @"rubyStrFrozen_test/testdata/ruby_benchmark/stripe/attr_reader/sig_checked.rb", align 8 - tail call void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i32 0, i32 0), i32 noundef 28) - %"rubyId_.i.i" = load i64, i64* @"rubyIdPrecomputed_", align 8 - %"rubyStr_.i.i" = load i64, i64* @"rubyStrFrozen_", align 8 - %"rubyStr_test/testdata/ruby_benchmark/stripe/attr_reader/sig_checked.rb.i.i" = load i64, i64* @"rubyStrFrozen_test/testdata/ruby_benchmark/stripe/attr_reader/sig_checked.rb", align 8 - %19 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_.i.i", i64 %"rubyId_.i.i", i64 %"rubyStr_test/testdata/ruby_benchmark/stripe/attr_reader/sig_checked.rb.i.i", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 0, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals.i.i, i32 noundef 0, i32 noundef 2) - store %struct.rb_iseq_struct* %19, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.17$152", align 8 - %rubyId_new.i = load i64, i64* @rubyIdPrecomputed_new, align 8, !dbg !43 - call void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_new, i64 %rubyId_new.i, i32 noundef 16, i32 noundef 1, i32 noundef 0, i64* noundef null), !dbg !43 - %"rubyId_<.i" = load i64, i64* @"rubyIdPrecomputed_<", align 8, !dbg !45 - call void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @"ic_<", i64 %"rubyId_<.i", i32 noundef 16, i32 noundef 1, i32 noundef 0, i64* noundef null), !dbg !45 - %rubyId_foo.i = load i64, i64* @rubyIdPrecomputed_foo, align 8, !dbg !46 - call void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_foo, i64 %rubyId_foo.i, i32 noundef 16, i32 noundef 0, i32 noundef 0, i64* noundef null), !dbg !46 - %"rubyId_+.i" = load i64, i64* @"rubyIdPrecomputed_+", align 8, !dbg !47 - call void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @"ic_+", i64 %"rubyId_+.i", i32 noundef 16, i32 noundef 1, i32 noundef 0, i64* noundef null), !dbg !47 - %rubyId_puts.i = load i64, i64* @rubyIdPrecomputed_puts, align 8, !dbg !48 - call void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts, i64 %rubyId_puts.i, i32 noundef 20, i32 noundef 1, i32 noundef 0, i64* noundef null), !dbg !48 - %rubyId_foo5.i = load i64, i64* @rubyIdPrecomputed_foo, align 8, !dbg !49 - call void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_foo.1, i64 %rubyId_foo5.i, i32 noundef 16, i32 noundef 0, i32 noundef 0, i64* noundef null), !dbg !49 - %rubyId_puts7.i = load i64, i64* @rubyIdPrecomputed_puts, align 8, !dbg !50 - call void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts.2, i64 %rubyId_puts7.i, i32 noundef 20, i32 noundef 1, i32 noundef 0, i64* noundef null), !dbg !50 - %20 = call i64 @sorbet_vm_fstring_new(i8* noundef getelementptr inbounds ([11 x i8], [11 x i8]* @str_initialize, i64 0, i64 0), i64 noundef 10) #16 - call void @rb_gc_register_mark_object(i64 %20) #16 - %rubyId_initialize.i.i = load i64, i64* @rubyIdPrecomputed_initialize, align 8 - %"rubyStr_test/testdata/ruby_benchmark/stripe/attr_reader/sig_checked.rb.i17.i" = load i64, i64* @"rubyStrFrozen_test/testdata/ruby_benchmark/stripe/attr_reader/sig_checked.rb", align 8 - %21 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %20, i64 %rubyId_initialize.i.i, i64 %"rubyStr_test/testdata/ruby_benchmark/stripe/attr_reader/sig_checked.rb.i17.i", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 1, i32 noundef 8, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals.i18.i, i32 noundef 0, i32 noundef 0) - store %struct.rb_iseq_struct* %21, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_AttrReaderSigChecked#10initialize", align 8 - %22 = call i64 @sorbet_getConstant(i8* noundef getelementptr inbounds ([30 x i8], [30 x i8]* @sorbet_getVoidSingleton.name, i64 0, i64 0), i64 noundef 30) #16 - store i64 %22, i64* @"", align 8 - %23 = call i64 @sorbet_vm_fstring_new(i8* noundef getelementptr inbounds ([4 x i8], [4 x i8]* @str_foo, i64 0, i64 0), i64 noundef 3) #16 - call void @rb_gc_register_mark_object(i64 %23) #16 - %rubyId_foo.i.i = load i64, i64* @rubyIdPrecomputed_foo, align 8 - %"rubyStr_test/testdata/ruby_benchmark/stripe/attr_reader/sig_checked.rb.i19.i" = load i64, i64* @"rubyStrFrozen_test/testdata/ruby_benchmark/stripe/attr_reader/sig_checked.rb", align 8 - %24 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %23, i64 %rubyId_foo.i.i, i64 %"rubyStr_test/testdata/ruby_benchmark/stripe/attr_reader/sig_checked.rb.i19.i", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 1, i32 noundef 13, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals.i20.i, i32 noundef 0, i32 noundef 0) - store %struct.rb_iseq_struct* %24, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_AttrReaderSigChecked#3foo", align 8 - %25 = call i64 @sorbet_vm_fstring_new(i8* noundef getelementptr inbounds ([29 x i8], [29 x i8]* @"str_", i64 0, i64 0), i64 noundef 28) #16 - call void @rb_gc_register_mark_object(i64 %25) #16 - %26 = bitcast i64* %locals.i22.i to i8* - call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %26) - %"rubyId_.i.i" = load i64, i64* @"rubyIdPrecomputed_", align 8 - %"rubyStr_test/testdata/ruby_benchmark/stripe/attr_reader/sig_checked.rb.i21.i" = load i64, i64* @"rubyStrFrozen_test/testdata/ruby_benchmark/stripe/attr_reader/sig_checked.rb", align 8 - %"rubyId_.i.i" = load i64, i64* @"rubyIdPrecomputed_", align 8 - store i64 %"rubyId_.i.i", i64* %locals.i22.i, align 8 - %27 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %25, i64 %"rubyId_.i.i", i64 %"rubyStr_test/testdata/ruby_benchmark/stripe/attr_reader/sig_checked.rb.i21.i", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 3, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull align 8 %locals.i22.i, i32 noundef 1, i32 noundef 4) - store %struct.rb_iseq_struct* %27, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_AttrReaderSigChecked.13", align 8 - call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %26) - %28 = call i64 @sorbet_vm_fstring_new(i8* noundef getelementptr inbounds ([38 x i8], [38 x i8]* @"str_block in ", i64 0, i64 0), i64 noundef 37) #16 - call void @rb_gc_register_mark_object(i64 %28) #16 - store i64 %28, i64* @"rubyStrFrozen_block in ", align 8 - %stackFrame.i.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_AttrReaderSigChecked.13", align 8 - %"rubyId_block in .i.i" = load i64, i64* @"rubyIdPrecomputed_block in ", align 8 - %"rubyStr_test/testdata/ruby_benchmark/stripe/attr_reader/sig_checked.rb.i23.i" = load i64, i64* @"rubyStrFrozen_test/testdata/ruby_benchmark/stripe/attr_reader/sig_checked.rb", align 8 - %29 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %28, i64 %"rubyId_block in .i.i", i64 %"rubyStr_test/testdata/ruby_benchmark/stripe/attr_reader/sig_checked.rb.i23.i", i64 %realpath, %struct.rb_iseq_struct* %stackFrame.i.i, i32 noundef 2, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef null, i32 noundef 0, i32 noundef 4) - store %struct.rb_iseq_struct* %29, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_AttrReaderSigChecked.13$block_1", align 8 - %stackFrame.i24.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_AttrReaderSigChecked.13", align 8 - %"rubyId_block in .i25.i" = load i64, i64* @"rubyIdPrecomputed_block in ", align 8 - %"rubyStr_block in .i26.i" = load i64, i64* @"rubyStrFrozen_block in ", align 8 - %"rubyStr_test/testdata/ruby_benchmark/stripe/attr_reader/sig_checked.rb.i27.i" = load i64, i64* @"rubyStrFrozen_test/testdata/ruby_benchmark/stripe/attr_reader/sig_checked.rb", align 8 - %30 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_block in .i26.i", i64 %"rubyId_block in .i25.i", i64 %"rubyStr_test/testdata/ruby_benchmark/stripe/attr_reader/sig_checked.rb.i27.i", i64 %realpath, %struct.rb_iseq_struct* %stackFrame.i24.i, i32 noundef 2, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef null, i32 noundef 0, i32 noundef 4) - store %struct.rb_iseq_struct* %30, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_AttrReaderSigChecked.13$block_2", align 8 - %rubyId_params.i = load i64, i64* @rubyIdPrecomputed_params, align 8, !dbg !41 - %rubyId_foo10.i = load i64, i64* @rubyIdPrecomputed_foo, align 8, !dbg !41 - %31 = call i64 @rb_id2sym(i64 %rubyId_foo10.i) #18, !dbg !41 - store i64 %31, i64* %keywords.i, align 8, !dbg !41 - call void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_params, i64 %rubyId_params.i, i32 noundef 68, i32 noundef 1, i32 noundef 1, i64* noundef nonnull align 8 %keywords.i), !dbg !41 - %rubyId_void.i = load i64, i64* @rubyIdPrecomputed_void, align 8, !dbg !41 - call void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_void, i64 %rubyId_void.i, i32 noundef 16, i32 noundef 0, i32 noundef 0, i64* noundef null), !dbg !41 - %rubyId_returns.i = load i64, i64* @rubyIdPrecomputed_returns, align 8, !dbg !51 - call void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_returns, i64 %rubyId_returns.i, i32 noundef 20, i32 noundef 1, i32 noundef 0, i64* noundef null), !dbg !51 - %rubyId_extend.i = load i64, i64* @rubyIdPrecomputed_extend, align 8, !dbg !30 - call void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_extend, i64 %rubyId_extend.i, i32 noundef 20, i32 noundef 1, i32 noundef 0, i64* noundef null), !dbg !30 - call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %0) - %32 = load %struct.rb_vm_struct*, %struct.rb_vm_struct** @ruby_current_vm_ptr, align 8, !tbaa !14 - %33 = getelementptr inbounds %struct.rb_vm_struct, %struct.rb_vm_struct* %32, i64 0, i32 18 - %34 = load i64, i64* %33, align 8, !tbaa !53 - %35 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !14 - %36 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %35, i64 0, i32 2 - %37 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %36, align 8, !tbaa !16 - %stackFrame.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.17$152", align 8 - %38 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %37, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame.i, %struct.rb_iseq_struct** %38, align 8, !tbaa !20 - %39 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %37, i64 0, i32 4 - %40 = load i64*, i64** %39, align 8, !tbaa !22 - %41 = load i64, i64* %40, align 8, !tbaa !6 - %42 = and i64 %41, -33 - store i64 %42, i64* %40, align 8, !tbaa !6 - call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %35, %struct.rb_control_frame_struct* %37, %struct.rb_iseq_struct* %stackFrame.i) #16 - %43 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %37, i64 0, i32 0 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %43, align 8, !dbg !61, !tbaa !14 - %44 = load i64, i64* @rb_cObject, align 8, !dbg !62 - %45 = call i64 @rb_define_class(i8* noundef getelementptr inbounds ([21 x i8], [21 x i8]* @str_AttrReaderSigChecked, i64 0, i64 0), i64 %44) #16, !dbg !62 - %46 = call %struct.rb_control_frame_struct* @sorbet_pushStaticInitFrame(i64 %45) #16, !dbg !62 - call fastcc void @"func_AttrReaderSigChecked.13L62"(i64 %45, %struct.rb_control_frame_struct* %46) #16, !dbg !62 - call void @sorbet_popFrame() #16, !dbg !62 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 16), i64** %43, align 8, !dbg !62, !tbaa !14 - %47 = load i64, i64* @guard_epoch_AttrReaderSigChecked, align 8, !dbg !43 - %48 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !43, !tbaa !31 - %needTakeSlowPath = icmp ne i64 %47, %48, !dbg !43 - br i1 %needTakeSlowPath, label %49, label %50, !dbg !43, !prof !33 - -49: ; preds = %entry - call void @const_recompute_AttrReaderSigChecked(), !dbg !43 - br label %50, !dbg !43 - -50: ; preds = %entry, %49 - %51 = load i64, i64* @guarded_const_AttrReaderSigChecked, align 8, !dbg !43 - %52 = load i64, i64* @guard_epoch_AttrReaderSigChecked, align 8, !dbg !43 - %53 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !43, !tbaa !31 - %guardUpdated = icmp eq i64 %52, %53, !dbg !43 - call void @llvm.assume(i1 %guardUpdated), !dbg !43 - %54 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %37, i64 0, i32 1, !dbg !43 - %55 = load i64*, i64** %54, align 8, !dbg !43 - store i64 %51, i64* %55, align 8, !dbg !43, !tbaa !6 - %56 = getelementptr inbounds i64, i64* %55, i64 1, !dbg !43 - store i64 2497, i64* %56, align 8, !dbg !43, !tbaa !6 - %57 = getelementptr inbounds i64, i64* %56, i64 1, !dbg !43 - store i64* %57, i64** %54, align 8, !dbg !43 - %send = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_new, i64 0), !dbg !43 - br label %BB2.i, !dbg !63 - -BB2.i: ; preds = %BB2.i.backedge, %50 - %i.sroa.0.0.i = phi i64 [ 1, %50 ], [ %i.sroa.0.0.i.be, %BB2.i.backedge ], !dbg !61 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 19), i64** %43, align 8, !tbaa !14 - %58 = and i64 %i.sroa.0.0.i, 1, !dbg !45 - %59 = icmp eq i64 %58, 0, !dbg !45 - br i1 %59, label %60, label %"fastSymCallIntrinsic_Integer_<.i", !dbg !45, !prof !64 - -60: ; preds = %BB2.i - %61 = and i64 %i.sroa.0.0.i, 7, !dbg !45 - %62 = icmp ne i64 %61, 0, !dbg !45 - %63 = and i64 %i.sroa.0.0.i, -9, !dbg !45 - %64 = icmp eq i64 %63, 0, !dbg !45 - %65 = or i1 %62, %64, !dbg !45 - br i1 %65, label %"alternativeCallIntrinsic_Integer_<.i", label %sorbet_isa_Integer.exit, !dbg !45, !prof !65 - -sorbet_isa_Integer.exit: ; preds = %60 - %66 = inttoptr i64 %i.sroa.0.0.i to %struct.iseq_inline_iv_cache_entry*, !dbg !45 - %67 = getelementptr inbounds %struct.iseq_inline_iv_cache_entry, %struct.iseq_inline_iv_cache_entry* %66, i64 0, i32 0, !dbg !45 - %68 = load i64, i64* %67, align 8, !dbg !45, !tbaa !66 - %69 = and i64 %68, 31, !dbg !45 - %70 = icmp eq i64 %69, 10, !dbg !45 - br i1 %70, label %"fastSymCallIntrinsic_Integer_<.i", label %"alternativeCallIntrinsic_Integer_<.i", !dbg !45, !prof !27 - -BB5.i: ; preds = %afterSend37.i - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 21), i64** %43, align 8, !tbaa !14 - %71 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %37, i64 0, i32 1, !dbg !46 - %72 = load i64*, i64** %71, align 8, !dbg !46 - store i64 %send, i64* %72, align 8, !dbg !46, !tbaa !6 - %73 = getelementptr inbounds i64, i64* %72, i64 1, !dbg !46 - store i64* %73, i64** %71, align 8, !dbg !46 - %send2 = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_foo, i64 0), !dbg !46 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 23), i64** %43, align 8, !dbg !46, !tbaa !14 - br i1 %74, label %"fastSymCallIntrinsic_Integer_+.i", label %"alternativeCallIntrinsic_Integer_+.i", !dbg !47 - -afterSend37.i: ; preds = %99, %sorbet_rb_int_lt.exit.i, %"alternativeCallIntrinsic_Integer_<.i" - %74 = phi i1 [ %77, %"alternativeCallIntrinsic_Integer_<.i" ], [ %82, %sorbet_rb_int_lt.exit.i ], [ %82, %99 ] - %"symIntrinsicRawPhi_<.i" = phi i64 [ %send4, %"alternativeCallIntrinsic_Integer_<.i" ], [ %rawSendResult89.i, %sorbet_rb_int_lt.exit.i ], [ %rawSendResult89.i, %99 ], !dbg !45 - %75 = and i64 %"symIntrinsicRawPhi_<.i", -9, !dbg !45 - %76 = icmp ne i64 %75, 0, !dbg !45 - br i1 %76, label %BB5.i, label %"func_.17$152.exit", !dbg !45 - -"alternativeCallIntrinsic_Integer_<.i": ; preds = %60, %sorbet_isa_Integer.exit - %77 = phi i1 [ %70, %sorbet_isa_Integer.exit ], [ false, %60 ] - %78 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %37, i64 0, i32 1, !dbg !45 - %79 = load i64*, i64** %78, align 8, !dbg !45 - store i64 %i.sroa.0.0.i, i64* %79, align 8, !dbg !45, !tbaa !6 - %80 = getelementptr inbounds i64, i64* %79, i64 1, !dbg !45 - store i64 20000001, i64* %80, align 8, !dbg !45, !tbaa !6 - %81 = getelementptr inbounds i64, i64* %80, i64 1, !dbg !45 - store i64* %81, i64** %78, align 8, !dbg !45 - %send4 = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @"ic_<", i64 0), !dbg !45 - br label %afterSend37.i, !dbg !45 - -"fastSymCallIntrinsic_Integer_<.i": ; preds = %BB2.i, %sorbet_isa_Integer.exit - %82 = phi i1 [ %70, %sorbet_isa_Integer.exit ], [ true, %BB2.i ] - call void @llvm.experimental.noalias.scope.decl(metadata !68) #16, !dbg !45 - %83 = and i64 %i.sroa.0.0.i, 1, !dbg !45 - %84 = icmp eq i64 %83, 0, !dbg !45 - br i1 %84, label %89, label %85, !dbg !45, !prof !71 - -85: ; preds = %"fastSymCallIntrinsic_Integer_<.i" - %86 = ashr i64 %i.sroa.0.0.i, 1, !dbg !45 - %87 = icmp slt i64 %86, 10000000, !dbg !45 - %88 = select i1 %87, i64 20, i64 0, !dbg !45 - br label %sorbet_rb_int_lt.exit.i, !dbg !45 - -89: ; preds = %"fastSymCallIntrinsic_Integer_<.i" - %90 = call i64 @sorbet_rb_int_lt_slowpath(i64 %i.sroa.0.0.i, i64 noundef 20000001) #16, !dbg !45, !noalias !68 - br label %sorbet_rb_int_lt.exit.i, !dbg !45 - -sorbet_rb_int_lt.exit.i: ; preds = %89, %85 - %rawSendResult89.i = phi i64 [ %88, %85 ], [ %90, %89 ] - %91 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !45, !tbaa !14 - %92 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %91, i64 0, i32 5, !dbg !45 - %93 = load i32, i32* %92, align 8, !dbg !45, !tbaa !25 - %94 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %91, i64 0, i32 6, !dbg !45 - %95 = load i32, i32* %94, align 4, !dbg !45, !tbaa !26 - %96 = xor i32 %95, -1, !dbg !45 - %97 = and i32 %96, %93, !dbg !45 - %98 = icmp eq i32 %97, 0, !dbg !45 - br i1 %98, label %afterSend37.i, label %99, !dbg !45, !prof !27 - -99: ; preds = %sorbet_rb_int_lt.exit.i - %100 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %91, i64 0, i32 8, !dbg !45 - %101 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %100, align 8, !dbg !45, !tbaa !28 - %102 = call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %101, i32 noundef 0) #16, !dbg !45 - br label %afterSend37.i, !dbg !45 - -"alternativeCallIntrinsic_Integer_+.i": ; preds = %BB5.i - %103 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %37, i64 0, i32 1, !dbg !47 - %104 = load i64*, i64** %103, align 8, !dbg !47 - store i64 %i.sroa.0.0.i, i64* %104, align 8, !dbg !47, !tbaa !6 - %105 = getelementptr inbounds i64, i64* %104, i64 1, !dbg !47 - store i64 3, i64* %105, align 8, !dbg !47, !tbaa !6 - %106 = getelementptr inbounds i64, i64* %105, i64 1, !dbg !47 - store i64* %106, i64** %103, align 8, !dbg !47 - %send6 = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @"ic_+", i64 0), !dbg !47 - br label %BB2.i.backedge, !dbg !47 - -"fastSymCallIntrinsic_Integer_+.i": ; preds = %BB5.i - call void @llvm.experimental.noalias.scope.decl(metadata !72) #16, !dbg !47 - %107 = and i64 %i.sroa.0.0.i, 1, !dbg !47 - %108 = icmp eq i64 %107, 0, !dbg !47 - br i1 %108, label %117, label %109, !dbg !47, !prof !71 - -109: ; preds = %"fastSymCallIntrinsic_Integer_+.i" - %110 = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %i.sroa.0.0.i, i64 noundef 2) #19, !dbg !47 - %111 = extractvalue { i64, i1 } %110, 1, !dbg !47 - %112 = extractvalue { i64, i1 } %110, 0, !dbg !47 - br i1 %111, label %113, label %sorbet_rb_int_plus.exit.i, !dbg !47 - -113: ; preds = %109 - %114 = ashr i64 %112, 1, !dbg !47 - %115 = xor i64 %114, -9223372036854775808, !dbg !47 - %116 = call i64 @rb_int2big(i64 %115) #16, !dbg !47 - br label %sorbet_rb_int_plus.exit.i, !dbg !47 - -117: ; preds = %"fastSymCallIntrinsic_Integer_+.i" - %118 = call i64 @sorbet_rb_int_plus_slowpath(i64 %i.sroa.0.0.i, i64 noundef 3) #16, !dbg !47, !noalias !72 - br label %sorbet_rb_int_plus.exit.i, !dbg !47 - -sorbet_rb_int_plus.exit.i: ; preds = %117, %113, %109 - %119 = phi i64 [ %118, %117 ], [ %116, %113 ], [ %112, %109 ], !dbg !47 - %120 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !47, !tbaa !14 - %121 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %120, i64 0, i32 5, !dbg !47 - %122 = load i32, i32* %121, align 8, !dbg !47, !tbaa !25 - %123 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %120, i64 0, i32 6, !dbg !47 - %124 = load i32, i32* %123, align 4, !dbg !47, !tbaa !26 - %125 = xor i32 %124, -1, !dbg !47 - %126 = and i32 %125, %122, !dbg !47 - %127 = icmp eq i32 %126, 0, !dbg !47 - br i1 %127, label %BB2.i.backedge, label %128, !dbg !47, !prof !27 - -128: ; preds = %sorbet_rb_int_plus.exit.i - %129 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %120, i64 0, i32 8, !dbg !47 - %130 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %129, align 8, !dbg !47, !tbaa !28 - %131 = call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %130, i32 noundef 0) #16, !dbg !47 - br label %BB2.i.backedge, !dbg !47 - -BB2.i.backedge: ; preds = %128, %sorbet_rb_int_plus.exit.i, %"alternativeCallIntrinsic_Integer_+.i" - %i.sroa.0.0.i.be = phi i64 [ %send6, %"alternativeCallIntrinsic_Integer_+.i" ], [ %119, %sorbet_rb_int_plus.exit.i ], [ %119, %128 ] - br label %BB2.i - -"func_.17$152.exit": ; preds = %afterSend37.i - %i.sroa.0.0.i.lcssa = phi i64 [ %i.sroa.0.0.i, %afterSend37.i ], !dbg !61 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 26), i64** %43, align 8, !tbaa !14 - %132 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %37, i64 0, i32 1, !dbg !48 - %133 = load i64*, i64** %132, align 8, !dbg !48 - store i64 %34, i64* %133, align 8, !dbg !48, !tbaa !6 - %134 = getelementptr inbounds i64, i64* %133, i64 1, !dbg !48 - store i64 %i.sroa.0.0.i.lcssa, i64* %134, align 8, !dbg !48, !tbaa !6 - %135 = getelementptr inbounds i64, i64* %134, i64 1, !dbg !48 - store i64* %135, i64** %132, align 8, !dbg !48 - %send8 = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts, i64 0), !dbg !48 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 27), i64** %43, align 8, !dbg !48, !tbaa !14 - %136 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %37, i64 0, i32 1, !dbg !49 - %137 = load i64*, i64** %136, align 8, !dbg !49 - store i64 %send, i64* %137, align 8, !dbg !49, !tbaa !6 - %138 = getelementptr inbounds i64, i64* %137, i64 1, !dbg !49 - store i64* %138, i64** %136, align 8, !dbg !49 - %send10 = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_foo.1, i64 0), !dbg !49 - %139 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %37, i64 0, i32 1, !dbg !50 - %140 = load i64*, i64** %139, align 8, !dbg !50 - store i64 %34, i64* %140, align 8, !dbg !50, !tbaa !6 - %141 = getelementptr inbounds i64, i64* %140, i64 1, !dbg !50 - store i64 %send10, i64* %141, align 8, !dbg !50, !tbaa !6 - %142 = getelementptr inbounds i64, i64* %141, i64 1, !dbg !50 - store i64* %142, i64** %139, align 8, !dbg !50 - %send12 = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts.2, i64 0), !dbg !50 - ret void -} - -; Function Attrs: nounwind sspreq uwtable -define i64 @"func_AttrReaderSigChecked#10initialize"(i32 %argc, i64* nocapture readonly %argArray, i64 %selfRaw, %struct.rb_control_frame_struct* nocapture nonnull writeonly align 8 dereferenceable(8) %cfp, i8* nocapture nofree readnone %0) #8 !dbg !75 { -functionEntryInitializers: - %1 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 0 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 8), i64** %1, align 8, !tbaa !14 - %tooManyArgs = icmp ugt i32 %argc, 1, !dbg !76 - %tooFewArgs = icmp ult i32 %argc, 1, !dbg !76 - %or.cond = or i1 %tooManyArgs, %tooFewArgs, !dbg !76 - br i1 %or.cond, label %argCountFailBlock, label %fillRequiredArgs, !dbg !76, !prof !71 - -argCountFailBlock: ; preds = %functionEntryInitializers - tail call void @sorbet_raiseArity(i32 %argc, i32 noundef 1, i32 noundef 1) #20, !dbg !76 - unreachable, !dbg !76 - -fillRequiredArgs: ; preds = %functionEntryInitializers - %rawArg_foo = load i64, i64* %argArray, align 8, !dbg !76 - %2 = and i64 %rawArg_foo, 1, !dbg !77 - %3 = icmp eq i64 %2, 0, !dbg !77 - br i1 %3, label %4, label %typeTestSuccess, !dbg !77, !prof !64 - -4: ; preds = %fillRequiredArgs - %5 = and i64 %rawArg_foo, 7, !dbg !77 - %6 = icmp ne i64 %5, 0, !dbg !77 - %7 = and i64 %rawArg_foo, -9, !dbg !77 - %8 = icmp eq i64 %7, 0, !dbg !77 - %9 = or i1 %6, %8, !dbg !77 - br i1 %9, label %codeRepl, label %sorbet_isa_Integer.exit, !dbg !77 - -sorbet_isa_Integer.exit: ; preds = %4 - %10 = inttoptr i64 %rawArg_foo to %struct.iseq_inline_iv_cache_entry*, !dbg !77 - %11 = getelementptr inbounds %struct.iseq_inline_iv_cache_entry, %struct.iseq_inline_iv_cache_entry* %10, i64 0, i32 0, !dbg !77 - %12 = load i64, i64* %11, align 8, !dbg !77, !tbaa !66 - %13 = and i64 %12, 31, !dbg !77 - %14 = icmp eq i64 %13, 10, !dbg !77 - br i1 %14, label %typeTestSuccess, label %codeRepl, !dbg !77, !prof !27 - -typeTestSuccess: ; preds = %fillRequiredArgs, %sorbet_isa_Integer.exit - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 9), i64** %1, align 8, !dbg !77, !tbaa !14 - %"rubyId_@foo" = load i64, i64* @"rubyIdPrecomputed_@foo", align 8, !dbg !78 - tail call void @sorbet_vm_setivar(i64 %selfRaw, i64 %"rubyId_@foo", i64 %rawArg_foo, %struct.iseq_inline_iv_cache_entry* noundef @"ivc_@foo") #16, !dbg !78 - %"rubyId_@foo13" = load i64, i64* @"rubyIdPrecomputed_@foo", align 8, !dbg !79 - %15 = tail call i64 @sorbet_vm_getivar(i64 %selfRaw, i64 %"rubyId_@foo13", %struct.iseq_inline_iv_cache_entry* noundef @"ivc_@foo.3") #16, !dbg !79 - %"" = load i64, i64* @"", align 8 - ret i64 %"" - -codeRepl: ; preds = %4, %sorbet_isa_Integer.exit - tail call fastcc void @"func_AttrReaderSigChecked#10initialize.cold.1"(i64 %rawArg_foo) #21, !dbg !77 - unreachable -} - -; Function Attrs: nounwind sspreq uwtable -define i64 @"func_AttrReaderSigChecked#3foo"(i32 %argc, i64* nocapture nofree readnone %argArray, i64 %selfRaw, %struct.rb_control_frame_struct* nocapture nonnull writeonly align 8 dereferenceable(8) %cfp, i8* nocapture nofree readnone %0) #8 !dbg !80 { -functionEntryInitializers: - %1 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 0 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 13), i64** %1, align 8, !tbaa !14 - %tooManyArgs = icmp ugt i32 %argc, 0, !dbg !81 - br i1 %tooManyArgs, label %argCountFailBlock, label %fillRequiredArgs, !dbg !81, !prof !64 - -argCountFailBlock: ; preds = %functionEntryInitializers - tail call void @sorbet_raiseArity(i32 %argc, i32 noundef 0, i32 noundef 0) #20, !dbg !81 - unreachable, !dbg !81 - -fillRequiredArgs: ; preds = %functionEntryInitializers - %"rubyId_@foo" = load i64, i64* @"rubyIdPrecomputed_@foo", align 8, !dbg !82 - %2 = tail call i64 @sorbet_vm_getivar(i64 %selfRaw, i64 %"rubyId_@foo", %struct.iseq_inline_iv_cache_entry* noundef @"ivc_@foo.4") #16, !dbg !82 - %3 = and i64 %2, 1 - %4 = icmp eq i64 %3, 0 - br i1 %4, label %5, label %typeTestSuccess, !prof !64 - -5: ; preds = %fillRequiredArgs - %6 = and i64 %2, 7 - %7 = icmp ne i64 %6, 0 - %8 = and i64 %2, -9 - %9 = icmp eq i64 %8, 0 - %10 = or i1 %7, %9 - br i1 %10, label %codeRepl, label %sorbet_isa_Integer.exit - -sorbet_isa_Integer.exit: ; preds = %5 - %11 = inttoptr i64 %2 to %struct.iseq_inline_iv_cache_entry* - %12 = getelementptr inbounds %struct.iseq_inline_iv_cache_entry, %struct.iseq_inline_iv_cache_entry* %11, i64 0, i32 0 - %13 = load i64, i64* %12, align 8, !tbaa !66 - %14 = and i64 %13, 31 - %15 = icmp eq i64 %14, 10 - br i1 %15, label %typeTestSuccess, label %codeRepl, !prof !27 - -typeTestSuccess: ; preds = %fillRequiredArgs, %sorbet_isa_Integer.exit - ret i64 %2 - -codeRepl: ; preds = %5, %sorbet_isa_Integer.exit - tail call fastcc void @"func_AttrReaderSigChecked#3foo.cold.1"(i64 %2) #21, !dbg !83 - unreachable -} - -; Function Attrs: ssp -define internal i64 @"func_AttrReaderSigChecked.13L62$block_1"(i64 %firstYieldArgRaw, i64 %localsOffset, i32 %argc, i64* nocapture nofree readnone %argArray, i64 %blockArg) #10 !dbg !42 { -functionEntryInitializers: - %0 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !14 - %1 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %0, i64 0, i32 2 - %2 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %1, align 8, !tbaa !16 - %3 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 3 - %4 = load i64, i64* %3, align 8, !tbaa !84 - %stackFrame = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_AttrReaderSigChecked.13$block_1", align 8 - %5 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame, %struct.rb_iseq_struct** %5, align 8, !tbaa !20 - %6 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 4 - %7 = load i64*, i64** %6, align 8, !tbaa !22 - %8 = load i64, i64* %7, align 8, !tbaa !6 - %9 = and i64 %8, -129 - store i64 %9, i64* %7, align 8, !tbaa !6 - %10 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 0 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 7), i64** %10, align 8, !tbaa !14 - %11 = load i64, i64* @rb_cInteger, align 8, !dbg !41 - %12 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 1, !dbg !41 - %13 = load i64*, i64** %12, align 8, !dbg !41 - store i64 %4, i64* %13, align 8, !dbg !41, !tbaa !6 - %14 = getelementptr inbounds i64, i64* %13, i64 1, !dbg !41 - store i64 %11, i64* %14, align 8, !dbg !41, !tbaa !6 - %15 = getelementptr inbounds i64, i64* %14, i64 1, !dbg !41 - store i64* %15, i64** %12, align 8, !dbg !41 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_params, i64 0), !dbg !41 - %16 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 1, !dbg !41 - %17 = load i64*, i64** %16, align 8, !dbg !41 - store i64 %send, i64* %17, align 8, !dbg !41, !tbaa !6 - %18 = getelementptr inbounds i64, i64* %17, i64 1, !dbg !41 - store i64* %18, i64** %16, align 8, !dbg !41 - %send16 = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_void, i64 0), !dbg !41 - ret i64 %send16, !dbg !85 -} - -; Function Attrs: ssp -define internal i64 @"func_AttrReaderSigChecked.13L62$block_2"(i64 %firstYieldArgRaw, i64 %localsOffset, i32 %argc, i64* nocapture nofree readnone %argArray, i64 %blockArg) #10 !dbg !52 { -functionEntryInitializers: - %0 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !14 - %1 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %0, i64 0, i32 2 - %2 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %1, align 8, !tbaa !16 - %3 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 3 - %4 = load i64, i64* %3, align 8, !tbaa !84 - %stackFrame = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_AttrReaderSigChecked.13$block_2", align 8 - %5 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame, %struct.rb_iseq_struct** %5, align 8, !tbaa !20 - %6 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 4 - %7 = load i64*, i64** %6, align 8, !tbaa !22 - %8 = load i64, i64* %7, align 8, !tbaa !6 - %9 = and i64 %8, -129 - store i64 %9, i64* %7, align 8, !tbaa !6 - %10 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 0 - store i64* getelementptr inbounds ([28 x i64], [28 x i64]* @iseqEncodedArray, i64 0, i64 12), i64** %10, align 8, !tbaa !14 - %11 = load i64, i64* @rb_cInteger, align 8, !dbg !51 - %12 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 1, !dbg !51 - %13 = load i64*, i64** %12, align 8, !dbg !51 - store i64 %4, i64* %13, align 8, !dbg !51, !tbaa !6 - %14 = getelementptr inbounds i64, i64* %13, i64 1, !dbg !51 - store i64 %11, i64* %14, align 8, !dbg !51, !tbaa !6 - %15 = getelementptr inbounds i64, i64* %14, i64 1, !dbg !51 - store i64* %15, i64** %12, align 8, !dbg !51 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_returns, i64 0), !dbg !51 - ret i64 %send, !dbg !86 -} - -; Function Attrs: inaccessiblememonly nofree nosync nounwind willreturn -declare void @llvm.experimental.noalias.scope.decl(metadata) #11 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #6 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #6 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly -declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #12 - -; Function Attrs: cold minsize noreturn nounwind sspreq uwtable -define internal fastcc void @"func_AttrReaderSigChecked#10initialize.cold.1"(i64 %rawArg_foo) unnamed_addr #13 !dbg !87 { -newFuncRoot: - tail call void @sorbet_cast_failure(i64 %rawArg_foo, i8* noundef getelementptr inbounds ([4 x i8], [4 x i8]* @str_sig, i64 0, i64 0), i8* noundef getelementptr inbounds ([8 x i8], [8 x i8]* @str_Integer, i64 0, i64 0)) #20, !dbg !89 - unreachable, !dbg !89 -} - -; Function Attrs: cold minsize noreturn nounwind sspreq uwtable -define internal fastcc void @"func_AttrReaderSigChecked#3foo.cold.1"(i64 %0) unnamed_addr #13 !dbg !90 { -newFuncRoot: - tail call void @sorbet_cast_failure(i64 %0, i8* noundef getelementptr inbounds ([13 x i8], [13 x i8]* @"str_Return value", i64 0, i64 0), i8* noundef getelementptr inbounds ([8 x i8], [8 x i8]* @str_Integer, i64 0, i64 0)) #20 - unreachable -} - -; Function Attrs: nofree nosync nounwind willreturn -declare void @llvm.assume(i1 noundef) #14 - -; Function Attrs: ssp -define linkonce void @"const_recompute_T::Sig"() local_unnamed_addr #10 { - %1 = tail call i64 @sorbet_getConstant(i8* getelementptr inbounds ([7 x i8], [7 x i8]* @"str_T::Sig", i64 0, i64 0), i64 6) - store i64 %1, i64* @"guarded_const_T::Sig", align 8 - %2 = load i64, i64* @ruby_vm_global_constant_state, align 8, !tbaa !31 - store i64 %2, i64* @"guard_epoch_T::Sig", align 8 - ret void -} - -; Function Attrs: ssp -define linkonce void @const_recompute_AttrReaderSigChecked() local_unnamed_addr #10 { - %1 = tail call i64 @sorbet_getConstant(i8* getelementptr inbounds ([21 x i8], [21 x i8]* @str_AttrReaderSigChecked, i64 0, i64 0), i64 20) - store i64 %1, i64* @guarded_const_AttrReaderSigChecked, align 8 - %2 = load i64, i64* @ruby_vm_global_constant_state, align 8, !tbaa !31 - store i64 %2, i64* @guard_epoch_AttrReaderSigChecked, align 8 - ret void -} - -attributes #0 = { nounwind readnone willreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #1 = { cold noreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #2 = { noreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #3 = { "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #4 = { nofree nosync nounwind readnone speculatable willreturn } -attributes #5 = { allocsize(0,1) "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #6 = { argmemonly nofree nosync nounwind willreturn } -attributes #7 = { nounwind ssp uwtable "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #8 = { nounwind sspreq uwtable } -attributes #9 = { sspreq } -attributes #10 = { ssp } -attributes #11 = { inaccessiblememonly nofree nosync nounwind willreturn } -attributes #12 = { argmemonly nofree nosync nounwind willreturn writeonly } -attributes #13 = { cold minsize noreturn nounwind sspreq uwtable } -attributes #14 = { nofree nosync nounwind willreturn } -attributes #15 = { noreturn nounwind } -attributes #16 = { nounwind } -attributes #17 = { nounwind allocsize(0,1) } -attributes #18 = { nounwind readnone willreturn } -attributes #19 = { nounwind willreturn } -attributes #20 = { noreturn } -attributes #21 = { noinline } - -!llvm.module.flags = !{!0, !1, !2} -!llvm.dbg.cu = !{!3} - -!0 = !{i32 2, !"Debug Info Version", i32 3} -!1 = !{i32 4, !"cf-protection-return", i32 1} -!2 = !{i32 4, !"cf-protection-branch", i32 1} -!3 = distinct !DICompileUnit(language: DW_LANG_C, file: !4, producer: "Sorbet LLVM", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !5) -!4 = !DIFile(filename: "test/testdata/ruby_benchmark/stripe/attr_reader/sig_checked.rb", directory: ".") -!5 = !{} -!6 = !{!7, !7, i64 0} -!7 = !{!"long", !8, i64 0} -!8 = !{!"omnipotent char", !9, i64 0} -!9 = !{!"Simple C/C++ TBAA"} -!10 = distinct !DISubprogram(name: "AttrReaderSigChecked.", linkageName: "func_AttrReaderSigChecked.13L62", scope: null, file: !4, line: 5, type: !11, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!11 = !DISubroutineType(types: !12) -!12 = !{!13} -!13 = !DIBasicType(name: "VALUE", size: 64, encoding: DW_ATE_signed) -!14 = !{!15, !15, i64 0} -!15 = !{!"any pointer", !8, i64 0} -!16 = !{!17, !15, i64 16} -!17 = !{!"rb_execution_context_struct", !15, i64 0, !7, i64 8, !15, i64 16, !15, i64 24, !15, i64 32, !18, i64 40, !18, i64 44, !15, i64 48, !15, i64 56, !15, i64 64, !7, i64 72, !7, i64 80, !15, i64 88, !7, i64 96, !15, i64 104, !15, i64 112, !7, i64 120, !7, i64 128, !8, i64 136, !8, i64 137, !7, i64 144, !19, i64 152} -!18 = !{!"int", !8, i64 0} -!19 = !{!"", !15, i64 0, !15, i64 8, !7, i64 16, !8, i64 24} -!20 = !{!21, !15, i64 16} -!21 = !{!"rb_control_frame_struct", !15, i64 0, !15, i64 8, !15, i64 16, !7, i64 24, !15, i64 32, !15, i64 40, !15, i64 48} -!22 = !{!21, !15, i64 32} -!23 = !DILocation(line: 0, scope: !10) -!24 = !DILocation(line: 7, column: 3, scope: !10) -!25 = !{!17, !18, i64 40} -!26 = !{!17, !18, i64 44} -!27 = !{!"branch_weights", i32 2000, i32 1} -!28 = !{!17, !15, i64 56} -!29 = !DILocation(line: 12, column: 3, scope: !10) -!30 = !DILocation(line: 6, column: 3, scope: !10) -!31 = !{!32, !32, i64 0} -!32 = !{!"long long", !8, i64 0} -!33 = !{!"branch_weights", i32 1, i32 10000} -!34 = !DILocation(line: 8, column: 3, scope: !10) -!35 = !{!36, !18, i64 8} -!36 = !{!"rb_sorbet_param_struct", !37, i64 0, !18, i64 4, !18, i64 8, !18, i64 12, !18, i64 16, !18, i64 20, !18, i64 24, !18, i64 28, !15, i64 32, !18, i64 40, !18, i64 44, !18, i64 48, !18, i64 52, !15, i64 56} -!37 = !{!"", !18, i64 0, !18, i64 0, !18, i64 0, !18, i64 0, !18, i64 0, !18, i64 0, !18, i64 0, !18, i64 0, !18, i64 1, !18, i64 1} -!38 = !{!36, !18, i64 4} -!39 = !{!36, !15, i64 32} -!40 = !DILocation(line: 13, column: 3, scope: !10) -!41 = !DILocation(line: 7, column: 8, scope: !42) -!42 = distinct !DISubprogram(name: "AttrReaderSigChecked.", linkageName: "func_AttrReaderSigChecked.13L62$block_1", scope: !10, file: !4, line: 5, type: !11, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!43 = !DILocation(line: 16, column: 5, scope: !44) -!44 = distinct !DISubprogram(name: ".", linkageName: "func_.17$152", scope: null, file: !4, line: 5, type: !11, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!45 = !DILocation(line: 19, column: 7, scope: !44) -!46 = !DILocation(line: 21, column: 3, scope: !44) -!47 = !DILocation(line: 23, column: 3, scope: !44) -!48 = !DILocation(line: 26, column: 1, scope: !44) -!49 = !DILocation(line: 27, column: 6, scope: !44) -!50 = !DILocation(line: 27, column: 1, scope: !44) -!51 = !DILocation(line: 12, column: 8, scope: !52) -!52 = distinct !DISubprogram(name: "AttrReaderSigChecked.", linkageName: "func_AttrReaderSigChecked.13L62$block_2", scope: !10, file: !4, line: 5, type: !11, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!53 = !{!54, !7, i64 400} -!54 = !{!"rb_vm_struct", !7, i64 0, !55, i64 8, !15, i64 192, !15, i64 200, !15, i64 208, !32, i64 216, !8, i64 224, !56, i64 264, !56, i64 280, !56, i64 296, !56, i64 312, !7, i64 328, !18, i64 336, !18, i64 340, !18, i64 344, !18, i64 344, !18, i64 344, !18, i64 344, !18, i64 348, !7, i64 352, !8, i64 360, !7, i64 400, !7, i64 408, !7, i64 416, !7, i64 424, !7, i64 432, !7, i64 440, !7, i64 448, !15, i64 456, !15, i64 464, !58, i64 472, !59, i64 992, !15, i64 1016, !15, i64 1024, !18, i64 1032, !18, i64 1036, !56, i64 1040, !8, i64 1056, !7, i64 1096, !7, i64 1104, !7, i64 1112, !7, i64 1120, !7, i64 1128, !18, i64 1136, !15, i64 1144, !15, i64 1152, !15, i64 1160, !15, i64 1168, !15, i64 1176, !15, i64 1184, !18, i64 1192, !60, i64 1200, !8, i64 1232} -!55 = !{!"rb_global_vm_lock_struct", !15, i64 0, !8, i64 8, !56, i64 48, !15, i64 64, !18, i64 72, !8, i64 80, !8, i64 128, !18, i64 176, !18, i64 180} -!56 = !{!"list_head", !57, i64 0} -!57 = !{!"list_node", !15, i64 0, !15, i64 8} -!58 = !{!"", !8, i64 0} -!59 = !{!"rb_hook_list_struct", !15, i64 0, !18, i64 8, !18, i64 12, !18, i64 16} -!60 = !{!"", !7, i64 0, !7, i64 8, !7, i64 16, !7, i64 24} -!61 = !DILocation(line: 0, scope: !44) -!62 = !DILocation(line: 5, column: 1, scope: !44) -!63 = !DILocation(line: 18, column: 5, scope: !44) -!64 = !{!"branch_weights", i32 1, i32 2000} -!65 = !{!"branch_weights", i32 1073205, i32 2146410443} -!66 = !{!67, !7, i64 0} -!67 = !{!"RBasic", !7, i64 0, !7, i64 8} -!68 = !{!69} -!69 = distinct !{!69, !70, !"sorbet_rb_int_lt: argument 0"} -!70 = distinct !{!70, !"sorbet_rb_int_lt"} -!71 = !{!"branch_weights", i32 4001, i32 4000000} -!72 = !{!73} -!73 = distinct !{!73, !74, !"sorbet_rb_int_plus: argument 0"} -!74 = distinct !{!74, !"sorbet_rb_int_plus"} -!75 = distinct !DISubprogram(name: "AttrReaderSigChecked#initialize", linkageName: "func_AttrReaderSigChecked#10initialize", scope: null, file: !4, line: 8, type: !11, scopeLine: 8, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!76 = !DILocation(line: 8, column: 3, scope: !75) -!77 = !DILocation(line: 8, column: 18, scope: !75) -!78 = !DILocation(line: 9, column: 12, scope: !75) -!79 = !DILocation(line: 9, column: 5, scope: !75) -!80 = distinct !DISubprogram(name: "AttrReaderSigChecked#foo", linkageName: "func_AttrReaderSigChecked#3foo", scope: null, file: !4, line: 13, type: !11, scopeLine: 13, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!81 = !DILocation(line: 13, column: 3, scope: !80) -!82 = !DILocation(line: 13, column: 16, scope: !80) -!83 = !DILocation(line: 0, scope: !80) -!84 = !{!21, !7, i64 24} -!85 = !DILocation(line: 7, column: 3, scope: !42) -!86 = !DILocation(line: 12, column: 3, scope: !52) -!87 = distinct !DISubprogram(name: "func_AttrReaderSigChecked#10initialize.cold.1", linkageName: "func_AttrReaderSigChecked#10initialize.cold.1", scope: null, file: !4, type: !88, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !3, retainedNodes: !5) -!88 = !DISubroutineType(types: !5) -!89 = !DILocation(line: 8, column: 18, scope: !87) -!90 = distinct !DISubprogram(name: "func_AttrReaderSigChecked#3foo.cold.1", linkageName: "func_AttrReaderSigChecked#3foo.cold.1", scope: null, file: !4, type: !88, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !3, retainedNodes: !5) diff --git a/test/testdata/ruby_benchmark/stripe/attr_reader/sig_checked.rb b/test/testdata/ruby_benchmark/stripe/attr_reader/sig_checked.rb deleted file mode 100644 index 880f3840b9..0000000000 --- a/test/testdata/ruby_benchmark/stripe/attr_reader/sig_checked.rb +++ /dev/null @@ -1,27 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -class AttrReaderSigChecked - extend T::Sig - sig {params(foo: Integer).void} - def initialize(foo) - @foo = foo - end - - sig {returns(Integer)} - attr_reader :foo -end - -x = AttrReaderSigChecked.new(1248) - -i = 0 -while i < 10_000_000 - - x.foo - - i += 1 -end - -puts i -puts x.foo diff --git a/test/testdata/ruby_benchmark/stripe/attr_reader/sig_unchecked.rb b/test/testdata/ruby_benchmark/stripe/attr_reader/sig_unchecked.rb deleted file mode 100644 index 375062623e..0000000000 --- a/test/testdata/ruby_benchmark/stripe/attr_reader/sig_unchecked.rb +++ /dev/null @@ -1,27 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -class AttrReaderSigUnchecked - extend T::Sig - sig {params(foo: Integer).void} - def initialize(foo) - @foo = foo - end - - sig {returns(Integer).checked(:never)} - attr_reader :foo -end - -x = AttrReaderSigUnchecked.new(1248) - -i = 0 -while i < 10_000_000 - - x.foo - - i += 1 -end - -puts i -puts x.foo diff --git a/test/testdata/ruby_benchmark/stripe/attr_reader/sig_unchecked_force_ivar.rb b/test/testdata/ruby_benchmark/stripe/attr_reader/sig_unchecked_force_ivar.rb deleted file mode 100644 index c83895d96a..0000000000 --- a/test/testdata/ruby_benchmark/stripe/attr_reader/sig_unchecked_force_ivar.rb +++ /dev/null @@ -1,31 +0,0 @@ -# frozen_string_literal: true -# typed: strict -# compiled: true - -class AttrReaderSigUncheckedForceIVAR - extend T::Sig - sig {params(foo: Integer).void} - def initialize(foo) - @foo = foo - end - - sig {returns(Integer).checked(:never)} - attr_reader :foo - - # This is here to trick the compiler into redefining the method using - # `attr_reader`, which the VM special cases. - self.send(:attr_reader, :foo) -end - -x = AttrReaderSigUncheckedForceIVAR.new(1248) - -i = 0 -while i < 10_000_000 - - x.foo - - i += 1 -end - -puts i -puts x.foo diff --git a/test/testdata/ruby_benchmark/stripe/attr_writer/no_sig.rb b/test/testdata/ruby_benchmark/stripe/attr_writer/no_sig.rb deleted file mode 100644 index fea845336e..0000000000 --- a/test/testdata/ruby_benchmark/stripe/attr_writer/no_sig.rb +++ /dev/null @@ -1,30 +0,0 @@ -# typed: true -# frozen_string_literal: true -# compiled: true - -class AttrWriterNoSig - extend T::Sig - - sig {params(foo: Integer).void} - def initialize(foo) - @foo = foo - end - - def foo - @foo - end - - # no sig - attr_writer :foo -end - -x = AttrWriterNoSig.new(97) - -i = 0 -while i < 10_000_000 - x.foo = i - i += 1 -end - -puts i -puts x.foo diff --git a/test/testdata/ruby_benchmark/stripe/attr_writer/sig_checked.rb b/test/testdata/ruby_benchmark/stripe/attr_writer/sig_checked.rb deleted file mode 100644 index 698d121d7e..0000000000 --- a/test/testdata/ruby_benchmark/stripe/attr_writer/sig_checked.rb +++ /dev/null @@ -1,30 +0,0 @@ -# typed: true -# frozen_string_literal: true -# compiled: true - -class AttrWriterSigChecked - extend T::Sig - - sig {params(foo: Integer).void} - def initialize(foo) - @foo = foo - end - - def foo - @foo - end - - sig {params(foo: Integer).void} - attr_writer :foo -end - -x = AttrWriterSigChecked.new(97) - -i = 0 -while i < 10_000_000 - x.foo = i - i += 1 -end - -puts i -puts x.foo diff --git a/test/testdata/ruby_benchmark/stripe/attr_writer/sig_unchecked.rb b/test/testdata/ruby_benchmark/stripe/attr_writer/sig_unchecked.rb deleted file mode 100644 index 21a54d3330..0000000000 --- a/test/testdata/ruby_benchmark/stripe/attr_writer/sig_unchecked.rb +++ /dev/null @@ -1,30 +0,0 @@ -# typed: true -# frozen_string_literal: true -# compiled: true - -class AttrWriterSigChecked - extend T::Sig - - sig {params(foo: Integer).void} - def initialize(foo) - @foo = foo - end - - def foo - @foo - end - - sig {params(foo: Integer).void.checked(:never)} - attr_writer :foo -end - -x = AttrWriterSigChecked.new(97) - -i = 0 -while i < 10_000_000 - x.foo = i - i += 1 -end - -puts i -puts x.foo diff --git a/test/testdata/ruby_benchmark/stripe/bang_intrinsic_dispatch.rb b/test/testdata/ruby_benchmark/stripe/bang_intrinsic_dispatch.rb deleted file mode 100644 index a0fefcf6b2..0000000000 --- a/test/testdata/ruby_benchmark/stripe/bang_intrinsic_dispatch.rb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -i = 0 -while i < 10_000_000 - !"foo" - i += 1 -end diff --git a/test/testdata/ruby_benchmark/stripe/bang_intrinsic_toggle.rb b/test/testdata/ruby_benchmark/stripe/bang_intrinsic_toggle.rb deleted file mode 100644 index cd206e6ccf..0000000000 --- a/test/testdata/ruby_benchmark/stripe/bang_intrinsic_toggle.rb +++ /dev/null @@ -1,12 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -i = 0 -x = T.let(true, T::Boolean) -while i < 10_000_000 - x = !x - i += 1 -end -puts i -puts x diff --git a/test/testdata/ruby_benchmark/stripe/call_compiled_method.rb b/test/testdata/ruby_benchmark/stripe/call_compiled_method.rb deleted file mode 100644 index 835335cda3..0000000000 --- a/test/testdata/ruby_benchmark/stripe/call_compiled_method.rb +++ /dev/null @@ -1,20 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -class A - def foo - end -end - -a = A.new - -i = 0 -while i < 10_000_000 - - a.foo - - i += 1 -end - -puts i diff --git a/test/testdata/ruby_benchmark/stripe/case_nil_str_obj.rb b/test/testdata/ruby_benchmark/stripe/case_nil_str_obj.rb deleted file mode 100644 index b84d96a694..0000000000 --- a/test/testdata/ruby_benchmark/stripe/case_nil_str_obj.rb +++ /dev/null @@ -1,83 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -nil_ = T.unsafe(nil) -str = T.unsafe('') - -i = 0 - -nil_eq_nil = T.let(nil, T.nilable(T::Boolean)) -str_eq_str = T.let(nil, T.nilable(T::Boolean)) - -nil_eq_str = T.let(nil, T.nilable(T::Boolean)) -str_eq_nil = T.let(nil, T.nilable(T::Boolean)) - -obj_eq_nil = T.let(nil, T.nilable(T::Boolean)) -obj_eq_str = T.let(nil, T.nilable(T::Boolean)) - -# We're using `case` in this example instead of calling `.===` directly because -# the VM will use different bytecode instructions to handle a `case`. -# -# It will eventually dispatch to a method's `===` method if required, but it's -# more common to see `case` written in normal code, so this makes the benchmark -# somewhat more realistic. - -while i < 10_000_000 - - # Classes are equal, so ancestor search will exit early (in interpreter) - - nil_eq_nil = - case nil_ - when NilClass then true - else false - end - - str_eq_str = - case str - when String then true - else false - end - - # When the result is false the VM never exits early (inspects every ancestor) - - nil_eq_str = - case str - when NilClass then true - else false - end - - - str_eq_nil = - case nil_ - when String then true - else false - end - - # Classes are not equal, but result is true (partial ancestor search) - - obj_eq_nil = - case nil_ - when Object then true - else false - end - - obj_eq_str = - case str - when Object then true - else false - end - - i += 1 -end - -puts "iterations: #{i}" -puts -puts "NilClass.===(nil_) => #{nil_eq_nil}" -puts "String.===(str) => #{str_eq_str}" -puts -puts "NilClass.===(str) => #{nil_eq_str}" -puts "String.===(nil_) => #{str_eq_nil}" -puts -puts "Object.===(nil_) => #{obj_eq_nil}" -puts "Object.===(str) => #{obj_eq_str}" diff --git a/test/testdata/ruby_benchmark/stripe/compiled_set_add.rb b/test/testdata/ruby_benchmark/stripe/compiled_set_add.rb deleted file mode 100644 index 2d12f70778..0000000000 --- a/test/testdata/ruby_benchmark/stripe/compiled_set_add.rb +++ /dev/null @@ -1,939 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -# This file contains a copy of the set.rb class from Ruby 2.7.2, and some -# benchmark code at the very bottom. -# -# Unfortunately it is duplicated in a number of other benchmarks, because for -# the moment I don't see an easy way for multiple benchmarks to share a -# compiled file. -# -# Modifications made from original set.rb: -# -# 1. Classes renamed to "CompiledFoo" (e.g., Set->CompiledSet) -# 2. Miscellaneous 'T.must's to make Sorbet TC happy -# 3. Removed the Set#divide function (this uses 'class <<' in unsupported -# ways). -# -# TODO(aprocter): Deduplicate the set.rb code - -# See bottom of the file for the actual benchmark code. - -# -# set.rb - defines the Set class -#++ -# Copyright (c) 2002-2016 Akinori MUSHA -# -# Documentation by Akinori MUSHA and Gavin Sinclair. -# -# All rights reserved. You can redistribute and/or modify it under the same -# terms as Ruby. -# -# $Id$ -# -# == Overview -# -# This library provides the Set class, which deals with a collection -# of unordered values with no duplicates. It is a hybrid of Array's -# intuitive inter-operation facilities and Hash's fast lookup. If you -# need to keep values sorted in some order, use the SortedSet class. -# -# The method +to_set+ is added to Enumerable for convenience. -# -# See the Set and SortedSet documentation for examples of usage. - - -# -# Set implements a collection of unordered values with no duplicates. -# This is a hybrid of Array's intuitive inter-operation facilities and -# Hash's fast lookup. -# -# Set is easy to use with Enumerable objects (implementing +each+). -# Most of the initializer methods and binary operators accept generic -# Enumerable objects besides sets and arrays. An Enumerable object -# can be converted to Set using the +to_set+ method. -# -# Set uses Hash as storage, so you must note the following points: -# -# * Equality of elements is determined according to Object#eql? and -# Object#hash. Use Set#compare_by_identity to make a set compare -# its elements by their identity. -# * Set assumes that the identity of each element does not change -# while it is stored. Modifying an element of a set will render the -# set to an unreliable state. -# * When a string is to be stored, a frozen copy of the string is -# stored instead unless the original string is already frozen. -# -# == Comparison -# -# The comparison operators <, >, <=, and >= are implemented as -# shorthand for the {proper_,}{subset?,superset?} methods. However, -# the <=> operator is intentionally left out because not every pair of -# sets is comparable ({x, y} vs. {x, z} for example). -# -# == Example -# -# require 'set' -# s1 = Set[1, 2] #=> # -# s2 = [1, 2].to_set #=> # -# s1 == s2 #=> true -# s1.add("foo") #=> # -# s1.merge([2, 6]) #=> # -# s1.subset?(s2) #=> false -# s2.subset?(s1) #=> true -# -# == Contact -# -# - Akinori MUSHA (current maintainer) -# -class CompiledSet - include Enumerable - - # Creates a new set containing the given objects. - # - # Set[1, 2] # => # - # Set[1, 2, 1] # => # - # Set[1, 'c', :s] # => # - def self.[](*ary) - new(ary) - end - - # Creates a new set containing the elements of the given enumerable - # object. - # - # If a block is given, the elements of enum are preprocessed by the - # given block. - # - # Set.new([1, 2]) #=> # - # Set.new([1, 2, 1]) #=> # - # Set.new([1, 'c', :s]) #=> # - # Set.new(1..5) #=> # - # Set.new([1, 2, 3]) { |x| x * x } #=> # - def initialize(enum = nil, &block) # :yields: o - @hash ||= Hash.new(false) - - enum.nil? and return - - if block - do_with_enum(enum) { |o| add(block[o]) } - else - merge(enum) - end - end - - # Makes the set compare its elements by their identity and returns - # self. This method may not be supported by all subclasses of Set. - def compare_by_identity - if @hash.respond_to?(:compare_by_identity) - @hash.compare_by_identity - self - else - raise NotImplementedError, "#{self.class.name}\##{__method__} is not implemented" - end - end - - # Returns true if the set will compare its elements by their - # identity. Also see Set#compare_by_identity. - def compare_by_identity? - @hash.respond_to?(:compare_by_identity?) && @hash.compare_by_identity? - end - - def do_with_enum(enum, &block) # :nodoc: - if enum.respond_to?(:each_entry) - enum.each_entry(&block) if block - elsif enum.respond_to?(:each) - enum.each(&block) if block - else - raise ArgumentError, "value must be enumerable" - end - end - private :do_with_enum - - # Dup internal hash. - def initialize_dup(orig) - super - @hash = orig.instance_variable_get(:@hash).dup - end - - # Clone internal hash. - def initialize_clone(orig) - super - @hash = orig.instance_variable_get(:@hash).clone - end - - def freeze # :nodoc: - @hash.freeze - super - end - - # Returns the number of elements. - def size - @hash.size - end - alias length size - - # Returns true if the set contains no elements. - def empty? - @hash.empty? - end - - # Removes all elements and returns self. - # - # set = Set[1, 'c', :s] #=> # - # set.clear #=> # - # set #=> # - def clear - @hash.clear - self - end - - # Replaces the contents of the set with the contents of the given - # enumerable object and returns self. - # - # set = Set[1, 'c', :s] #=> # - # set.replace([1, 2]) #=> # - # set #=> # - def replace(enum) - if enum.instance_of?(self.class) - @hash.replace(enum.instance_variable_get(:@hash)) - self - else - do_with_enum(enum) # make sure enum is enumerable before calling clear - clear - merge(enum) - end - end - - # Converts the set to an array. The order of elements is uncertain. - # - # Set[1, 2].to_a #=> [1, 2] - # Set[1, 'c', :s].to_a #=> [1, "c", :s] - def to_a - @hash.keys - end - - # Returns self if no arguments are given. Otherwise, converts the - # set to another with klass.new(self, *args, &block). - # - # In subclasses, returns klass.new(self, *args, &block) unless - # overridden. - def to_compiled_set(klass = CompiledSet, *args, &block) - return self if instance_of?(CompiledSet) && klass == CompiledSet && block.nil? && args.empty? - klass.new(self, *args, &block) - end - - def flatten_merge(set, seen = CompiledSet.new) # :nodoc: - set.each { |e| - if e.is_a?(CompiledSet) - if seen.include?(e_id = e.object_id) - raise ArgumentError, "tried to flatten recursive CompiledSet" - end - - seen.add(e_id) - flatten_merge(e, seen) - seen.delete(e_id) - else - add(e) - end - } - - self - end - protected :flatten_merge - - # Returns a new set that is a copy of the set, flattening each - # containing set recursively. - def flatten - self.class.new.flatten_merge(self) - end - - # Equivalent to Set#flatten, but replaces the receiver with the - # result in place. Returns nil if no modifications were made. - def flatten! - replace(flatten()) if any? { |e| e.is_a?(CompiledSet) } - end - - # Returns true if the set contains the given object. - # - # Note that include? and member? do not test member - # equality using == as do other Enumerables. - # - # See also Enumerable#include? - def include?(o) - @hash[o] - end - alias member? include? - - # Returns true if the set is a superset of the given set. - def superset?(set) - case - when set.instance_of?(self.class) && @hash.respond_to?(:>=) - @hash >= set.instance_variable_get(:@hash) - when set.is_a?(CompiledSet) - size >= set.size && set.all? { |o| include?(o) } - else - raise ArgumentError, "value must be a set" - end - end - alias >= superset? - - # Returns true if the set is a proper superset of the given set. - def proper_superset?(set) - case - when set.instance_of?(self.class) && @hash.respond_to?(:>) - @hash > set.instance_variable_get(:@hash) - when set.is_a?(CompiledSet) - size > set.size && set.all? { |o| include?(o) } - else - raise ArgumentError, "value must be a set" - end - end - alias > proper_superset? - - # Returns true if the set is a subset of the given set. - def subset?(set) - case - when set.instance_of?(self.class) && @hash.respond_to?(:<=) - @hash <= set.instance_variable_get(:@hash) - when set.is_a?(CompiledSet) - size <= set.size && all? { |o| set.include?(o) } - else - raise ArgumentError, "value must be a set" - end - end - alias <= subset? - - # Returns true if the set is a proper subset of the given set. - def proper_subset?(set) - case - when set.instance_of?(self.class) && @hash.respond_to?(:<) - @hash < set.instance_variable_get(:@hash) - when set.is_a?(CompiledSet) - size < set.size && all? { |o| set.include?(o) } - else - raise ArgumentError, "value must be a set" - end - end - alias < proper_subset? - - # Returns true if the set and the given set have at least one - # element in common. - # - # Set[1, 2, 3].intersect? Set[4, 5] #=> false - # Set[1, 2, 3].intersect? Set[3, 4] #=> true - def intersect?(set) - set.is_a?(CompiledSet) or raise ArgumentError, "value must be a set" - if size < set.size - any? { |o| set.include?(o) } - else - set.any? { |o| include?(o) } - end - end - - # Returns true if the set and the given set have no element in - # common. This method is the opposite of +intersect?+. - # - # Set[1, 2, 3].disjoint? Set[3, 4] #=> false - # Set[1, 2, 3].disjoint? Set[4, 5] #=> true - def disjoint?(set) - !intersect?(set) - end - - # Calls the given block once for each element in the set, passing - # the element as parameter. Returns an enumerator if no block is - # given. - def each(&block) - block or return enum_for(T.must(__method__)) { size } - @hash.each_key(&block) - self - end - - # Adds the given object to the set and returns self. Use +merge+ to - # add many elements at once. - # - # Set[1, 2].add(3) #=> # - # Set[1, 2].add([3, 4]) #=> # - # Set[1, 2].add(2) #=> # - def add(o) - @hash[o] = true - self - end - alias << add - - # Adds the given object to the set and returns self. If the - # object is already in the set, returns nil. - # - # Set[1, 2].add?(3) #=> # - # Set[1, 2].add?([3, 4]) #=> # - # Set[1, 2].add?(2) #=> nil - def add?(o) - add(o) unless include?(o) - end - - # Deletes the given object from the set and returns self. Use +subtract+ to - # delete many items at once. - def delete(o) - @hash.delete(o) - self - end - - # Deletes the given object from the set and returns self. If the - # object is not in the set, returns nil. - def delete?(o) - delete(o) if include?(o) - end - - # Deletes every element of the set for which block evaluates to - # true, and returns self. Returns an enumerator if no block is - # given. - def delete_if - block_given? or return enum_for(T.must(__method__)) { size } - # @hash.delete_if should be faster, but using it breaks the order - # of enumeration in subclasses. - select { |o| yield o }.each { |o| @hash.delete(o) } - self - end - - # Deletes every element of the set for which block evaluates to - # false, and returns self. Returns an enumerator if no block is - # given. - def keep_if - block_given? or return enum_for(T.must(__method__)) { size } - # @hash.keep_if should be faster, but using it breaks the order of - # enumeration in subclasses. - reject { |o| yield o }.each { |o| @hash.delete(o) } - self - end - - # Replaces the elements with ones returned by collect(). - # Returns an enumerator if no block is given. - def collect! - block_given? or return enum_for(T.must(__method__)) { size } - set = self.class.new - each { |o| set << yield(o) } - replace(set) - end - alias map! collect! - - # Equivalent to Set#delete_if, but returns nil if no changes were - # made. Returns an enumerator if no block is given. - def reject!(&block) - block or return enum_for(T.must(__method__)) { size } - n = size - delete_if(&block) - self if size != n - end - - # Equivalent to Set#keep_if, but returns nil if no changes were - # made. Returns an enumerator if no block is given. - def select!(&block) - block or return enum_for(T.must(__method__)) { size } - n = size - keep_if(&block) - self if size != n - end - - # Equivalent to Set#select! - alias filter! select! - - # Merges the elements of the given enumerable object to the set and - # returns self. - def merge(enum) - if enum.instance_of?(self.class) - @hash.update(enum.instance_variable_get(:@hash)) - else - do_with_enum(enum) { |o| add(o) } - end - - self - end - - # Deletes every element that appears in the given enumerable object - # and returns self. - def subtract(enum) - do_with_enum(enum) { |o| delete(o) } - self - end - - # Returns a new set built by merging the set and the elements of the - # given enumerable object. - # - # Set[1, 2, 3] | Set[2, 4, 5] #=> # - # Set[1, 5, 'z'] | (1..6) #=> # - def |(enum) - dup.merge(enum) - end - alias + | - alias union | - - # Returns a new set built by duplicating the set, removing every - # element that appears in the given enumerable object. - # - # Set[1, 3, 5] - Set[1, 5] #=> # - # Set['a', 'b', 'z'] - ['a', 'c'] #=> # - def -(enum) - dup.subtract(enum) - end - alias difference - - - # Returns a new set containing elements common to the set and the - # given enumerable object. - # - # Set[1, 3, 5] & Set[3, 2, 1] #=> # - # Set['a', 'b', 'z'] & ['a', 'b', 'c'] #=> # - def &(enum) - n = self.class.new - do_with_enum(enum) { |o| n.add(o) if include?(o) } - n - end - alias intersection & - - # Returns a new set containing elements exclusive between the set - # and the given enumerable object. (set ^ enum) is equivalent to - # ((set | enum) - (set & enum)). - # - # Set[1, 2] ^ Set[2, 3] #=> # - # Set[1, 'b', 'c'] ^ ['b', 'd'] #=> # - def ^(enum) - n = CompiledSet.new(enum) - each { |o| n.add(o) unless n.delete?(o) } - n - end - - # Returns true if two sets are equal. The equality of each couple - # of elements is defined according to Object#eql?. - # - # Set[1, 2] == Set[2, 1] #=> true - # Set[1, 3, 5] == Set[1, 5] #=> false - # Set['a', 'b', 'c'] == Set['a', 'c', 'b'] #=> true - # Set['a', 'b', 'c'] == ['a', 'c', 'b'] #=> false - def ==(other) - if self.equal?(other) - true - elsif other.instance_of?(self.class) - @hash == other.instance_variable_get(:@hash) - elsif other.is_a?(CompiledSet) && self.size == other.size - other.all? { |o| @hash.include?(o) } - else - false - end - end - - def hash # :nodoc: - @hash.hash - end - - def eql?(o) # :nodoc: - return false unless o.is_a?(CompiledSet) - @hash.eql?(o.instance_variable_get(:@hash)) - end - - # Resets the internal state after modification to existing elements - # and returns self. - # - # Elements will be reindexed and deduplicated. - def reset - if @hash.respond_to?(:rehash) - @hash.rehash # This should perform frozenness check. - else - raise FrozenError, "can't modify frozen #{self.class.name}" if frozen? - end - self - end - - # Returns true if the given object is a member of the set, - # and false otherwise. - # - # Used in case statements: - # - # require 'set' - # - # case :apple - # when Set[:potato, :carrot] - # "vegetable" - # when Set[:apple, :banana] - # "fruit" - # end - # # => "fruit" - # - # Or by itself: - # - # Set[1, 2, 3] === 2 #=> true - # Set[1, 2, 3] === 4 #=> false - # - alias === include? - - # Classifies the set by the return value of the given block and - # returns a hash of {value => set of elements} pairs. The block is - # called once for each element of the set, passing the element as - # parameter. - # - # require 'set' - # files = Set.new(Dir.glob("*.rb")) - # hash = files.classify { |f| File.mtime(f).year } - # hash #=> {2000=>#, - # # 2001=>#, - # # 2002=>#} - # - # Returns an enumerator if no block is given. - def classify # :yields: o - block_given? or return enum_for(T.must(__method__)) { size } - - h = {} - - each { |i| - (h[yield(i)] ||= self.class.new).add(i) - } - - h - end - - # Divides the set into a set of subsets according to the commonality - # defined by the given block. - # - # If the arity of the block is 2, elements o1 and o2 are in common - # if block.call(o1, o2) is true. Otherwise, elements o1 and o2 are - # in common if block.call(o1) == block.call(o2). - # - # require 'set' - # numbers = Set[1, 3, 4, 6, 9, 10, 11] - # set = numbers.divide { |i,j| (i - j).abs == 1 } - # set #=> #, - # # #, - # # #, - # # #}> - # - # Returns an enumerator if no block is given. - # def divide(&func) - # func or return enum_for(__method__) { size } - - # if func.arity == 2 - # require 'tsort' - - # class << dig = {} # :nodoc: - # include TSort - - # alias tsort_each_node each_key - # def tsort_each_child(node, &block) - # fetch(node).each(&block) - # end - # end - - # each { |u| - # dig[u] = a = [] - # each{ |v| func.call(u, v) and a << v } - # } - - # set = Set.new() - # dig.each_strongly_connected_component { |css| - # set.add(self.class.new(css)) - # } - # set - # else - # Set.new(classify(&func).values) - # end - # end - - InspectKey = :__inspect_key__ # :nodoc: - - # Returns a string containing a human-readable representation of the - # set ("#"). - def inspect - ids = (Thread.current[InspectKey] ||= []) - - if ids.include?(object_id) - return sprintf('#<%s: {...}>', self.class.name) - end - - ids << object_id - begin - return sprintf('#<%s: {%s}>', self.class, to_a.inspect[1..-2]) - ensure - ids.pop - end - end - - alias to_s inspect - - def pretty_print(pp) # :nodoc: - pp.text sprintf('#<%s: {', self.class.name) - pp.nest(1) { - pp.seplist(self) { |o| - pp.pp o - } - } - pp.text "}>" - end - - def pretty_print_cycle(pp) # :nodoc: - pp.text sprintf('#<%s: {%s}>', self.class.name, empty? ? '' : '...') - end -end - -# -# SortedSet implements a Set that guarantees that its elements are -# yielded in sorted order (according to the return values of their -# #<=> methods) when iterating over them. -# -# All elements that are added to a SortedSet must respond to the <=> -# method for comparison. -# -# Also, all elements must be mutually comparable: el1 <=> -# el2 must not return nil for any elements el1 -# and el2, else an ArgumentError will be raised when -# iterating over the SortedSet. -# -# == Example -# -# require "set" -# -# set = SortedSet.new([2, 1, 5, 6, 4, 5, 3, 3, 3]) -# ary = [] -# -# set.each do |obj| -# ary << obj -# end -# -# p ary # => [1, 2, 3, 4, 5, 6] -# -# set2 = SortedSet.new([1, 2, "3"]) -# set2.each { |obj| } # => raises ArgumentError: comparison of Fixnum with String failed -# -class CompiledSortedSet < CompiledSet - @@setup = false - @@mutex = Mutex.new - - class << self - def [](*ary) # :nodoc: - new(ary) - end - - def setup # :nodoc: - @@setup and return - - @@mutex.synchronize do - # a hack to shut up warning - #alias_method :old_init, :initialize - - begin - require 'rbtree' - - module_eval <<-END, __FILE__, __LINE__+1 - def initialize(*args) - @hash = RBTree.new - super - end - - def add(o) - o.respond_to?(:<=>) or raise ArgumentError, "value must respond to <=>" - super - end - alias << add - END - rescue LoadError - module_eval <<-END, __FILE__, __LINE__+1 - def initialize(*args) - @keys = nil - super - end - - def clear - @keys = nil - super - end - - def replace(enum) - @keys = nil - super - end - - def add(o) - o.respond_to?(:<=>) or raise ArgumentError, "value must respond to <=>" - @keys = nil - super - end - alias << add - - def delete(o) - @keys = nil - @hash.delete(o) - self - end - - def delete_if - block_given? or return enum_for(__method__) { size } - n = @hash.size - super - @keys = nil if @hash.size != n - self - end - - def keep_if - block_given? or return enum_for(__method__) { size } - n = @hash.size - super - @keys = nil if @hash.size != n - self - end - - def merge(enum) - @keys = nil - super - end - - def each(&block) - block or return enum_for(__method__) { size } - to_a.each(&block) - self - end - - def to_a - (@keys = @hash.keys).sort! unless @keys - @keys - end - - def freeze - to_a - super - end - - def rehash - @keys = nil - super - end - END - end - # a hack to shut up warning - #remove_method :old_init - - @@setup = true - end - end - end - - def initialize(*args, &block) # :nodoc: - CompiledSortedSet.setup - @keys = nil - super - end -end - -module Enumerable - # Makes a set from the enumerable object with given arguments. - # Needs to +require "set"+ to use this method. - def to_compiled_set(klass = CompiledSet, *args, &block) - klass.new(self, *args, &block) - end -end - -# =begin -# == RestricedSet class -# RestricedSet implements a set with restrictions defined by a given -# block. -# -# === Super class -# Set -# -# === Class Methods -# --- RestricedSet::new(enum = nil) { |o| ... } -# --- RestricedSet::new(enum = nil) { |rset, o| ... } -# Creates a new restricted set containing the elements of the given -# enumerable object. Restrictions are defined by the given block. -# -# If the block's arity is 2, it is called with the RestrictedSet -# itself and an object to see if the object is allowed to be put in -# the set. -# -# Otherwise, the block is called with an object to see if the object -# is allowed to be put in the set. -# -# === Instance Methods -# --- restriction_proc -# Returns the restriction procedure of the set. -# -# =end -# -# class RestricedSet < Set -# def initialize(*args, &block) -# @proc = block or raise ArgumentError, "missing a block" -# -# if @proc.arity == 2 -# instance_eval %{ -# def add(o) -# @hash[o] = true if @proc.call(self, o) -# self -# end -# alias << add -# -# def add?(o) -# if include?(o) || !@proc.call(self, o) -# nil -# else -# @hash[o] = true -# self -# end -# end -# -# def replace(enum) -# enum.respond_to?(:each) or raise ArgumentError, "value must be enumerable" -# clear -# enum.each_entry { |o| add(o) } -# -# self -# end -# -# def merge(enum) -# enum.respond_to?(:each) or raise ArgumentError, "value must be enumerable" -# enum.each_entry { |o| add(o) } -# -# self -# end -# } -# else -# instance_eval %{ -# def add(o) -# if @proc.call(o) -# @hash[o] = true -# end -# self -# end -# alias << add -# -# def add?(o) -# if include?(o) || !@proc.call(o) -# nil -# else -# @hash[o] = true -# self -# end -# end -# } -# end -# -# super(*args) -# end -# -# def restriction_proc -# @proc -# end -# end - -# Tests have been moved to test/test_set.rb. - -### -### CompiledSet benchmark code for 'add'. -### - -s = CompiledSet.new - -i = 0 - -while i < 1_000_000 do - s.add(i) - - i += 1 -end - -puts i -puts s.size diff --git a/test/testdata/ruby_benchmark/stripe/compiled_set_each.rb b/test/testdata/ruby_benchmark/stripe/compiled_set_each.rb deleted file mode 100644 index a09a93c586..0000000000 --- a/test/testdata/ruby_benchmark/stripe/compiled_set_each.rb +++ /dev/null @@ -1,939 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -# This file contains a copy of the set.rb class from Ruby 2.7.2, and some -# benchmark code at the very bottom. -# -# Unfortunately it is duplicated in a number of other benchmarks, because for -# the moment I don't see an easy way for multiple benchmarks to share a -# compiled file. -# -# Modifications made from original set.rb: -# -# 1. Classes renamed to "CompiledFoo" (e.g., Set->CompiledSet) -# 2. Miscellaneous 'T.must's to make Sorbet TC happy -# 3. Removed the Set#divide function (this uses 'class <<' in unsupported -# ways). -# -# TODO(aprocter): Deduplicate the set.rb code - -# See bottom of the file for the actual benchmark code. - -# -# set.rb - defines the Set class -#++ -# Copyright (c) 2002-2016 Akinori MUSHA -# -# Documentation by Akinori MUSHA and Gavin Sinclair. -# -# All rights reserved. You can redistribute and/or modify it under the same -# terms as Ruby. -# -# $Id$ -# -# == Overview -# -# This library provides the Set class, which deals with a collection -# of unordered values with no duplicates. It is a hybrid of Array's -# intuitive inter-operation facilities and Hash's fast lookup. If you -# need to keep values sorted in some order, use the SortedSet class. -# -# The method +to_set+ is added to Enumerable for convenience. -# -# See the Set and SortedSet documentation for examples of usage. - - -# -# Set implements a collection of unordered values with no duplicates. -# This is a hybrid of Array's intuitive inter-operation facilities and -# Hash's fast lookup. -# -# Set is easy to use with Enumerable objects (implementing +each+). -# Most of the initializer methods and binary operators accept generic -# Enumerable objects besides sets and arrays. An Enumerable object -# can be converted to Set using the +to_set+ method. -# -# Set uses Hash as storage, so you must note the following points: -# -# * Equality of elements is determined according to Object#eql? and -# Object#hash. Use Set#compare_by_identity to make a set compare -# its elements by their identity. -# * Set assumes that the identity of each element does not change -# while it is stored. Modifying an element of a set will render the -# set to an unreliable state. -# * When a string is to be stored, a frozen copy of the string is -# stored instead unless the original string is already frozen. -# -# == Comparison -# -# The comparison operators <, >, <=, and >= are implemented as -# shorthand for the {proper_,}{subset?,superset?} methods. However, -# the <=> operator is intentionally left out because not every pair of -# sets is comparable ({x, y} vs. {x, z} for example). -# -# == Example -# -# require 'set' -# s1 = Set[1, 2] #=> # -# s2 = [1, 2].to_set #=> # -# s1 == s2 #=> true -# s1.add("foo") #=> # -# s1.merge([2, 6]) #=> # -# s1.subset?(s2) #=> false -# s2.subset?(s1) #=> true -# -# == Contact -# -# - Akinori MUSHA (current maintainer) -# -class CompiledSet - include Enumerable - - # Creates a new set containing the given objects. - # - # Set[1, 2] # => # - # Set[1, 2, 1] # => # - # Set[1, 'c', :s] # => # - def self.[](*ary) - new(ary) - end - - # Creates a new set containing the elements of the given enumerable - # object. - # - # If a block is given, the elements of enum are preprocessed by the - # given block. - # - # Set.new([1, 2]) #=> # - # Set.new([1, 2, 1]) #=> # - # Set.new([1, 'c', :s]) #=> # - # Set.new(1..5) #=> # - # Set.new([1, 2, 3]) { |x| x * x } #=> # - def initialize(enum = nil, &block) # :yields: o - @hash ||= Hash.new(false) - - enum.nil? and return - - if block - do_with_enum(enum) { |o| add(block[o]) } - else - merge(enum) - end - end - - # Makes the set compare its elements by their identity and returns - # self. This method may not be supported by all subclasses of Set. - def compare_by_identity - if @hash.respond_to?(:compare_by_identity) - @hash.compare_by_identity - self - else - raise NotImplementedError, "#{self.class.name}\##{__method__} is not implemented" - end - end - - # Returns true if the set will compare its elements by their - # identity. Also see Set#compare_by_identity. - def compare_by_identity? - @hash.respond_to?(:compare_by_identity?) && @hash.compare_by_identity? - end - - def do_with_enum(enum, &block) # :nodoc: - if enum.respond_to?(:each_entry) - enum.each_entry(&block) if block - elsif enum.respond_to?(:each) - enum.each(&block) if block - else - raise ArgumentError, "value must be enumerable" - end - end - private :do_with_enum - - # Dup internal hash. - def initialize_dup(orig) - super - @hash = orig.instance_variable_get(:@hash).dup - end - - # Clone internal hash. - def initialize_clone(orig) - super - @hash = orig.instance_variable_get(:@hash).clone - end - - def freeze # :nodoc: - @hash.freeze - super - end - - # Returns the number of elements. - def size - @hash.size - end - alias length size - - # Returns true if the set contains no elements. - def empty? - @hash.empty? - end - - # Removes all elements and returns self. - # - # set = Set[1, 'c', :s] #=> # - # set.clear #=> # - # set #=> # - def clear - @hash.clear - self - end - - # Replaces the contents of the set with the contents of the given - # enumerable object and returns self. - # - # set = Set[1, 'c', :s] #=> # - # set.replace([1, 2]) #=> # - # set #=> # - def replace(enum) - if enum.instance_of?(self.class) - @hash.replace(enum.instance_variable_get(:@hash)) - self - else - do_with_enum(enum) # make sure enum is enumerable before calling clear - clear - merge(enum) - end - end - - # Converts the set to an array. The order of elements is uncertain. - # - # Set[1, 2].to_a #=> [1, 2] - # Set[1, 'c', :s].to_a #=> [1, "c", :s] - def to_a - @hash.keys - end - - # Returns self if no arguments are given. Otherwise, converts the - # set to another with klass.new(self, *args, &block). - # - # In subclasses, returns klass.new(self, *args, &block) unless - # overridden. - def to_compiled_set(klass = CompiledSet, *args, &block) - return self if instance_of?(CompiledSet) && klass == CompiledSet && block.nil? && args.empty? - klass.new(self, *args, &block) - end - - def flatten_merge(set, seen = CompiledSet.new) # :nodoc: - set.each { |e| - if e.is_a?(CompiledSet) - if seen.include?(e_id = e.object_id) - raise ArgumentError, "tried to flatten recursive CompiledSet" - end - - seen.add(e_id) - flatten_merge(e, seen) - seen.delete(e_id) - else - add(e) - end - } - - self - end - protected :flatten_merge - - # Returns a new set that is a copy of the set, flattening each - # containing set recursively. - def flatten - self.class.new.flatten_merge(self) - end - - # Equivalent to Set#flatten, but replaces the receiver with the - # result in place. Returns nil if no modifications were made. - def flatten! - replace(flatten()) if any? { |e| e.is_a?(CompiledSet) } - end - - # Returns true if the set contains the given object. - # - # Note that include? and member? do not test member - # equality using == as do other Enumerables. - # - # See also Enumerable#include? - def include?(o) - @hash[o] - end - alias member? include? - - # Returns true if the set is a superset of the given set. - def superset?(set) - case - when set.instance_of?(self.class) && @hash.respond_to?(:>=) - @hash >= set.instance_variable_get(:@hash) - when set.is_a?(CompiledSet) - size >= set.size && set.all? { |o| include?(o) } - else - raise ArgumentError, "value must be a set" - end - end - alias >= superset? - - # Returns true if the set is a proper superset of the given set. - def proper_superset?(set) - case - when set.instance_of?(self.class) && @hash.respond_to?(:>) - @hash > set.instance_variable_get(:@hash) - when set.is_a?(CompiledSet) - size > set.size && set.all? { |o| include?(o) } - else - raise ArgumentError, "value must be a set" - end - end - alias > proper_superset? - - # Returns true if the set is a subset of the given set. - def subset?(set) - case - when set.instance_of?(self.class) && @hash.respond_to?(:<=) - @hash <= set.instance_variable_get(:@hash) - when set.is_a?(CompiledSet) - size <= set.size && all? { |o| set.include?(o) } - else - raise ArgumentError, "value must be a set" - end - end - alias <= subset? - - # Returns true if the set is a proper subset of the given set. - def proper_subset?(set) - case - when set.instance_of?(self.class) && @hash.respond_to?(:<) - @hash < set.instance_variable_get(:@hash) - when set.is_a?(CompiledSet) - size < set.size && all? { |o| set.include?(o) } - else - raise ArgumentError, "value must be a set" - end - end - alias < proper_subset? - - # Returns true if the set and the given set have at least one - # element in common. - # - # Set[1, 2, 3].intersect? Set[4, 5] #=> false - # Set[1, 2, 3].intersect? Set[3, 4] #=> true - def intersect?(set) - set.is_a?(CompiledSet) or raise ArgumentError, "value must be a set" - if size < set.size - any? { |o| set.include?(o) } - else - set.any? { |o| include?(o) } - end - end - - # Returns true if the set and the given set have no element in - # common. This method is the opposite of +intersect?+. - # - # Set[1, 2, 3].disjoint? Set[3, 4] #=> false - # Set[1, 2, 3].disjoint? Set[4, 5] #=> true - def disjoint?(set) - !intersect?(set) - end - - # Calls the given block once for each element in the set, passing - # the element as parameter. Returns an enumerator if no block is - # given. - def each(&block) - block or return enum_for(T.must(__method__)) { size } - @hash.each_key(&block) - self - end - - # Adds the given object to the set and returns self. Use +merge+ to - # add many elements at once. - # - # Set[1, 2].add(3) #=> # - # Set[1, 2].add([3, 4]) #=> # - # Set[1, 2].add(2) #=> # - def add(o) - @hash[o] = true - self - end - alias << add - - # Adds the given object to the set and returns self. If the - # object is already in the set, returns nil. - # - # Set[1, 2].add?(3) #=> # - # Set[1, 2].add?([3, 4]) #=> # - # Set[1, 2].add?(2) #=> nil - def add?(o) - add(o) unless include?(o) - end - - # Deletes the given object from the set and returns self. Use +subtract+ to - # delete many items at once. - def delete(o) - @hash.delete(o) - self - end - - # Deletes the given object from the set and returns self. If the - # object is not in the set, returns nil. - def delete?(o) - delete(o) if include?(o) - end - - # Deletes every element of the set for which block evaluates to - # true, and returns self. Returns an enumerator if no block is - # given. - def delete_if - block_given? or return enum_for(T.must(__method__)) { size } - # @hash.delete_if should be faster, but using it breaks the order - # of enumeration in subclasses. - select { |o| yield o }.each { |o| @hash.delete(o) } - self - end - - # Deletes every element of the set for which block evaluates to - # false, and returns self. Returns an enumerator if no block is - # given. - def keep_if - block_given? or return enum_for(T.must(__method__)) { size } - # @hash.keep_if should be faster, but using it breaks the order of - # enumeration in subclasses. - reject { |o| yield o }.each { |o| @hash.delete(o) } - self - end - - # Replaces the elements with ones returned by collect(). - # Returns an enumerator if no block is given. - def collect! - block_given? or return enum_for(T.must(__method__)) { size } - set = self.class.new - each { |o| set << yield(o) } - replace(set) - end - alias map! collect! - - # Equivalent to Set#delete_if, but returns nil if no changes were - # made. Returns an enumerator if no block is given. - def reject!(&block) - block or return enum_for(T.must(__method__)) { size } - n = size - delete_if(&block) - self if size != n - end - - # Equivalent to Set#keep_if, but returns nil if no changes were - # made. Returns an enumerator if no block is given. - def select!(&block) - block or return enum_for(T.must(__method__)) { size } - n = size - keep_if(&block) - self if size != n - end - - # Equivalent to Set#select! - alias filter! select! - - # Merges the elements of the given enumerable object to the set and - # returns self. - def merge(enum) - if enum.instance_of?(self.class) - @hash.update(enum.instance_variable_get(:@hash)) - else - do_with_enum(enum) { |o| add(o) } - end - - self - end - - # Deletes every element that appears in the given enumerable object - # and returns self. - def subtract(enum) - do_with_enum(enum) { |o| delete(o) } - self - end - - # Returns a new set built by merging the set and the elements of the - # given enumerable object. - # - # Set[1, 2, 3] | Set[2, 4, 5] #=> # - # Set[1, 5, 'z'] | (1..6) #=> # - def |(enum) - dup.merge(enum) - end - alias + | - alias union | - - # Returns a new set built by duplicating the set, removing every - # element that appears in the given enumerable object. - # - # Set[1, 3, 5] - Set[1, 5] #=> # - # Set['a', 'b', 'z'] - ['a', 'c'] #=> # - def -(enum) - dup.subtract(enum) - end - alias difference - - - # Returns a new set containing elements common to the set and the - # given enumerable object. - # - # Set[1, 3, 5] & Set[3, 2, 1] #=> # - # Set['a', 'b', 'z'] & ['a', 'b', 'c'] #=> # - def &(enum) - n = self.class.new - do_with_enum(enum) { |o| n.add(o) if include?(o) } - n - end - alias intersection & - - # Returns a new set containing elements exclusive between the set - # and the given enumerable object. (set ^ enum) is equivalent to - # ((set | enum) - (set & enum)). - # - # Set[1, 2] ^ Set[2, 3] #=> # - # Set[1, 'b', 'c'] ^ ['b', 'd'] #=> # - def ^(enum) - n = CompiledSet.new(enum) - each { |o| n.add(o) unless n.delete?(o) } - n - end - - # Returns true if two sets are equal. The equality of each couple - # of elements is defined according to Object#eql?. - # - # Set[1, 2] == Set[2, 1] #=> true - # Set[1, 3, 5] == Set[1, 5] #=> false - # Set['a', 'b', 'c'] == Set['a', 'c', 'b'] #=> true - # Set['a', 'b', 'c'] == ['a', 'c', 'b'] #=> false - def ==(other) - if self.equal?(other) - true - elsif other.instance_of?(self.class) - @hash == other.instance_variable_get(:@hash) - elsif other.is_a?(CompiledSet) && self.size == other.size - other.all? { |o| @hash.include?(o) } - else - false - end - end - - def hash # :nodoc: - @hash.hash - end - - def eql?(o) # :nodoc: - return false unless o.is_a?(CompiledSet) - @hash.eql?(o.instance_variable_get(:@hash)) - end - - # Resets the internal state after modification to existing elements - # and returns self. - # - # Elements will be reindexed and deduplicated. - def reset - if @hash.respond_to?(:rehash) - @hash.rehash # This should perform frozenness check. - else - raise FrozenError, "can't modify frozen #{self.class.name}" if frozen? - end - self - end - - # Returns true if the given object is a member of the set, - # and false otherwise. - # - # Used in case statements: - # - # require 'set' - # - # case :apple - # when Set[:potato, :carrot] - # "vegetable" - # when Set[:apple, :banana] - # "fruit" - # end - # # => "fruit" - # - # Or by itself: - # - # Set[1, 2, 3] === 2 #=> true - # Set[1, 2, 3] === 4 #=> false - # - alias === include? - - # Classifies the set by the return value of the given block and - # returns a hash of {value => set of elements} pairs. The block is - # called once for each element of the set, passing the element as - # parameter. - # - # require 'set' - # files = Set.new(Dir.glob("*.rb")) - # hash = files.classify { |f| File.mtime(f).year } - # hash #=> {2000=>#, - # # 2001=>#, - # # 2002=>#} - # - # Returns an enumerator if no block is given. - def classify # :yields: o - block_given? or return enum_for(T.must(__method__)) { size } - - h = {} - - each { |i| - (h[yield(i)] ||= self.class.new).add(i) - } - - h - end - - # Divides the set into a set of subsets according to the commonality - # defined by the given block. - # - # If the arity of the block is 2, elements o1 and o2 are in common - # if block.call(o1, o2) is true. Otherwise, elements o1 and o2 are - # in common if block.call(o1) == block.call(o2). - # - # require 'set' - # numbers = Set[1, 3, 4, 6, 9, 10, 11] - # set = numbers.divide { |i,j| (i - j).abs == 1 } - # set #=> #, - # # #, - # # #, - # # #}> - # - # Returns an enumerator if no block is given. - # def divide(&func) - # func or return enum_for(__method__) { size } - - # if func.arity == 2 - # require 'tsort' - - # class << dig = {} # :nodoc: - # include TSort - - # alias tsort_each_node each_key - # def tsort_each_child(node, &block) - # fetch(node).each(&block) - # end - # end - - # each { |u| - # dig[u] = a = [] - # each{ |v| func.call(u, v) and a << v } - # } - - # set = Set.new() - # dig.each_strongly_connected_component { |css| - # set.add(self.class.new(css)) - # } - # set - # else - # Set.new(classify(&func).values) - # end - # end - - InspectKey = :__inspect_key__ # :nodoc: - - # Returns a string containing a human-readable representation of the - # set ("#"). - def inspect - ids = (Thread.current[InspectKey] ||= []) - - if ids.include?(object_id) - return sprintf('#<%s: {...}>', self.class.name) - end - - ids << object_id - begin - return sprintf('#<%s: {%s}>', self.class, to_a.inspect[1..-2]) - ensure - ids.pop - end - end - - alias to_s inspect - - def pretty_print(pp) # :nodoc: - pp.text sprintf('#<%s: {', self.class.name) - pp.nest(1) { - pp.seplist(self) { |o| - pp.pp o - } - } - pp.text "}>" - end - - def pretty_print_cycle(pp) # :nodoc: - pp.text sprintf('#<%s: {%s}>', self.class.name, empty? ? '' : '...') - end -end - -# -# SortedSet implements a Set that guarantees that its elements are -# yielded in sorted order (according to the return values of their -# #<=> methods) when iterating over them. -# -# All elements that are added to a SortedSet must respond to the <=> -# method for comparison. -# -# Also, all elements must be mutually comparable: el1 <=> -# el2 must not return nil for any elements el1 -# and el2, else an ArgumentError will be raised when -# iterating over the SortedSet. -# -# == Example -# -# require "set" -# -# set = SortedSet.new([2, 1, 5, 6, 4, 5, 3, 3, 3]) -# ary = [] -# -# set.each do |obj| -# ary << obj -# end -# -# p ary # => [1, 2, 3, 4, 5, 6] -# -# set2 = SortedSet.new([1, 2, "3"]) -# set2.each { |obj| } # => raises ArgumentError: comparison of Fixnum with String failed -# -class CompiledSortedSet < CompiledSet - @@setup = false - @@mutex = Mutex.new - - class << self - def [](*ary) # :nodoc: - new(ary) - end - - def setup # :nodoc: - @@setup and return - - @@mutex.synchronize do - # a hack to shut up warning - #alias_method :old_init, :initialize - - begin - require 'rbtree' - - module_eval <<-END, __FILE__, __LINE__+1 - def initialize(*args) - @hash = RBTree.new - super - end - - def add(o) - o.respond_to?(:<=>) or raise ArgumentError, "value must respond to <=>" - super - end - alias << add - END - rescue LoadError - module_eval <<-END, __FILE__, __LINE__+1 - def initialize(*args) - @keys = nil - super - end - - def clear - @keys = nil - super - end - - def replace(enum) - @keys = nil - super - end - - def add(o) - o.respond_to?(:<=>) or raise ArgumentError, "value must respond to <=>" - @keys = nil - super - end - alias << add - - def delete(o) - @keys = nil - @hash.delete(o) - self - end - - def delete_if - block_given? or return enum_for(__method__) { size } - n = @hash.size - super - @keys = nil if @hash.size != n - self - end - - def keep_if - block_given? or return enum_for(__method__) { size } - n = @hash.size - super - @keys = nil if @hash.size != n - self - end - - def merge(enum) - @keys = nil - super - end - - def each(&block) - block or return enum_for(__method__) { size } - to_a.each(&block) - self - end - - def to_a - (@keys = @hash.keys).sort! unless @keys - @keys - end - - def freeze - to_a - super - end - - def rehash - @keys = nil - super - end - END - end - # a hack to shut up warning - #remove_method :old_init - - @@setup = true - end - end - end - - def initialize(*args, &block) # :nodoc: - CompiledSortedSet.setup - @keys = nil - super - end -end - -module Enumerable - # Makes a set from the enumerable object with given arguments. - # Needs to +require "set"+ to use this method. - def to_compiled_set(klass = CompiledSet, *args, &block) - klass.new(self, *args, &block) - end -end - -# =begin -# == RestricedSet class -# RestricedSet implements a set with restrictions defined by a given -# block. -# -# === Super class -# Set -# -# === Class Methods -# --- RestricedSet::new(enum = nil) { |o| ... } -# --- RestricedSet::new(enum = nil) { |rset, o| ... } -# Creates a new restricted set containing the elements of the given -# enumerable object. Restrictions are defined by the given block. -# -# If the block's arity is 2, it is called with the RestrictedSet -# itself and an object to see if the object is allowed to be put in -# the set. -# -# Otherwise, the block is called with an object to see if the object -# is allowed to be put in the set. -# -# === Instance Methods -# --- restriction_proc -# Returns the restriction procedure of the set. -# -# =end -# -# class RestricedSet < Set -# def initialize(*args, &block) -# @proc = block or raise ArgumentError, "missing a block" -# -# if @proc.arity == 2 -# instance_eval %{ -# def add(o) -# @hash[o] = true if @proc.call(self, o) -# self -# end -# alias << add -# -# def add?(o) -# if include?(o) || !@proc.call(self, o) -# nil -# else -# @hash[o] = true -# self -# end -# end -# -# def replace(enum) -# enum.respond_to?(:each) or raise ArgumentError, "value must be enumerable" -# clear -# enum.each_entry { |o| add(o) } -# -# self -# end -# -# def merge(enum) -# enum.respond_to?(:each) or raise ArgumentError, "value must be enumerable" -# enum.each_entry { |o| add(o) } -# -# self -# end -# } -# else -# instance_eval %{ -# def add(o) -# if @proc.call(o) -# @hash[o] = true -# end -# self -# end -# alias << add -# -# def add?(o) -# if include?(o) || !@proc.call(o) -# nil -# else -# @hash[o] = true -# self -# end -# end -# } -# end -# -# super(*args) -# end -# -# def restriction_proc -# @proc -# end -# end - -# Tests have been moved to test/test_set.rb. - -### -### CompiledSet benchmark code for 'each'. -### - -s = CompiledSet.new(0..10) -i = 0 -j = 0 - -while i < 1_000_000 do - s.each { |x| j += 1 } - - i += 1 -end - -puts i -puts j diff --git a/test/testdata/ruby_benchmark/stripe/compiled_set_include_p.rb b/test/testdata/ruby_benchmark/stripe/compiled_set_include_p.rb deleted file mode 100644 index 258bbdec6e..0000000000 --- a/test/testdata/ruby_benchmark/stripe/compiled_set_include_p.rb +++ /dev/null @@ -1,942 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -# This file contains a copy of the set.rb class from Ruby 2.7.2, and some -# benchmark code at the very bottom. -# -# Unfortunately it is duplicated in a number of other benchmarks, because for -# the moment I don't see an easy way for multiple benchmarks to share a -# compiled file. -# -# Modifications made from original set.rb: -# -# 1. Classes renamed to "CompiledFoo" (e.g., Set->CompiledSet) -# 2. Miscellaneous 'T.must's to make Sorbet TC happy -# 3. Removed the Set#divide function (this uses 'class <<' in unsupported -# ways). -# -# TODO(aprocter): Deduplicate the set.rb code - -# See bottom of the file for the actual benchmark code. - -# -# set.rb - defines the Set class -#++ -# Copyright (c) 2002-2016 Akinori MUSHA -# -# Documentation by Akinori MUSHA and Gavin Sinclair. -# -# All rights reserved. You can redistribute and/or modify it under the same -# terms as Ruby. -# -# $Id$ -# -# == Overview -# -# This library provides the Set class, which deals with a collection -# of unordered values with no duplicates. It is a hybrid of Array's -# intuitive inter-operation facilities and Hash's fast lookup. If you -# need to keep values sorted in some order, use the SortedSet class. -# -# The method +to_set+ is added to Enumerable for convenience. -# -# See the Set and SortedSet documentation for examples of usage. - - -# -# Set implements a collection of unordered values with no duplicates. -# This is a hybrid of Array's intuitive inter-operation facilities and -# Hash's fast lookup. -# -# Set is easy to use with Enumerable objects (implementing +each+). -# Most of the initializer methods and binary operators accept generic -# Enumerable objects besides sets and arrays. An Enumerable object -# can be converted to Set using the +to_set+ method. -# -# Set uses Hash as storage, so you must note the following points: -# -# * Equality of elements is determined according to Object#eql? and -# Object#hash. Use Set#compare_by_identity to make a set compare -# its elements by their identity. -# * Set assumes that the identity of each element does not change -# while it is stored. Modifying an element of a set will render the -# set to an unreliable state. -# * When a string is to be stored, a frozen copy of the string is -# stored instead unless the original string is already frozen. -# -# == Comparison -# -# The comparison operators <, >, <=, and >= are implemented as -# shorthand for the {proper_,}{subset?,superset?} methods. However, -# the <=> operator is intentionally left out because not every pair of -# sets is comparable ({x, y} vs. {x, z} for example). -# -# == Example -# -# require 'set' -# s1 = Set[1, 2] #=> # -# s2 = [1, 2].to_set #=> # -# s1 == s2 #=> true -# s1.add("foo") #=> # -# s1.merge([2, 6]) #=> # -# s1.subset?(s2) #=> false -# s2.subset?(s1) #=> true -# -# == Contact -# -# - Akinori MUSHA (current maintainer) -# -class CompiledSet - include Enumerable - - # Creates a new set containing the given objects. - # - # Set[1, 2] # => # - # Set[1, 2, 1] # => # - # Set[1, 'c', :s] # => # - def self.[](*ary) - new(ary) - end - - # Creates a new set containing the elements of the given enumerable - # object. - # - # If a block is given, the elements of enum are preprocessed by the - # given block. - # - # Set.new([1, 2]) #=> # - # Set.new([1, 2, 1]) #=> # - # Set.new([1, 'c', :s]) #=> # - # Set.new(1..5) #=> # - # Set.new([1, 2, 3]) { |x| x * x } #=> # - def initialize(enum = nil, &block) # :yields: o - @hash ||= Hash.new(false) - - enum.nil? and return - - if block - do_with_enum(enum) { |o| add(block[o]) } - else - merge(enum) - end - end - - # Makes the set compare its elements by their identity and returns - # self. This method may not be supported by all subclasses of Set. - def compare_by_identity - if @hash.respond_to?(:compare_by_identity) - @hash.compare_by_identity - self - else - raise NotImplementedError, "#{self.class.name}\##{__method__} is not implemented" - end - end - - # Returns true if the set will compare its elements by their - # identity. Also see Set#compare_by_identity. - def compare_by_identity? - @hash.respond_to?(:compare_by_identity?) && @hash.compare_by_identity? - end - - def do_with_enum(enum, &block) # :nodoc: - if enum.respond_to?(:each_entry) - enum.each_entry(&block) if block - elsif enum.respond_to?(:each) - enum.each(&block) if block - else - raise ArgumentError, "value must be enumerable" - end - end - private :do_with_enum - - # Dup internal hash. - def initialize_dup(orig) - super - @hash = orig.instance_variable_get(:@hash).dup - end - - # Clone internal hash. - def initialize_clone(orig) - super - @hash = orig.instance_variable_get(:@hash).clone - end - - def freeze # :nodoc: - @hash.freeze - super - end - - # Returns the number of elements. - def size - @hash.size - end - alias length size - - # Returns true if the set contains no elements. - def empty? - @hash.empty? - end - - # Removes all elements and returns self. - # - # set = Set[1, 'c', :s] #=> # - # set.clear #=> # - # set #=> # - def clear - @hash.clear - self - end - - # Replaces the contents of the set with the contents of the given - # enumerable object and returns self. - # - # set = Set[1, 'c', :s] #=> # - # set.replace([1, 2]) #=> # - # set #=> # - def replace(enum) - if enum.instance_of?(self.class) - @hash.replace(enum.instance_variable_get(:@hash)) - self - else - do_with_enum(enum) # make sure enum is enumerable before calling clear - clear - merge(enum) - end - end - - # Converts the set to an array. The order of elements is uncertain. - # - # Set[1, 2].to_a #=> [1, 2] - # Set[1, 'c', :s].to_a #=> [1, "c", :s] - def to_a - @hash.keys - end - - # Returns self if no arguments are given. Otherwise, converts the - # set to another with klass.new(self, *args, &block). - # - # In subclasses, returns klass.new(self, *args, &block) unless - # overridden. - def to_compiled_set(klass = CompiledSet, *args, &block) - return self if instance_of?(CompiledSet) && klass == CompiledSet && block.nil? && args.empty? - klass.new(self, *args, &block) - end - - def flatten_merge(set, seen = CompiledSet.new) # :nodoc: - set.each { |e| - if e.is_a?(CompiledSet) - if seen.include?(e_id = e.object_id) - raise ArgumentError, "tried to flatten recursive CompiledSet" - end - - seen.add(e_id) - flatten_merge(e, seen) - seen.delete(e_id) - else - add(e) - end - } - - self - end - protected :flatten_merge - - # Returns a new set that is a copy of the set, flattening each - # containing set recursively. - def flatten - self.class.new.flatten_merge(self) - end - - # Equivalent to Set#flatten, but replaces the receiver with the - # result in place. Returns nil if no modifications were made. - def flatten! - replace(flatten()) if any? { |e| e.is_a?(CompiledSet) } - end - - # Returns true if the set contains the given object. - # - # Note that include? and member? do not test member - # equality using == as do other Enumerables. - # - # See also Enumerable#include? - def include?(o) - @hash[o] - end - alias member? include? - - # Returns true if the set is a superset of the given set. - def superset?(set) - case - when set.instance_of?(self.class) && @hash.respond_to?(:>=) - @hash >= set.instance_variable_get(:@hash) - when set.is_a?(CompiledSet) - size >= set.size && set.all? { |o| include?(o) } - else - raise ArgumentError, "value must be a set" - end - end - alias >= superset? - - # Returns true if the set is a proper superset of the given set. - def proper_superset?(set) - case - when set.instance_of?(self.class) && @hash.respond_to?(:>) - @hash > set.instance_variable_get(:@hash) - when set.is_a?(CompiledSet) - size > set.size && set.all? { |o| include?(o) } - else - raise ArgumentError, "value must be a set" - end - end - alias > proper_superset? - - # Returns true if the set is a subset of the given set. - def subset?(set) - case - when set.instance_of?(self.class) && @hash.respond_to?(:<=) - @hash <= set.instance_variable_get(:@hash) - when set.is_a?(CompiledSet) - size <= set.size && all? { |o| set.include?(o) } - else - raise ArgumentError, "value must be a set" - end - end - alias <= subset? - - # Returns true if the set is a proper subset of the given set. - def proper_subset?(set) - case - when set.instance_of?(self.class) && @hash.respond_to?(:<) - @hash < set.instance_variable_get(:@hash) - when set.is_a?(CompiledSet) - size < set.size && all? { |o| set.include?(o) } - else - raise ArgumentError, "value must be a set" - end - end - alias < proper_subset? - - # Returns true if the set and the given set have at least one - # element in common. - # - # Set[1, 2, 3].intersect? Set[4, 5] #=> false - # Set[1, 2, 3].intersect? Set[3, 4] #=> true - def intersect?(set) - set.is_a?(CompiledSet) or raise ArgumentError, "value must be a set" - if size < set.size - any? { |o| set.include?(o) } - else - set.any? { |o| include?(o) } - end - end - - # Returns true if the set and the given set have no element in - # common. This method is the opposite of +intersect?+. - # - # Set[1, 2, 3].disjoint? Set[3, 4] #=> false - # Set[1, 2, 3].disjoint? Set[4, 5] #=> true - def disjoint?(set) - !intersect?(set) - end - - # Calls the given block once for each element in the set, passing - # the element as parameter. Returns an enumerator if no block is - # given. - def each(&block) - block or return enum_for(T.must(__method__)) { size } - @hash.each_key(&block) - self - end - - # Adds the given object to the set and returns self. Use +merge+ to - # add many elements at once. - # - # Set[1, 2].add(3) #=> # - # Set[1, 2].add([3, 4]) #=> # - # Set[1, 2].add(2) #=> # - def add(o) - @hash[o] = true - self - end - alias << add - - # Adds the given object to the set and returns self. If the - # object is already in the set, returns nil. - # - # Set[1, 2].add?(3) #=> # - # Set[1, 2].add?([3, 4]) #=> # - # Set[1, 2].add?(2) #=> nil - def add?(o) - add(o) unless include?(o) - end - - # Deletes the given object from the set and returns self. Use +subtract+ to - # delete many items at once. - def delete(o) - @hash.delete(o) - self - end - - # Deletes the given object from the set and returns self. If the - # object is not in the set, returns nil. - def delete?(o) - delete(o) if include?(o) - end - - # Deletes every element of the set for which block evaluates to - # true, and returns self. Returns an enumerator if no block is - # given. - def delete_if - block_given? or return enum_for(T.must(__method__)) { size } - # @hash.delete_if should be faster, but using it breaks the order - # of enumeration in subclasses. - select { |o| yield o }.each { |o| @hash.delete(o) } - self - end - - # Deletes every element of the set for which block evaluates to - # false, and returns self. Returns an enumerator if no block is - # given. - def keep_if - block_given? or return enum_for(T.must(__method__)) { size } - # @hash.keep_if should be faster, but using it breaks the order of - # enumeration in subclasses. - reject { |o| yield o }.each { |o| @hash.delete(o) } - self - end - - # Replaces the elements with ones returned by collect(). - # Returns an enumerator if no block is given. - def collect! - block_given? or return enum_for(T.must(__method__)) { size } - set = self.class.new - each { |o| set << yield(o) } - replace(set) - end - alias map! collect! - - # Equivalent to Set#delete_if, but returns nil if no changes were - # made. Returns an enumerator if no block is given. - def reject!(&block) - block or return enum_for(T.must(__method__)) { size } - n = size - delete_if(&block) - self if size != n - end - - # Equivalent to Set#keep_if, but returns nil if no changes were - # made. Returns an enumerator if no block is given. - def select!(&block) - block or return enum_for(T.must(__method__)) { size } - n = size - keep_if(&block) - self if size != n - end - - # Equivalent to Set#select! - alias filter! select! - - # Merges the elements of the given enumerable object to the set and - # returns self. - def merge(enum) - if enum.instance_of?(self.class) - @hash.update(enum.instance_variable_get(:@hash)) - else - do_with_enum(enum) { |o| add(o) } - end - - self - end - - # Deletes every element that appears in the given enumerable object - # and returns self. - def subtract(enum) - do_with_enum(enum) { |o| delete(o) } - self - end - - # Returns a new set built by merging the set and the elements of the - # given enumerable object. - # - # Set[1, 2, 3] | Set[2, 4, 5] #=> # - # Set[1, 5, 'z'] | (1..6) #=> # - def |(enum) - dup.merge(enum) - end - alias + | - alias union | - - # Returns a new set built by duplicating the set, removing every - # element that appears in the given enumerable object. - # - # Set[1, 3, 5] - Set[1, 5] #=> # - # Set['a', 'b', 'z'] - ['a', 'c'] #=> # - def -(enum) - dup.subtract(enum) - end - alias difference - - - # Returns a new set containing elements common to the set and the - # given enumerable object. - # - # Set[1, 3, 5] & Set[3, 2, 1] #=> # - # Set['a', 'b', 'z'] & ['a', 'b', 'c'] #=> # - def &(enum) - n = self.class.new - do_with_enum(enum) { |o| n.add(o) if include?(o) } - n - end - alias intersection & - - # Returns a new set containing elements exclusive between the set - # and the given enumerable object. (set ^ enum) is equivalent to - # ((set | enum) - (set & enum)). - # - # Set[1, 2] ^ Set[2, 3] #=> # - # Set[1, 'b', 'c'] ^ ['b', 'd'] #=> # - def ^(enum) - n = CompiledSet.new(enum) - each { |o| n.add(o) unless n.delete?(o) } - n - end - - # Returns true if two sets are equal. The equality of each couple - # of elements is defined according to Object#eql?. - # - # Set[1, 2] == Set[2, 1] #=> true - # Set[1, 3, 5] == Set[1, 5] #=> false - # Set['a', 'b', 'c'] == Set['a', 'c', 'b'] #=> true - # Set['a', 'b', 'c'] == ['a', 'c', 'b'] #=> false - def ==(other) - if self.equal?(other) - true - elsif other.instance_of?(self.class) - @hash == other.instance_variable_get(:@hash) - elsif other.is_a?(CompiledSet) && self.size == other.size - other.all? { |o| @hash.include?(o) } - else - false - end - end - - def hash # :nodoc: - @hash.hash - end - - def eql?(o) # :nodoc: - return false unless o.is_a?(CompiledSet) - @hash.eql?(o.instance_variable_get(:@hash)) - end - - # Resets the internal state after modification to existing elements - # and returns self. - # - # Elements will be reindexed and deduplicated. - def reset - if @hash.respond_to?(:rehash) - @hash.rehash # This should perform frozenness check. - else - raise FrozenError, "can't modify frozen #{self.class.name}" if frozen? - end - self - end - - # Returns true if the given object is a member of the set, - # and false otherwise. - # - # Used in case statements: - # - # require 'set' - # - # case :apple - # when Set[:potato, :carrot] - # "vegetable" - # when Set[:apple, :banana] - # "fruit" - # end - # # => "fruit" - # - # Or by itself: - # - # Set[1, 2, 3] === 2 #=> true - # Set[1, 2, 3] === 4 #=> false - # - alias === include? - - # Classifies the set by the return value of the given block and - # returns a hash of {value => set of elements} pairs. The block is - # called once for each element of the set, passing the element as - # parameter. - # - # require 'set' - # files = Set.new(Dir.glob("*.rb")) - # hash = files.classify { |f| File.mtime(f).year } - # hash #=> {2000=>#, - # # 2001=>#, - # # 2002=>#} - # - # Returns an enumerator if no block is given. - def classify # :yields: o - block_given? or return enum_for(T.must(__method__)) { size } - - h = {} - - each { |i| - (h[yield(i)] ||= self.class.new).add(i) - } - - h - end - - # Divides the set into a set of subsets according to the commonality - # defined by the given block. - # - # If the arity of the block is 2, elements o1 and o2 are in common - # if block.call(o1, o2) is true. Otherwise, elements o1 and o2 are - # in common if block.call(o1) == block.call(o2). - # - # require 'set' - # numbers = Set[1, 3, 4, 6, 9, 10, 11] - # set = numbers.divide { |i,j| (i - j).abs == 1 } - # set #=> #, - # # #, - # # #, - # # #}> - # - # Returns an enumerator if no block is given. - # def divide(&func) - # func or return enum_for(__method__) { size } - - # if func.arity == 2 - # require 'tsort' - - # class << dig = {} # :nodoc: - # include TSort - - # alias tsort_each_node each_key - # def tsort_each_child(node, &block) - # fetch(node).each(&block) - # end - # end - - # each { |u| - # dig[u] = a = [] - # each{ |v| func.call(u, v) and a << v } - # } - - # set = Set.new() - # dig.each_strongly_connected_component { |css| - # set.add(self.class.new(css)) - # } - # set - # else - # Set.new(classify(&func).values) - # end - # end - - InspectKey = :__inspect_key__ # :nodoc: - - # Returns a string containing a human-readable representation of the - # set ("#"). - def inspect - ids = (Thread.current[InspectKey] ||= []) - - if ids.include?(object_id) - return sprintf('#<%s: {...}>', self.class.name) - end - - ids << object_id - begin - return sprintf('#<%s: {%s}>', self.class, to_a.inspect[1..-2]) - ensure - ids.pop - end - end - - alias to_s inspect - - def pretty_print(pp) # :nodoc: - pp.text sprintf('#<%s: {', self.class.name) - pp.nest(1) { - pp.seplist(self) { |o| - pp.pp o - } - } - pp.text "}>" - end - - def pretty_print_cycle(pp) # :nodoc: - pp.text sprintf('#<%s: {%s}>', self.class.name, empty? ? '' : '...') - end -end - -# -# SortedSet implements a Set that guarantees that its elements are -# yielded in sorted order (according to the return values of their -# #<=> methods) when iterating over them. -# -# All elements that are added to a SortedSet must respond to the <=> -# method for comparison. -# -# Also, all elements must be mutually comparable: el1 <=> -# el2 must not return nil for any elements el1 -# and el2, else an ArgumentError will be raised when -# iterating over the SortedSet. -# -# == Example -# -# require "set" -# -# set = SortedSet.new([2, 1, 5, 6, 4, 5, 3, 3, 3]) -# ary = [] -# -# set.each do |obj| -# ary << obj -# end -# -# p ary # => [1, 2, 3, 4, 5, 6] -# -# set2 = SortedSet.new([1, 2, "3"]) -# set2.each { |obj| } # => raises ArgumentError: comparison of Fixnum with String failed -# -class CompiledSortedSet < CompiledSet - @@setup = false - @@mutex = Mutex.new - - class << self - def [](*ary) # :nodoc: - new(ary) - end - - def setup # :nodoc: - @@setup and return - - @@mutex.synchronize do - # a hack to shut up warning - #alias_method :old_init, :initialize - - begin - require 'rbtree' - - module_eval <<-END, __FILE__, __LINE__+1 - def initialize(*args) - @hash = RBTree.new - super - end - - def add(o) - o.respond_to?(:<=>) or raise ArgumentError, "value must respond to <=>" - super - end - alias << add - END - rescue LoadError - module_eval <<-END, __FILE__, __LINE__+1 - def initialize(*args) - @keys = nil - super - end - - def clear - @keys = nil - super - end - - def replace(enum) - @keys = nil - super - end - - def add(o) - o.respond_to?(:<=>) or raise ArgumentError, "value must respond to <=>" - @keys = nil - super - end - alias << add - - def delete(o) - @keys = nil - @hash.delete(o) - self - end - - def delete_if - block_given? or return enum_for(__method__) { size } - n = @hash.size - super - @keys = nil if @hash.size != n - self - end - - def keep_if - block_given? or return enum_for(__method__) { size } - n = @hash.size - super - @keys = nil if @hash.size != n - self - end - - def merge(enum) - @keys = nil - super - end - - def each(&block) - block or return enum_for(__method__) { size } - to_a.each(&block) - self - end - - def to_a - (@keys = @hash.keys).sort! unless @keys - @keys - end - - def freeze - to_a - super - end - - def rehash - @keys = nil - super - end - END - end - # a hack to shut up warning - #remove_method :old_init - - @@setup = true - end - end - end - - def initialize(*args, &block) # :nodoc: - CompiledSortedSet.setup - @keys = nil - super - end -end - -module Enumerable - # Makes a set from the enumerable object with given arguments. - # Needs to +require "set"+ to use this method. - def to_compiled_set(klass = CompiledSet, *args, &block) - klass.new(self, *args, &block) - end -end - -# =begin -# == RestricedSet class -# RestricedSet implements a set with restrictions defined by a given -# block. -# -# === Super class -# Set -# -# === Class Methods -# --- RestricedSet::new(enum = nil) { |o| ... } -# --- RestricedSet::new(enum = nil) { |rset, o| ... } -# Creates a new restricted set containing the elements of the given -# enumerable object. Restrictions are defined by the given block. -# -# If the block's arity is 2, it is called with the RestrictedSet -# itself and an object to see if the object is allowed to be put in -# the set. -# -# Otherwise, the block is called with an object to see if the object -# is allowed to be put in the set. -# -# === Instance Methods -# --- restriction_proc -# Returns the restriction procedure of the set. -# -# =end -# -# class RestricedSet < Set -# def initialize(*args, &block) -# @proc = block or raise ArgumentError, "missing a block" -# -# if @proc.arity == 2 -# instance_eval %{ -# def add(o) -# @hash[o] = true if @proc.call(self, o) -# self -# end -# alias << add -# -# def add?(o) -# if include?(o) || !@proc.call(self, o) -# nil -# else -# @hash[o] = true -# self -# end -# end -# -# def replace(enum) -# enum.respond_to?(:each) or raise ArgumentError, "value must be enumerable" -# clear -# enum.each_entry { |o| add(o) } -# -# self -# end -# -# def merge(enum) -# enum.respond_to?(:each) or raise ArgumentError, "value must be enumerable" -# enum.each_entry { |o| add(o) } -# -# self -# end -# } -# else -# instance_eval %{ -# def add(o) -# if @proc.call(o) -# @hash[o] = true -# end -# self -# end -# alias << add -# -# def add?(o) -# if include?(o) || !@proc.call(o) -# nil -# else -# @hash[o] = true -# self -# end -# end -# } -# end -# -# super(*args) -# end -# -# def restriction_proc -# @proc -# end -# end - -# Tests have been moved to test/test_set.rb. - -### -### CompiledSet benchmark code for 'include?'. -### - -s = CompiledSet.new(1..1000000) - -i = 0 -j = 0 - -while i < 1_000_000 do - if s.include?(i) - j += 1 - end - - i += 1 -end - -puts i -puts j diff --git a/test/testdata/ruby_benchmark/stripe/compiled_set_initialize_block.rb b/test/testdata/ruby_benchmark/stripe/compiled_set_initialize_block.rb deleted file mode 100644 index decb84adb4..0000000000 --- a/test/testdata/ruby_benchmark/stripe/compiled_set_initialize_block.rb +++ /dev/null @@ -1,938 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -# This file contains a copy of the set.rb class from Ruby 2.7.2, and some -# benchmark code at the very bottom. -# -# Unfortunately it is duplicated in a number of other benchmarks, because for -# the moment I don't see an easy way for multiple benchmarks to share a -# compiled file. -# -# Modifications made from original set.rb: -# -# 1. Classes renamed to "CompiledFoo" (e.g., Set->CompiledSet) -# 2. Miscellaneous 'T.must's to make Sorbet TC happy -# 3. Removed the Set#divide function (this uses 'class <<' in unsupported -# ways). -# -# TODO(aprocter): Deduplicate the set.rb code - -# See bottom of the file for the actual benchmark code. - -# -# set.rb - defines the Set class -#++ -# Copyright (c) 2002-2016 Akinori MUSHA -# -# Documentation by Akinori MUSHA and Gavin Sinclair. -# -# All rights reserved. You can redistribute and/or modify it under the same -# terms as Ruby. -# -# $Id$ -# -# == Overview -# -# This library provides the Set class, which deals with a collection -# of unordered values with no duplicates. It is a hybrid of Array's -# intuitive inter-operation facilities and Hash's fast lookup. If you -# need to keep values sorted in some order, use the SortedSet class. -# -# The method +to_set+ is added to Enumerable for convenience. -# -# See the Set and SortedSet documentation for examples of usage. - - -# -# Set implements a collection of unordered values with no duplicates. -# This is a hybrid of Array's intuitive inter-operation facilities and -# Hash's fast lookup. -# -# Set is easy to use with Enumerable objects (implementing +each+). -# Most of the initializer methods and binary operators accept generic -# Enumerable objects besides sets and arrays. An Enumerable object -# can be converted to Set using the +to_set+ method. -# -# Set uses Hash as storage, so you must note the following points: -# -# * Equality of elements is determined according to Object#eql? and -# Object#hash. Use Set#compare_by_identity to make a set compare -# its elements by their identity. -# * Set assumes that the identity of each element does not change -# while it is stored. Modifying an element of a set will render the -# set to an unreliable state. -# * When a string is to be stored, a frozen copy of the string is -# stored instead unless the original string is already frozen. -# -# == Comparison -# -# The comparison operators <, >, <=, and >= are implemented as -# shorthand for the {proper_,}{subset?,superset?} methods. However, -# the <=> operator is intentionally left out because not every pair of -# sets is comparable ({x, y} vs. {x, z} for example). -# -# == Example -# -# require 'set' -# s1 = Set[1, 2] #=> # -# s2 = [1, 2].to_set #=> # -# s1 == s2 #=> true -# s1.add("foo") #=> # -# s1.merge([2, 6]) #=> # -# s1.subset?(s2) #=> false -# s2.subset?(s1) #=> true -# -# == Contact -# -# - Akinori MUSHA (current maintainer) -# -class CompiledSet - include Enumerable - - # Creates a new set containing the given objects. - # - # Set[1, 2] # => # - # Set[1, 2, 1] # => # - # Set[1, 'c', :s] # => # - def self.[](*ary) - new(ary) - end - - # Creates a new set containing the elements of the given enumerable - # object. - # - # If a block is given, the elements of enum are preprocessed by the - # given block. - # - # Set.new([1, 2]) #=> # - # Set.new([1, 2, 1]) #=> # - # Set.new([1, 'c', :s]) #=> # - # Set.new(1..5) #=> # - # Set.new([1, 2, 3]) { |x| x * x } #=> # - def initialize(enum = nil, &block) # :yields: o - @hash ||= Hash.new(false) - - enum.nil? and return - - if block - do_with_enum(enum) { |o| add(block[o]) } - else - merge(enum) - end - end - - # Makes the set compare its elements by their identity and returns - # self. This method may not be supported by all subclasses of Set. - def compare_by_identity - if @hash.respond_to?(:compare_by_identity) - @hash.compare_by_identity - self - else - raise NotImplementedError, "#{self.class.name}\##{__method__} is not implemented" - end - end - - # Returns true if the set will compare its elements by their - # identity. Also see Set#compare_by_identity. - def compare_by_identity? - @hash.respond_to?(:compare_by_identity?) && @hash.compare_by_identity? - end - - def do_with_enum(enum, &block) # :nodoc: - if enum.respond_to?(:each_entry) - enum.each_entry(&block) if block - elsif enum.respond_to?(:each) - enum.each(&block) if block - else - raise ArgumentError, "value must be enumerable" - end - end - private :do_with_enum - - # Dup internal hash. - def initialize_dup(orig) - super - @hash = orig.instance_variable_get(:@hash).dup - end - - # Clone internal hash. - def initialize_clone(orig) - super - @hash = orig.instance_variable_get(:@hash).clone - end - - def freeze # :nodoc: - @hash.freeze - super - end - - # Returns the number of elements. - def size - @hash.size - end - alias length size - - # Returns true if the set contains no elements. - def empty? - @hash.empty? - end - - # Removes all elements and returns self. - # - # set = Set[1, 'c', :s] #=> # - # set.clear #=> # - # set #=> # - def clear - @hash.clear - self - end - - # Replaces the contents of the set with the contents of the given - # enumerable object and returns self. - # - # set = Set[1, 'c', :s] #=> # - # set.replace([1, 2]) #=> # - # set #=> # - def replace(enum) - if enum.instance_of?(self.class) - @hash.replace(enum.instance_variable_get(:@hash)) - self - else - do_with_enum(enum) # make sure enum is enumerable before calling clear - clear - merge(enum) - end - end - - # Converts the set to an array. The order of elements is uncertain. - # - # Set[1, 2].to_a #=> [1, 2] - # Set[1, 'c', :s].to_a #=> [1, "c", :s] - def to_a - @hash.keys - end - - # Returns self if no arguments are given. Otherwise, converts the - # set to another with klass.new(self, *args, &block). - # - # In subclasses, returns klass.new(self, *args, &block) unless - # overridden. - def to_compiled_set(klass = CompiledSet, *args, &block) - return self if instance_of?(CompiledSet) && klass == CompiledSet && block.nil? && args.empty? - klass.new(self, *args, &block) - end - - def flatten_merge(set, seen = CompiledSet.new) # :nodoc: - set.each { |e| - if e.is_a?(CompiledSet) - if seen.include?(e_id = e.object_id) - raise ArgumentError, "tried to flatten recursive CompiledSet" - end - - seen.add(e_id) - flatten_merge(e, seen) - seen.delete(e_id) - else - add(e) - end - } - - self - end - protected :flatten_merge - - # Returns a new set that is a copy of the set, flattening each - # containing set recursively. - def flatten - self.class.new.flatten_merge(self) - end - - # Equivalent to Set#flatten, but replaces the receiver with the - # result in place. Returns nil if no modifications were made. - def flatten! - replace(flatten()) if any? { |e| e.is_a?(CompiledSet) } - end - - # Returns true if the set contains the given object. - # - # Note that include? and member? do not test member - # equality using == as do other Enumerables. - # - # See also Enumerable#include? - def include?(o) - @hash[o] - end - alias member? include? - - # Returns true if the set is a superset of the given set. - def superset?(set) - case - when set.instance_of?(self.class) && @hash.respond_to?(:>=) - @hash >= set.instance_variable_get(:@hash) - when set.is_a?(CompiledSet) - size >= set.size && set.all? { |o| include?(o) } - else - raise ArgumentError, "value must be a set" - end - end - alias >= superset? - - # Returns true if the set is a proper superset of the given set. - def proper_superset?(set) - case - when set.instance_of?(self.class) && @hash.respond_to?(:>) - @hash > set.instance_variable_get(:@hash) - when set.is_a?(CompiledSet) - size > set.size && set.all? { |o| include?(o) } - else - raise ArgumentError, "value must be a set" - end - end - alias > proper_superset? - - # Returns true if the set is a subset of the given set. - def subset?(set) - case - when set.instance_of?(self.class) && @hash.respond_to?(:<=) - @hash <= set.instance_variable_get(:@hash) - when set.is_a?(CompiledSet) - size <= set.size && all? { |o| set.include?(o) } - else - raise ArgumentError, "value must be a set" - end - end - alias <= subset? - - # Returns true if the set is a proper subset of the given set. - def proper_subset?(set) - case - when set.instance_of?(self.class) && @hash.respond_to?(:<) - @hash < set.instance_variable_get(:@hash) - when set.is_a?(CompiledSet) - size < set.size && all? { |o| set.include?(o) } - else - raise ArgumentError, "value must be a set" - end - end - alias < proper_subset? - - # Returns true if the set and the given set have at least one - # element in common. - # - # Set[1, 2, 3].intersect? Set[4, 5] #=> false - # Set[1, 2, 3].intersect? Set[3, 4] #=> true - def intersect?(set) - set.is_a?(CompiledSet) or raise ArgumentError, "value must be a set" - if size < set.size - any? { |o| set.include?(o) } - else - set.any? { |o| include?(o) } - end - end - - # Returns true if the set and the given set have no element in - # common. This method is the opposite of +intersect?+. - # - # Set[1, 2, 3].disjoint? Set[3, 4] #=> false - # Set[1, 2, 3].disjoint? Set[4, 5] #=> true - def disjoint?(set) - !intersect?(set) - end - - # Calls the given block once for each element in the set, passing - # the element as parameter. Returns an enumerator if no block is - # given. - def each(&block) - block or return enum_for(T.must(__method__)) { size } - @hash.each_key(&block) - self - end - - # Adds the given object to the set and returns self. Use +merge+ to - # add many elements at once. - # - # Set[1, 2].add(3) #=> # - # Set[1, 2].add([3, 4]) #=> # - # Set[1, 2].add(2) #=> # - def add(o) - @hash[o] = true - self - end - alias << add - - # Adds the given object to the set and returns self. If the - # object is already in the set, returns nil. - # - # Set[1, 2].add?(3) #=> # - # Set[1, 2].add?([3, 4]) #=> # - # Set[1, 2].add?(2) #=> nil - def add?(o) - add(o) unless include?(o) - end - - # Deletes the given object from the set and returns self. Use +subtract+ to - # delete many items at once. - def delete(o) - @hash.delete(o) - self - end - - # Deletes the given object from the set and returns self. If the - # object is not in the set, returns nil. - def delete?(o) - delete(o) if include?(o) - end - - # Deletes every element of the set for which block evaluates to - # true, and returns self. Returns an enumerator if no block is - # given. - def delete_if - block_given? or return enum_for(T.must(__method__)) { size } - # @hash.delete_if should be faster, but using it breaks the order - # of enumeration in subclasses. - select { |o| yield o }.each { |o| @hash.delete(o) } - self - end - - # Deletes every element of the set for which block evaluates to - # false, and returns self. Returns an enumerator if no block is - # given. - def keep_if - block_given? or return enum_for(T.must(__method__)) { size } - # @hash.keep_if should be faster, but using it breaks the order of - # enumeration in subclasses. - reject { |o| yield o }.each { |o| @hash.delete(o) } - self - end - - # Replaces the elements with ones returned by collect(). - # Returns an enumerator if no block is given. - def collect! - block_given? or return enum_for(T.must(__method__)) { size } - set = self.class.new - each { |o| set << yield(o) } - replace(set) - end - alias map! collect! - - # Equivalent to Set#delete_if, but returns nil if no changes were - # made. Returns an enumerator if no block is given. - def reject!(&block) - block or return enum_for(T.must(__method__)) { size } - n = size - delete_if(&block) - self if size != n - end - - # Equivalent to Set#keep_if, but returns nil if no changes were - # made. Returns an enumerator if no block is given. - def select!(&block) - block or return enum_for(T.must(__method__)) { size } - n = size - keep_if(&block) - self if size != n - end - - # Equivalent to Set#select! - alias filter! select! - - # Merges the elements of the given enumerable object to the set and - # returns self. - def merge(enum) - if enum.instance_of?(self.class) - @hash.update(enum.instance_variable_get(:@hash)) - else - do_with_enum(enum) { |o| add(o) } - end - - self - end - - # Deletes every element that appears in the given enumerable object - # and returns self. - def subtract(enum) - do_with_enum(enum) { |o| delete(o) } - self - end - - # Returns a new set built by merging the set and the elements of the - # given enumerable object. - # - # Set[1, 2, 3] | Set[2, 4, 5] #=> # - # Set[1, 5, 'z'] | (1..6) #=> # - def |(enum) - dup.merge(enum) - end - alias + | - alias union | - - # Returns a new set built by duplicating the set, removing every - # element that appears in the given enumerable object. - # - # Set[1, 3, 5] - Set[1, 5] #=> # - # Set['a', 'b', 'z'] - ['a', 'c'] #=> # - def -(enum) - dup.subtract(enum) - end - alias difference - - - # Returns a new set containing elements common to the set and the - # given enumerable object. - # - # Set[1, 3, 5] & Set[3, 2, 1] #=> # - # Set['a', 'b', 'z'] & ['a', 'b', 'c'] #=> # - def &(enum) - n = self.class.new - do_with_enum(enum) { |o| n.add(o) if include?(o) } - n - end - alias intersection & - - # Returns a new set containing elements exclusive between the set - # and the given enumerable object. (set ^ enum) is equivalent to - # ((set | enum) - (set & enum)). - # - # Set[1, 2] ^ Set[2, 3] #=> # - # Set[1, 'b', 'c'] ^ ['b', 'd'] #=> # - def ^(enum) - n = CompiledSet.new(enum) - each { |o| n.add(o) unless n.delete?(o) } - n - end - - # Returns true if two sets are equal. The equality of each couple - # of elements is defined according to Object#eql?. - # - # Set[1, 2] == Set[2, 1] #=> true - # Set[1, 3, 5] == Set[1, 5] #=> false - # Set['a', 'b', 'c'] == Set['a', 'c', 'b'] #=> true - # Set['a', 'b', 'c'] == ['a', 'c', 'b'] #=> false - def ==(other) - if self.equal?(other) - true - elsif other.instance_of?(self.class) - @hash == other.instance_variable_get(:@hash) - elsif other.is_a?(CompiledSet) && self.size == other.size - other.all? { |o| @hash.include?(o) } - else - false - end - end - - def hash # :nodoc: - @hash.hash - end - - def eql?(o) # :nodoc: - return false unless o.is_a?(CompiledSet) - @hash.eql?(o.instance_variable_get(:@hash)) - end - - # Resets the internal state after modification to existing elements - # and returns self. - # - # Elements will be reindexed and deduplicated. - def reset - if @hash.respond_to?(:rehash) - @hash.rehash # This should perform frozenness check. - else - raise FrozenError, "can't modify frozen #{self.class.name}" if frozen? - end - self - end - - # Returns true if the given object is a member of the set, - # and false otherwise. - # - # Used in case statements: - # - # require 'set' - # - # case :apple - # when Set[:potato, :carrot] - # "vegetable" - # when Set[:apple, :banana] - # "fruit" - # end - # # => "fruit" - # - # Or by itself: - # - # Set[1, 2, 3] === 2 #=> true - # Set[1, 2, 3] === 4 #=> false - # - alias === include? - - # Classifies the set by the return value of the given block and - # returns a hash of {value => set of elements} pairs. The block is - # called once for each element of the set, passing the element as - # parameter. - # - # require 'set' - # files = Set.new(Dir.glob("*.rb")) - # hash = files.classify { |f| File.mtime(f).year } - # hash #=> {2000=>#, - # # 2001=>#, - # # 2002=>#} - # - # Returns an enumerator if no block is given. - def classify # :yields: o - block_given? or return enum_for(T.must(__method__)) { size } - - h = {} - - each { |i| - (h[yield(i)] ||= self.class.new).add(i) - } - - h - end - - # Divides the set into a set of subsets according to the commonality - # defined by the given block. - # - # If the arity of the block is 2, elements o1 and o2 are in common - # if block.call(o1, o2) is true. Otherwise, elements o1 and o2 are - # in common if block.call(o1) == block.call(o2). - # - # require 'set' - # numbers = Set[1, 3, 4, 6, 9, 10, 11] - # set = numbers.divide { |i,j| (i - j).abs == 1 } - # set #=> #, - # # #, - # # #, - # # #}> - # - # Returns an enumerator if no block is given. - # def divide(&func) - # func or return enum_for(__method__) { size } - - # if func.arity == 2 - # require 'tsort' - - # class << dig = {} # :nodoc: - # include TSort - - # alias tsort_each_node each_key - # def tsort_each_child(node, &block) - # fetch(node).each(&block) - # end - # end - - # each { |u| - # dig[u] = a = [] - # each{ |v| func.call(u, v) and a << v } - # } - - # set = Set.new() - # dig.each_strongly_connected_component { |css| - # set.add(self.class.new(css)) - # } - # set - # else - # Set.new(classify(&func).values) - # end - # end - - InspectKey = :__inspect_key__ # :nodoc: - - # Returns a string containing a human-readable representation of the - # set ("#"). - def inspect - ids = (Thread.current[InspectKey] ||= []) - - if ids.include?(object_id) - return sprintf('#<%s: {...}>', self.class.name) - end - - ids << object_id - begin - return sprintf('#<%s: {%s}>', self.class, to_a.inspect[1..-2]) - ensure - ids.pop - end - end - - alias to_s inspect - - def pretty_print(pp) # :nodoc: - pp.text sprintf('#<%s: {', self.class.name) - pp.nest(1) { - pp.seplist(self) { |o| - pp.pp o - } - } - pp.text "}>" - end - - def pretty_print_cycle(pp) # :nodoc: - pp.text sprintf('#<%s: {%s}>', self.class.name, empty? ? '' : '...') - end -end - -# -# SortedSet implements a Set that guarantees that its elements are -# yielded in sorted order (according to the return values of their -# #<=> methods) when iterating over them. -# -# All elements that are added to a SortedSet must respond to the <=> -# method for comparison. -# -# Also, all elements must be mutually comparable: el1 <=> -# el2 must not return nil for any elements el1 -# and el2, else an ArgumentError will be raised when -# iterating over the SortedSet. -# -# == Example -# -# require "set" -# -# set = SortedSet.new([2, 1, 5, 6, 4, 5, 3, 3, 3]) -# ary = [] -# -# set.each do |obj| -# ary << obj -# end -# -# p ary # => [1, 2, 3, 4, 5, 6] -# -# set2 = SortedSet.new([1, 2, "3"]) -# set2.each { |obj| } # => raises ArgumentError: comparison of Fixnum with String failed -# -class CompiledSortedSet < CompiledSet - @@setup = false - @@mutex = Mutex.new - - class << self - def [](*ary) # :nodoc: - new(ary) - end - - def setup # :nodoc: - @@setup and return - - @@mutex.synchronize do - # a hack to shut up warning - #alias_method :old_init, :initialize - - begin - require 'rbtree' - - module_eval <<-END, __FILE__, __LINE__+1 - def initialize(*args) - @hash = RBTree.new - super - end - - def add(o) - o.respond_to?(:<=>) or raise ArgumentError, "value must respond to <=>" - super - end - alias << add - END - rescue LoadError - module_eval <<-END, __FILE__, __LINE__+1 - def initialize(*args) - @keys = nil - super - end - - def clear - @keys = nil - super - end - - def replace(enum) - @keys = nil - super - end - - def add(o) - o.respond_to?(:<=>) or raise ArgumentError, "value must respond to <=>" - @keys = nil - super - end - alias << add - - def delete(o) - @keys = nil - @hash.delete(o) - self - end - - def delete_if - block_given? or return enum_for(__method__) { size } - n = @hash.size - super - @keys = nil if @hash.size != n - self - end - - def keep_if - block_given? or return enum_for(__method__) { size } - n = @hash.size - super - @keys = nil if @hash.size != n - self - end - - def merge(enum) - @keys = nil - super - end - - def each(&block) - block or return enum_for(__method__) { size } - to_a.each(&block) - self - end - - def to_a - (@keys = @hash.keys).sort! unless @keys - @keys - end - - def freeze - to_a - super - end - - def rehash - @keys = nil - super - end - END - end - # a hack to shut up warning - #remove_method :old_init - - @@setup = true - end - end - end - - def initialize(*args, &block) # :nodoc: - CompiledSortedSet.setup - @keys = nil - super - end -end - -module Enumerable - # Makes a set from the enumerable object with given arguments. - # Needs to +require "set"+ to use this method. - def to_compiled_set(klass = CompiledSet, *args, &block) - klass.new(self, *args, &block) - end -end - -# =begin -# == RestricedSet class -# RestricedSet implements a set with restrictions defined by a given -# block. -# -# === Super class -# Set -# -# === Class Methods -# --- RestricedSet::new(enum = nil) { |o| ... } -# --- RestricedSet::new(enum = nil) { |rset, o| ... } -# Creates a new restricted set containing the elements of the given -# enumerable object. Restrictions are defined by the given block. -# -# If the block's arity is 2, it is called with the RestrictedSet -# itself and an object to see if the object is allowed to be put in -# the set. -# -# Otherwise, the block is called with an object to see if the object -# is allowed to be put in the set. -# -# === Instance Methods -# --- restriction_proc -# Returns the restriction procedure of the set. -# -# =end -# -# class RestricedSet < Set -# def initialize(*args, &block) -# @proc = block or raise ArgumentError, "missing a block" -# -# if @proc.arity == 2 -# instance_eval %{ -# def add(o) -# @hash[o] = true if @proc.call(self, o) -# self -# end -# alias << add -# -# def add?(o) -# if include?(o) || !@proc.call(self, o) -# nil -# else -# @hash[o] = true -# self -# end -# end -# -# def replace(enum) -# enum.respond_to?(:each) or raise ArgumentError, "value must be enumerable" -# clear -# enum.each_entry { |o| add(o) } -# -# self -# end -# -# def merge(enum) -# enum.respond_to?(:each) or raise ArgumentError, "value must be enumerable" -# enum.each_entry { |o| add(o) } -# -# self -# end -# } -# else -# instance_eval %{ -# def add(o) -# if @proc.call(o) -# @hash[o] = true -# end -# self -# end -# alias << add -# -# def add?(o) -# if include?(o) || !@proc.call(o) -# nil -# else -# @hash[o] = true -# self -# end -# end -# } -# end -# -# super(*args) -# end -# -# def restriction_proc -# @proc -# end -# end - -# Tests have been moved to test/test_set.rb. - -### -### CompiledSet benchmark code for 'initialize' (with block set). -### - -sets = [] -i = 0 - -while i < 1_000_000 do - sets << CompiledSet.new([1,2,3,4,5]) { |x| x+x } - - i += 1 -end - -puts i -puts sets.size diff --git a/test/testdata/ruby_benchmark/stripe/compiled_set_initialize_empty.rb b/test/testdata/ruby_benchmark/stripe/compiled_set_initialize_empty.rb deleted file mode 100644 index 6984e2b1b9..0000000000 --- a/test/testdata/ruby_benchmark/stripe/compiled_set_initialize_empty.rb +++ /dev/null @@ -1,938 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -# This file contains a copy of the set.rb class from Ruby 2.7.2, and some -# benchmark code at the very bottom. -# -# Unfortunately it is duplicated in a number of other benchmarks, because for -# the moment I don't see an easy way for multiple benchmarks to share a -# compiled file. -# -# Modifications made from original set.rb: -# -# 1. Classes renamed to "CompiledFoo" (e.g., Set->CompiledSet) -# 2. Miscellaneous 'T.must's to make Sorbet TC happy -# 3. Removed the Set#divide function (this uses 'class <<' in unsupported -# ways). -# -# TODO(aprocter): Deduplicate the set.rb code - -# See bottom of the file for the actual benchmark code. - -# -# set.rb - defines the Set class -#++ -# Copyright (c) 2002-2016 Akinori MUSHA -# -# Documentation by Akinori MUSHA and Gavin Sinclair. -# -# All rights reserved. You can redistribute and/or modify it under the same -# terms as Ruby. -# -# $Id$ -# -# == Overview -# -# This library provides the Set class, which deals with a collection -# of unordered values with no duplicates. It is a hybrid of Array's -# intuitive inter-operation facilities and Hash's fast lookup. If you -# need to keep values sorted in some order, use the SortedSet class. -# -# The method +to_set+ is added to Enumerable for convenience. -# -# See the Set and SortedSet documentation for examples of usage. - - -# -# Set implements a collection of unordered values with no duplicates. -# This is a hybrid of Array's intuitive inter-operation facilities and -# Hash's fast lookup. -# -# Set is easy to use with Enumerable objects (implementing +each+). -# Most of the initializer methods and binary operators accept generic -# Enumerable objects besides sets and arrays. An Enumerable object -# can be converted to Set using the +to_set+ method. -# -# Set uses Hash as storage, so you must note the following points: -# -# * Equality of elements is determined according to Object#eql? and -# Object#hash. Use Set#compare_by_identity to make a set compare -# its elements by their identity. -# * Set assumes that the identity of each element does not change -# while it is stored. Modifying an element of a set will render the -# set to an unreliable state. -# * When a string is to be stored, a frozen copy of the string is -# stored instead unless the original string is already frozen. -# -# == Comparison -# -# The comparison operators <, >, <=, and >= are implemented as -# shorthand for the {proper_,}{subset?,superset?} methods. However, -# the <=> operator is intentionally left out because not every pair of -# sets is comparable ({x, y} vs. {x, z} for example). -# -# == Example -# -# require 'set' -# s1 = Set[1, 2] #=> # -# s2 = [1, 2].to_set #=> # -# s1 == s2 #=> true -# s1.add("foo") #=> # -# s1.merge([2, 6]) #=> # -# s1.subset?(s2) #=> false -# s2.subset?(s1) #=> true -# -# == Contact -# -# - Akinori MUSHA (current maintainer) -# -class CompiledSet - include Enumerable - - # Creates a new set containing the given objects. - # - # Set[1, 2] # => # - # Set[1, 2, 1] # => # - # Set[1, 'c', :s] # => # - def self.[](*ary) - new(ary) - end - - # Creates a new set containing the elements of the given enumerable - # object. - # - # If a block is given, the elements of enum are preprocessed by the - # given block. - # - # Set.new([1, 2]) #=> # - # Set.new([1, 2, 1]) #=> # - # Set.new([1, 'c', :s]) #=> # - # Set.new(1..5) #=> # - # Set.new([1, 2, 3]) { |x| x * x } #=> # - def initialize(enum = nil, &block) # :yields: o - @hash ||= Hash.new(false) - - enum.nil? and return - - if block - do_with_enum(enum) { |o| add(block[o]) } - else - merge(enum) - end - end - - # Makes the set compare its elements by their identity and returns - # self. This method may not be supported by all subclasses of Set. - def compare_by_identity - if @hash.respond_to?(:compare_by_identity) - @hash.compare_by_identity - self - else - raise NotImplementedError, "#{self.class.name}\##{__method__} is not implemented" - end - end - - # Returns true if the set will compare its elements by their - # identity. Also see Set#compare_by_identity. - def compare_by_identity? - @hash.respond_to?(:compare_by_identity?) && @hash.compare_by_identity? - end - - def do_with_enum(enum, &block) # :nodoc: - if enum.respond_to?(:each_entry) - enum.each_entry(&block) if block - elsif enum.respond_to?(:each) - enum.each(&block) if block - else - raise ArgumentError, "value must be enumerable" - end - end - private :do_with_enum - - # Dup internal hash. - def initialize_dup(orig) - super - @hash = orig.instance_variable_get(:@hash).dup - end - - # Clone internal hash. - def initialize_clone(orig) - super - @hash = orig.instance_variable_get(:@hash).clone - end - - def freeze # :nodoc: - @hash.freeze - super - end - - # Returns the number of elements. - def size - @hash.size - end - alias length size - - # Returns true if the set contains no elements. - def empty? - @hash.empty? - end - - # Removes all elements and returns self. - # - # set = Set[1, 'c', :s] #=> # - # set.clear #=> # - # set #=> # - def clear - @hash.clear - self - end - - # Replaces the contents of the set with the contents of the given - # enumerable object and returns self. - # - # set = Set[1, 'c', :s] #=> # - # set.replace([1, 2]) #=> # - # set #=> # - def replace(enum) - if enum.instance_of?(self.class) - @hash.replace(enum.instance_variable_get(:@hash)) - self - else - do_with_enum(enum) # make sure enum is enumerable before calling clear - clear - merge(enum) - end - end - - # Converts the set to an array. The order of elements is uncertain. - # - # Set[1, 2].to_a #=> [1, 2] - # Set[1, 'c', :s].to_a #=> [1, "c", :s] - def to_a - @hash.keys - end - - # Returns self if no arguments are given. Otherwise, converts the - # set to another with klass.new(self, *args, &block). - # - # In subclasses, returns klass.new(self, *args, &block) unless - # overridden. - def to_compiled_set(klass = CompiledSet, *args, &block) - return self if instance_of?(CompiledSet) && klass == CompiledSet && block.nil? && args.empty? - klass.new(self, *args, &block) - end - - def flatten_merge(set, seen = CompiledSet.new) # :nodoc: - set.each { |e| - if e.is_a?(CompiledSet) - if seen.include?(e_id = e.object_id) - raise ArgumentError, "tried to flatten recursive CompiledSet" - end - - seen.add(e_id) - flatten_merge(e, seen) - seen.delete(e_id) - else - add(e) - end - } - - self - end - protected :flatten_merge - - # Returns a new set that is a copy of the set, flattening each - # containing set recursively. - def flatten - self.class.new.flatten_merge(self) - end - - # Equivalent to Set#flatten, but replaces the receiver with the - # result in place. Returns nil if no modifications were made. - def flatten! - replace(flatten()) if any? { |e| e.is_a?(CompiledSet) } - end - - # Returns true if the set contains the given object. - # - # Note that include? and member? do not test member - # equality using == as do other Enumerables. - # - # See also Enumerable#include? - def include?(o) - @hash[o] - end - alias member? include? - - # Returns true if the set is a superset of the given set. - def superset?(set) - case - when set.instance_of?(self.class) && @hash.respond_to?(:>=) - @hash >= set.instance_variable_get(:@hash) - when set.is_a?(CompiledSet) - size >= set.size && set.all? { |o| include?(o) } - else - raise ArgumentError, "value must be a set" - end - end - alias >= superset? - - # Returns true if the set is a proper superset of the given set. - def proper_superset?(set) - case - when set.instance_of?(self.class) && @hash.respond_to?(:>) - @hash > set.instance_variable_get(:@hash) - when set.is_a?(CompiledSet) - size > set.size && set.all? { |o| include?(o) } - else - raise ArgumentError, "value must be a set" - end - end - alias > proper_superset? - - # Returns true if the set is a subset of the given set. - def subset?(set) - case - when set.instance_of?(self.class) && @hash.respond_to?(:<=) - @hash <= set.instance_variable_get(:@hash) - when set.is_a?(CompiledSet) - size <= set.size && all? { |o| set.include?(o) } - else - raise ArgumentError, "value must be a set" - end - end - alias <= subset? - - # Returns true if the set is a proper subset of the given set. - def proper_subset?(set) - case - when set.instance_of?(self.class) && @hash.respond_to?(:<) - @hash < set.instance_variable_get(:@hash) - when set.is_a?(CompiledSet) - size < set.size && all? { |o| set.include?(o) } - else - raise ArgumentError, "value must be a set" - end - end - alias < proper_subset? - - # Returns true if the set and the given set have at least one - # element in common. - # - # Set[1, 2, 3].intersect? Set[4, 5] #=> false - # Set[1, 2, 3].intersect? Set[3, 4] #=> true - def intersect?(set) - set.is_a?(CompiledSet) or raise ArgumentError, "value must be a set" - if size < set.size - any? { |o| set.include?(o) } - else - set.any? { |o| include?(o) } - end - end - - # Returns true if the set and the given set have no element in - # common. This method is the opposite of +intersect?+. - # - # Set[1, 2, 3].disjoint? Set[3, 4] #=> false - # Set[1, 2, 3].disjoint? Set[4, 5] #=> true - def disjoint?(set) - !intersect?(set) - end - - # Calls the given block once for each element in the set, passing - # the element as parameter. Returns an enumerator if no block is - # given. - def each(&block) - block or return enum_for(T.must(__method__)) { size } - @hash.each_key(&block) - self - end - - # Adds the given object to the set and returns self. Use +merge+ to - # add many elements at once. - # - # Set[1, 2].add(3) #=> # - # Set[1, 2].add([3, 4]) #=> # - # Set[1, 2].add(2) #=> # - def add(o) - @hash[o] = true - self - end - alias << add - - # Adds the given object to the set and returns self. If the - # object is already in the set, returns nil. - # - # Set[1, 2].add?(3) #=> # - # Set[1, 2].add?([3, 4]) #=> # - # Set[1, 2].add?(2) #=> nil - def add?(o) - add(o) unless include?(o) - end - - # Deletes the given object from the set and returns self. Use +subtract+ to - # delete many items at once. - def delete(o) - @hash.delete(o) - self - end - - # Deletes the given object from the set and returns self. If the - # object is not in the set, returns nil. - def delete?(o) - delete(o) if include?(o) - end - - # Deletes every element of the set for which block evaluates to - # true, and returns self. Returns an enumerator if no block is - # given. - def delete_if - block_given? or return enum_for(T.must(__method__)) { size } - # @hash.delete_if should be faster, but using it breaks the order - # of enumeration in subclasses. - select { |o| yield o }.each { |o| @hash.delete(o) } - self - end - - # Deletes every element of the set for which block evaluates to - # false, and returns self. Returns an enumerator if no block is - # given. - def keep_if - block_given? or return enum_for(T.must(__method__)) { size } - # @hash.keep_if should be faster, but using it breaks the order of - # enumeration in subclasses. - reject { |o| yield o }.each { |o| @hash.delete(o) } - self - end - - # Replaces the elements with ones returned by collect(). - # Returns an enumerator if no block is given. - def collect! - block_given? or return enum_for(T.must(__method__)) { size } - set = self.class.new - each { |o| set << yield(o) } - replace(set) - end - alias map! collect! - - # Equivalent to Set#delete_if, but returns nil if no changes were - # made. Returns an enumerator if no block is given. - def reject!(&block) - block or return enum_for(T.must(__method__)) { size } - n = size - delete_if(&block) - self if size != n - end - - # Equivalent to Set#keep_if, but returns nil if no changes were - # made. Returns an enumerator if no block is given. - def select!(&block) - block or return enum_for(T.must(__method__)) { size } - n = size - keep_if(&block) - self if size != n - end - - # Equivalent to Set#select! - alias filter! select! - - # Merges the elements of the given enumerable object to the set and - # returns self. - def merge(enum) - if enum.instance_of?(self.class) - @hash.update(enum.instance_variable_get(:@hash)) - else - do_with_enum(enum) { |o| add(o) } - end - - self - end - - # Deletes every element that appears in the given enumerable object - # and returns self. - def subtract(enum) - do_with_enum(enum) { |o| delete(o) } - self - end - - # Returns a new set built by merging the set and the elements of the - # given enumerable object. - # - # Set[1, 2, 3] | Set[2, 4, 5] #=> # - # Set[1, 5, 'z'] | (1..6) #=> # - def |(enum) - dup.merge(enum) - end - alias + | - alias union | - - # Returns a new set built by duplicating the set, removing every - # element that appears in the given enumerable object. - # - # Set[1, 3, 5] - Set[1, 5] #=> # - # Set['a', 'b', 'z'] - ['a', 'c'] #=> # - def -(enum) - dup.subtract(enum) - end - alias difference - - - # Returns a new set containing elements common to the set and the - # given enumerable object. - # - # Set[1, 3, 5] & Set[3, 2, 1] #=> # - # Set['a', 'b', 'z'] & ['a', 'b', 'c'] #=> # - def &(enum) - n = self.class.new - do_with_enum(enum) { |o| n.add(o) if include?(o) } - n - end - alias intersection & - - # Returns a new set containing elements exclusive between the set - # and the given enumerable object. (set ^ enum) is equivalent to - # ((set | enum) - (set & enum)). - # - # Set[1, 2] ^ Set[2, 3] #=> # - # Set[1, 'b', 'c'] ^ ['b', 'd'] #=> # - def ^(enum) - n = CompiledSet.new(enum) - each { |o| n.add(o) unless n.delete?(o) } - n - end - - # Returns true if two sets are equal. The equality of each couple - # of elements is defined according to Object#eql?. - # - # Set[1, 2] == Set[2, 1] #=> true - # Set[1, 3, 5] == Set[1, 5] #=> false - # Set['a', 'b', 'c'] == Set['a', 'c', 'b'] #=> true - # Set['a', 'b', 'c'] == ['a', 'c', 'b'] #=> false - def ==(other) - if self.equal?(other) - true - elsif other.instance_of?(self.class) - @hash == other.instance_variable_get(:@hash) - elsif other.is_a?(CompiledSet) && self.size == other.size - other.all? { |o| @hash.include?(o) } - else - false - end - end - - def hash # :nodoc: - @hash.hash - end - - def eql?(o) # :nodoc: - return false unless o.is_a?(CompiledSet) - @hash.eql?(o.instance_variable_get(:@hash)) - end - - # Resets the internal state after modification to existing elements - # and returns self. - # - # Elements will be reindexed and deduplicated. - def reset - if @hash.respond_to?(:rehash) - @hash.rehash # This should perform frozenness check. - else - raise FrozenError, "can't modify frozen #{self.class.name}" if frozen? - end - self - end - - # Returns true if the given object is a member of the set, - # and false otherwise. - # - # Used in case statements: - # - # require 'set' - # - # case :apple - # when Set[:potato, :carrot] - # "vegetable" - # when Set[:apple, :banana] - # "fruit" - # end - # # => "fruit" - # - # Or by itself: - # - # Set[1, 2, 3] === 2 #=> true - # Set[1, 2, 3] === 4 #=> false - # - alias === include? - - # Classifies the set by the return value of the given block and - # returns a hash of {value => set of elements} pairs. The block is - # called once for each element of the set, passing the element as - # parameter. - # - # require 'set' - # files = Set.new(Dir.glob("*.rb")) - # hash = files.classify { |f| File.mtime(f).year } - # hash #=> {2000=>#, - # # 2001=>#, - # # 2002=>#} - # - # Returns an enumerator if no block is given. - def classify # :yields: o - block_given? or return enum_for(T.must(__method__)) { size } - - h = {} - - each { |i| - (h[yield(i)] ||= self.class.new).add(i) - } - - h - end - - # Divides the set into a set of subsets according to the commonality - # defined by the given block. - # - # If the arity of the block is 2, elements o1 and o2 are in common - # if block.call(o1, o2) is true. Otherwise, elements o1 and o2 are - # in common if block.call(o1) == block.call(o2). - # - # require 'set' - # numbers = Set[1, 3, 4, 6, 9, 10, 11] - # set = numbers.divide { |i,j| (i - j).abs == 1 } - # set #=> #, - # # #, - # # #, - # # #}> - # - # Returns an enumerator if no block is given. - # def divide(&func) - # func or return enum_for(__method__) { size } - - # if func.arity == 2 - # require 'tsort' - - # class << dig = {} # :nodoc: - # include TSort - - # alias tsort_each_node each_key - # def tsort_each_child(node, &block) - # fetch(node).each(&block) - # end - # end - - # each { |u| - # dig[u] = a = [] - # each{ |v| func.call(u, v) and a << v } - # } - - # set = Set.new() - # dig.each_strongly_connected_component { |css| - # set.add(self.class.new(css)) - # } - # set - # else - # Set.new(classify(&func).values) - # end - # end - - InspectKey = :__inspect_key__ # :nodoc: - - # Returns a string containing a human-readable representation of the - # set ("#"). - def inspect - ids = (Thread.current[InspectKey] ||= []) - - if ids.include?(object_id) - return sprintf('#<%s: {...}>', self.class.name) - end - - ids << object_id - begin - return sprintf('#<%s: {%s}>', self.class, to_a.inspect[1..-2]) - ensure - ids.pop - end - end - - alias to_s inspect - - def pretty_print(pp) # :nodoc: - pp.text sprintf('#<%s: {', self.class.name) - pp.nest(1) { - pp.seplist(self) { |o| - pp.pp o - } - } - pp.text "}>" - end - - def pretty_print_cycle(pp) # :nodoc: - pp.text sprintf('#<%s: {%s}>', self.class.name, empty? ? '' : '...') - end -end - -# -# SortedSet implements a Set that guarantees that its elements are -# yielded in sorted order (according to the return values of their -# #<=> methods) when iterating over them. -# -# All elements that are added to a SortedSet must respond to the <=> -# method for comparison. -# -# Also, all elements must be mutually comparable: el1 <=> -# el2 must not return nil for any elements el1 -# and el2, else an ArgumentError will be raised when -# iterating over the SortedSet. -# -# == Example -# -# require "set" -# -# set = SortedSet.new([2, 1, 5, 6, 4, 5, 3, 3, 3]) -# ary = [] -# -# set.each do |obj| -# ary << obj -# end -# -# p ary # => [1, 2, 3, 4, 5, 6] -# -# set2 = SortedSet.new([1, 2, "3"]) -# set2.each { |obj| } # => raises ArgumentError: comparison of Fixnum with String failed -# -class CompiledSortedSet < CompiledSet - @@setup = false - @@mutex = Mutex.new - - class << self - def [](*ary) # :nodoc: - new(ary) - end - - def setup # :nodoc: - @@setup and return - - @@mutex.synchronize do - # a hack to shut up warning - #alias_method :old_init, :initialize - - begin - require 'rbtree' - - module_eval <<-END, __FILE__, __LINE__+1 - def initialize(*args) - @hash = RBTree.new - super - end - - def add(o) - o.respond_to?(:<=>) or raise ArgumentError, "value must respond to <=>" - super - end - alias << add - END - rescue LoadError - module_eval <<-END, __FILE__, __LINE__+1 - def initialize(*args) - @keys = nil - super - end - - def clear - @keys = nil - super - end - - def replace(enum) - @keys = nil - super - end - - def add(o) - o.respond_to?(:<=>) or raise ArgumentError, "value must respond to <=>" - @keys = nil - super - end - alias << add - - def delete(o) - @keys = nil - @hash.delete(o) - self - end - - def delete_if - block_given? or return enum_for(__method__) { size } - n = @hash.size - super - @keys = nil if @hash.size != n - self - end - - def keep_if - block_given? or return enum_for(__method__) { size } - n = @hash.size - super - @keys = nil if @hash.size != n - self - end - - def merge(enum) - @keys = nil - super - end - - def each(&block) - block or return enum_for(__method__) { size } - to_a.each(&block) - self - end - - def to_a - (@keys = @hash.keys).sort! unless @keys - @keys - end - - def freeze - to_a - super - end - - def rehash - @keys = nil - super - end - END - end - # a hack to shut up warning - #remove_method :old_init - - @@setup = true - end - end - end - - def initialize(*args, &block) # :nodoc: - CompiledSortedSet.setup - @keys = nil - super - end -end - -module Enumerable - # Makes a set from the enumerable object with given arguments. - # Needs to +require "set"+ to use this method. - def to_compiled_set(klass = CompiledSet, *args, &block) - klass.new(self, *args, &block) - end -end - -# =begin -# == RestricedSet class -# RestricedSet implements a set with restrictions defined by a given -# block. -# -# === Super class -# Set -# -# === Class Methods -# --- RestricedSet::new(enum = nil) { |o| ... } -# --- RestricedSet::new(enum = nil) { |rset, o| ... } -# Creates a new restricted set containing the elements of the given -# enumerable object. Restrictions are defined by the given block. -# -# If the block's arity is 2, it is called with the RestrictedSet -# itself and an object to see if the object is allowed to be put in -# the set. -# -# Otherwise, the block is called with an object to see if the object -# is allowed to be put in the set. -# -# === Instance Methods -# --- restriction_proc -# Returns the restriction procedure of the set. -# -# =end -# -# class RestricedSet < Set -# def initialize(*args, &block) -# @proc = block or raise ArgumentError, "missing a block" -# -# if @proc.arity == 2 -# instance_eval %{ -# def add(o) -# @hash[o] = true if @proc.call(self, o) -# self -# end -# alias << add -# -# def add?(o) -# if include?(o) || !@proc.call(self, o) -# nil -# else -# @hash[o] = true -# self -# end -# end -# -# def replace(enum) -# enum.respond_to?(:each) or raise ArgumentError, "value must be enumerable" -# clear -# enum.each_entry { |o| add(o) } -# -# self -# end -# -# def merge(enum) -# enum.respond_to?(:each) or raise ArgumentError, "value must be enumerable" -# enum.each_entry { |o| add(o) } -# -# self -# end -# } -# else -# instance_eval %{ -# def add(o) -# if @proc.call(o) -# @hash[o] = true -# end -# self -# end -# alias << add -# -# def add?(o) -# if include?(o) || !@proc.call(o) -# nil -# else -# @hash[o] = true -# self -# end -# end -# } -# end -# -# super(*args) -# end -# -# def restriction_proc -# @proc -# end -# end - -# Tests have been moved to test/test_set.rb. - -### -### CompiledSet benchmark code for 'initialize' (empty set). -### - -sets = [] -i = 0 - -while i < 10_000_000 do - sets << CompiledSet.new - - i += 1 -end - -puts i -puts sets.size diff --git a/test/testdata/ruby_benchmark/stripe/compiled_set_initialize_nonempty.rb b/test/testdata/ruby_benchmark/stripe/compiled_set_initialize_nonempty.rb deleted file mode 100644 index 6a7209f279..0000000000 --- a/test/testdata/ruby_benchmark/stripe/compiled_set_initialize_nonempty.rb +++ /dev/null @@ -1,938 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -# This file contains a copy of the set.rb class from Ruby 2.7.2, and some -# benchmark code at the very bottom. -# -# Unfortunately it is duplicated in a number of other benchmarks, because for -# the moment I don't see an easy way for multiple benchmarks to share a -# compiled file. -# -# Modifications made from original set.rb: -# -# 1. Classes renamed to "CompiledFoo" (e.g., Set->CompiledSet) -# 2. Miscellaneous 'T.must's to make Sorbet TC happy -# 3. Removed the Set#divide function (this uses 'class <<' in unsupported -# ways). -# -# TODO(aprocter): Deduplicate the set.rb code - -# See bottom of the file for the actual benchmark code. - -# -# set.rb - defines the Set class -#++ -# Copyright (c) 2002-2016 Akinori MUSHA -# -# Documentation by Akinori MUSHA and Gavin Sinclair. -# -# All rights reserved. You can redistribute and/or modify it under the same -# terms as Ruby. -# -# $Id$ -# -# == Overview -# -# This library provides the Set class, which deals with a collection -# of unordered values with no duplicates. It is a hybrid of Array's -# intuitive inter-operation facilities and Hash's fast lookup. If you -# need to keep values sorted in some order, use the SortedSet class. -# -# The method +to_set+ is added to Enumerable for convenience. -# -# See the Set and SortedSet documentation for examples of usage. - - -# -# Set implements a collection of unordered values with no duplicates. -# This is a hybrid of Array's intuitive inter-operation facilities and -# Hash's fast lookup. -# -# Set is easy to use with Enumerable objects (implementing +each+). -# Most of the initializer methods and binary operators accept generic -# Enumerable objects besides sets and arrays. An Enumerable object -# can be converted to Set using the +to_set+ method. -# -# Set uses Hash as storage, so you must note the following points: -# -# * Equality of elements is determined according to Object#eql? and -# Object#hash. Use Set#compare_by_identity to make a set compare -# its elements by their identity. -# * Set assumes that the identity of each element does not change -# while it is stored. Modifying an element of a set will render the -# set to an unreliable state. -# * When a string is to be stored, a frozen copy of the string is -# stored instead unless the original string is already frozen. -# -# == Comparison -# -# The comparison operators <, >, <=, and >= are implemented as -# shorthand for the {proper_,}{subset?,superset?} methods. However, -# the <=> operator is intentionally left out because not every pair of -# sets is comparable ({x, y} vs. {x, z} for example). -# -# == Example -# -# require 'set' -# s1 = Set[1, 2] #=> # -# s2 = [1, 2].to_set #=> # -# s1 == s2 #=> true -# s1.add("foo") #=> # -# s1.merge([2, 6]) #=> # -# s1.subset?(s2) #=> false -# s2.subset?(s1) #=> true -# -# == Contact -# -# - Akinori MUSHA (current maintainer) -# -class CompiledSet - include Enumerable - - # Creates a new set containing the given objects. - # - # Set[1, 2] # => # - # Set[1, 2, 1] # => # - # Set[1, 'c', :s] # => # - def self.[](*ary) - new(ary) - end - - # Creates a new set containing the elements of the given enumerable - # object. - # - # If a block is given, the elements of enum are preprocessed by the - # given block. - # - # Set.new([1, 2]) #=> # - # Set.new([1, 2, 1]) #=> # - # Set.new([1, 'c', :s]) #=> # - # Set.new(1..5) #=> # - # Set.new([1, 2, 3]) { |x| x * x } #=> # - def initialize(enum = nil, &block) # :yields: o - @hash ||= Hash.new(false) - - enum.nil? and return - - if block - do_with_enum(enum) { |o| add(block[o]) } - else - merge(enum) - end - end - - # Makes the set compare its elements by their identity and returns - # self. This method may not be supported by all subclasses of Set. - def compare_by_identity - if @hash.respond_to?(:compare_by_identity) - @hash.compare_by_identity - self - else - raise NotImplementedError, "#{self.class.name}\##{__method__} is not implemented" - end - end - - # Returns true if the set will compare its elements by their - # identity. Also see Set#compare_by_identity. - def compare_by_identity? - @hash.respond_to?(:compare_by_identity?) && @hash.compare_by_identity? - end - - def do_with_enum(enum, &block) # :nodoc: - if enum.respond_to?(:each_entry) - enum.each_entry(&block) if block - elsif enum.respond_to?(:each) - enum.each(&block) if block - else - raise ArgumentError, "value must be enumerable" - end - end - private :do_with_enum - - # Dup internal hash. - def initialize_dup(orig) - super - @hash = orig.instance_variable_get(:@hash).dup - end - - # Clone internal hash. - def initialize_clone(orig) - super - @hash = orig.instance_variable_get(:@hash).clone - end - - def freeze # :nodoc: - @hash.freeze - super - end - - # Returns the number of elements. - def size - @hash.size - end - alias length size - - # Returns true if the set contains no elements. - def empty? - @hash.empty? - end - - # Removes all elements and returns self. - # - # set = Set[1, 'c', :s] #=> # - # set.clear #=> # - # set #=> # - def clear - @hash.clear - self - end - - # Replaces the contents of the set with the contents of the given - # enumerable object and returns self. - # - # set = Set[1, 'c', :s] #=> # - # set.replace([1, 2]) #=> # - # set #=> # - def replace(enum) - if enum.instance_of?(self.class) - @hash.replace(enum.instance_variable_get(:@hash)) - self - else - do_with_enum(enum) # make sure enum is enumerable before calling clear - clear - merge(enum) - end - end - - # Converts the set to an array. The order of elements is uncertain. - # - # Set[1, 2].to_a #=> [1, 2] - # Set[1, 'c', :s].to_a #=> [1, "c", :s] - def to_a - @hash.keys - end - - # Returns self if no arguments are given. Otherwise, converts the - # set to another with klass.new(self, *args, &block). - # - # In subclasses, returns klass.new(self, *args, &block) unless - # overridden. - def to_compiled_set(klass = CompiledSet, *args, &block) - return self if instance_of?(CompiledSet) && klass == CompiledSet && block.nil? && args.empty? - klass.new(self, *args, &block) - end - - def flatten_merge(set, seen = CompiledSet.new) # :nodoc: - set.each { |e| - if e.is_a?(CompiledSet) - if seen.include?(e_id = e.object_id) - raise ArgumentError, "tried to flatten recursive CompiledSet" - end - - seen.add(e_id) - flatten_merge(e, seen) - seen.delete(e_id) - else - add(e) - end - } - - self - end - protected :flatten_merge - - # Returns a new set that is a copy of the set, flattening each - # containing set recursively. - def flatten - self.class.new.flatten_merge(self) - end - - # Equivalent to Set#flatten, but replaces the receiver with the - # result in place. Returns nil if no modifications were made. - def flatten! - replace(flatten()) if any? { |e| e.is_a?(CompiledSet) } - end - - # Returns true if the set contains the given object. - # - # Note that include? and member? do not test member - # equality using == as do other Enumerables. - # - # See also Enumerable#include? - def include?(o) - @hash[o] - end - alias member? include? - - # Returns true if the set is a superset of the given set. - def superset?(set) - case - when set.instance_of?(self.class) && @hash.respond_to?(:>=) - @hash >= set.instance_variable_get(:@hash) - when set.is_a?(CompiledSet) - size >= set.size && set.all? { |o| include?(o) } - else - raise ArgumentError, "value must be a set" - end - end - alias >= superset? - - # Returns true if the set is a proper superset of the given set. - def proper_superset?(set) - case - when set.instance_of?(self.class) && @hash.respond_to?(:>) - @hash > set.instance_variable_get(:@hash) - when set.is_a?(CompiledSet) - size > set.size && set.all? { |o| include?(o) } - else - raise ArgumentError, "value must be a set" - end - end - alias > proper_superset? - - # Returns true if the set is a subset of the given set. - def subset?(set) - case - when set.instance_of?(self.class) && @hash.respond_to?(:<=) - @hash <= set.instance_variable_get(:@hash) - when set.is_a?(CompiledSet) - size <= set.size && all? { |o| set.include?(o) } - else - raise ArgumentError, "value must be a set" - end - end - alias <= subset? - - # Returns true if the set is a proper subset of the given set. - def proper_subset?(set) - case - when set.instance_of?(self.class) && @hash.respond_to?(:<) - @hash < set.instance_variable_get(:@hash) - when set.is_a?(CompiledSet) - size < set.size && all? { |o| set.include?(o) } - else - raise ArgumentError, "value must be a set" - end - end - alias < proper_subset? - - # Returns true if the set and the given set have at least one - # element in common. - # - # Set[1, 2, 3].intersect? Set[4, 5] #=> false - # Set[1, 2, 3].intersect? Set[3, 4] #=> true - def intersect?(set) - set.is_a?(CompiledSet) or raise ArgumentError, "value must be a set" - if size < set.size - any? { |o| set.include?(o) } - else - set.any? { |o| include?(o) } - end - end - - # Returns true if the set and the given set have no element in - # common. This method is the opposite of +intersect?+. - # - # Set[1, 2, 3].disjoint? Set[3, 4] #=> false - # Set[1, 2, 3].disjoint? Set[4, 5] #=> true - def disjoint?(set) - !intersect?(set) - end - - # Calls the given block once for each element in the set, passing - # the element as parameter. Returns an enumerator if no block is - # given. - def each(&block) - block or return enum_for(T.must(__method__)) { size } - @hash.each_key(&block) - self - end - - # Adds the given object to the set and returns self. Use +merge+ to - # add many elements at once. - # - # Set[1, 2].add(3) #=> # - # Set[1, 2].add([3, 4]) #=> # - # Set[1, 2].add(2) #=> # - def add(o) - @hash[o] = true - self - end - alias << add - - # Adds the given object to the set and returns self. If the - # object is already in the set, returns nil. - # - # Set[1, 2].add?(3) #=> # - # Set[1, 2].add?([3, 4]) #=> # - # Set[1, 2].add?(2) #=> nil - def add?(o) - add(o) unless include?(o) - end - - # Deletes the given object from the set and returns self. Use +subtract+ to - # delete many items at once. - def delete(o) - @hash.delete(o) - self - end - - # Deletes the given object from the set and returns self. If the - # object is not in the set, returns nil. - def delete?(o) - delete(o) if include?(o) - end - - # Deletes every element of the set for which block evaluates to - # true, and returns self. Returns an enumerator if no block is - # given. - def delete_if - block_given? or return enum_for(T.must(__method__)) { size } - # @hash.delete_if should be faster, but using it breaks the order - # of enumeration in subclasses. - select { |o| yield o }.each { |o| @hash.delete(o) } - self - end - - # Deletes every element of the set for which block evaluates to - # false, and returns self. Returns an enumerator if no block is - # given. - def keep_if - block_given? or return enum_for(T.must(__method__)) { size } - # @hash.keep_if should be faster, but using it breaks the order of - # enumeration in subclasses. - reject { |o| yield o }.each { |o| @hash.delete(o) } - self - end - - # Replaces the elements with ones returned by collect(). - # Returns an enumerator if no block is given. - def collect! - block_given? or return enum_for(T.must(__method__)) { size } - set = self.class.new - each { |o| set << yield(o) } - replace(set) - end - alias map! collect! - - # Equivalent to Set#delete_if, but returns nil if no changes were - # made. Returns an enumerator if no block is given. - def reject!(&block) - block or return enum_for(T.must(__method__)) { size } - n = size - delete_if(&block) - self if size != n - end - - # Equivalent to Set#keep_if, but returns nil if no changes were - # made. Returns an enumerator if no block is given. - def select!(&block) - block or return enum_for(T.must(__method__)) { size } - n = size - keep_if(&block) - self if size != n - end - - # Equivalent to Set#select! - alias filter! select! - - # Merges the elements of the given enumerable object to the set and - # returns self. - def merge(enum) - if enum.instance_of?(self.class) - @hash.update(enum.instance_variable_get(:@hash)) - else - do_with_enum(enum) { |o| add(o) } - end - - self - end - - # Deletes every element that appears in the given enumerable object - # and returns self. - def subtract(enum) - do_with_enum(enum) { |o| delete(o) } - self - end - - # Returns a new set built by merging the set and the elements of the - # given enumerable object. - # - # Set[1, 2, 3] | Set[2, 4, 5] #=> # - # Set[1, 5, 'z'] | (1..6) #=> # - def |(enum) - dup.merge(enum) - end - alias + | - alias union | - - # Returns a new set built by duplicating the set, removing every - # element that appears in the given enumerable object. - # - # Set[1, 3, 5] - Set[1, 5] #=> # - # Set['a', 'b', 'z'] - ['a', 'c'] #=> # - def -(enum) - dup.subtract(enum) - end - alias difference - - - # Returns a new set containing elements common to the set and the - # given enumerable object. - # - # Set[1, 3, 5] & Set[3, 2, 1] #=> # - # Set['a', 'b', 'z'] & ['a', 'b', 'c'] #=> # - def &(enum) - n = self.class.new - do_with_enum(enum) { |o| n.add(o) if include?(o) } - n - end - alias intersection & - - # Returns a new set containing elements exclusive between the set - # and the given enumerable object. (set ^ enum) is equivalent to - # ((set | enum) - (set & enum)). - # - # Set[1, 2] ^ Set[2, 3] #=> # - # Set[1, 'b', 'c'] ^ ['b', 'd'] #=> # - def ^(enum) - n = CompiledSet.new(enum) - each { |o| n.add(o) unless n.delete?(o) } - n - end - - # Returns true if two sets are equal. The equality of each couple - # of elements is defined according to Object#eql?. - # - # Set[1, 2] == Set[2, 1] #=> true - # Set[1, 3, 5] == Set[1, 5] #=> false - # Set['a', 'b', 'c'] == Set['a', 'c', 'b'] #=> true - # Set['a', 'b', 'c'] == ['a', 'c', 'b'] #=> false - def ==(other) - if self.equal?(other) - true - elsif other.instance_of?(self.class) - @hash == other.instance_variable_get(:@hash) - elsif other.is_a?(CompiledSet) && self.size == other.size - other.all? { |o| @hash.include?(o) } - else - false - end - end - - def hash # :nodoc: - @hash.hash - end - - def eql?(o) # :nodoc: - return false unless o.is_a?(CompiledSet) - @hash.eql?(o.instance_variable_get(:@hash)) - end - - # Resets the internal state after modification to existing elements - # and returns self. - # - # Elements will be reindexed and deduplicated. - def reset - if @hash.respond_to?(:rehash) - @hash.rehash # This should perform frozenness check. - else - raise FrozenError, "can't modify frozen #{self.class.name}" if frozen? - end - self - end - - # Returns true if the given object is a member of the set, - # and false otherwise. - # - # Used in case statements: - # - # require 'set' - # - # case :apple - # when Set[:potato, :carrot] - # "vegetable" - # when Set[:apple, :banana] - # "fruit" - # end - # # => "fruit" - # - # Or by itself: - # - # Set[1, 2, 3] === 2 #=> true - # Set[1, 2, 3] === 4 #=> false - # - alias === include? - - # Classifies the set by the return value of the given block and - # returns a hash of {value => set of elements} pairs. The block is - # called once for each element of the set, passing the element as - # parameter. - # - # require 'set' - # files = Set.new(Dir.glob("*.rb")) - # hash = files.classify { |f| File.mtime(f).year } - # hash #=> {2000=>#, - # # 2001=>#, - # # 2002=>#} - # - # Returns an enumerator if no block is given. - def classify # :yields: o - block_given? or return enum_for(T.must(__method__)) { size } - - h = {} - - each { |i| - (h[yield(i)] ||= self.class.new).add(i) - } - - h - end - - # Divides the set into a set of subsets according to the commonality - # defined by the given block. - # - # If the arity of the block is 2, elements o1 and o2 are in common - # if block.call(o1, o2) is true. Otherwise, elements o1 and o2 are - # in common if block.call(o1) == block.call(o2). - # - # require 'set' - # numbers = Set[1, 3, 4, 6, 9, 10, 11] - # set = numbers.divide { |i,j| (i - j).abs == 1 } - # set #=> #, - # # #, - # # #, - # # #}> - # - # Returns an enumerator if no block is given. - # def divide(&func) - # func or return enum_for(__method__) { size } - - # if func.arity == 2 - # require 'tsort' - - # class << dig = {} # :nodoc: - # include TSort - - # alias tsort_each_node each_key - # def tsort_each_child(node, &block) - # fetch(node).each(&block) - # end - # end - - # each { |u| - # dig[u] = a = [] - # each{ |v| func.call(u, v) and a << v } - # } - - # set = Set.new() - # dig.each_strongly_connected_component { |css| - # set.add(self.class.new(css)) - # } - # set - # else - # Set.new(classify(&func).values) - # end - # end - - InspectKey = :__inspect_key__ # :nodoc: - - # Returns a string containing a human-readable representation of the - # set ("#"). - def inspect - ids = (Thread.current[InspectKey] ||= []) - - if ids.include?(object_id) - return sprintf('#<%s: {...}>', self.class.name) - end - - ids << object_id - begin - return sprintf('#<%s: {%s}>', self.class, to_a.inspect[1..-2]) - ensure - ids.pop - end - end - - alias to_s inspect - - def pretty_print(pp) # :nodoc: - pp.text sprintf('#<%s: {', self.class.name) - pp.nest(1) { - pp.seplist(self) { |o| - pp.pp o - } - } - pp.text "}>" - end - - def pretty_print_cycle(pp) # :nodoc: - pp.text sprintf('#<%s: {%s}>', self.class.name, empty? ? '' : '...') - end -end - -# -# SortedSet implements a Set that guarantees that its elements are -# yielded in sorted order (according to the return values of their -# #<=> methods) when iterating over them. -# -# All elements that are added to a SortedSet must respond to the <=> -# method for comparison. -# -# Also, all elements must be mutually comparable: el1 <=> -# el2 must not return nil for any elements el1 -# and el2, else an ArgumentError will be raised when -# iterating over the SortedSet. -# -# == Example -# -# require "set" -# -# set = SortedSet.new([2, 1, 5, 6, 4, 5, 3, 3, 3]) -# ary = [] -# -# set.each do |obj| -# ary << obj -# end -# -# p ary # => [1, 2, 3, 4, 5, 6] -# -# set2 = SortedSet.new([1, 2, "3"]) -# set2.each { |obj| } # => raises ArgumentError: comparison of Fixnum with String failed -# -class CompiledSortedSet < CompiledSet - @@setup = false - @@mutex = Mutex.new - - class << self - def [](*ary) # :nodoc: - new(ary) - end - - def setup # :nodoc: - @@setup and return - - @@mutex.synchronize do - # a hack to shut up warning - #alias_method :old_init, :initialize - - begin - require 'rbtree' - - module_eval <<-END, __FILE__, __LINE__+1 - def initialize(*args) - @hash = RBTree.new - super - end - - def add(o) - o.respond_to?(:<=>) or raise ArgumentError, "value must respond to <=>" - super - end - alias << add - END - rescue LoadError - module_eval <<-END, __FILE__, __LINE__+1 - def initialize(*args) - @keys = nil - super - end - - def clear - @keys = nil - super - end - - def replace(enum) - @keys = nil - super - end - - def add(o) - o.respond_to?(:<=>) or raise ArgumentError, "value must respond to <=>" - @keys = nil - super - end - alias << add - - def delete(o) - @keys = nil - @hash.delete(o) - self - end - - def delete_if - block_given? or return enum_for(__method__) { size } - n = @hash.size - super - @keys = nil if @hash.size != n - self - end - - def keep_if - block_given? or return enum_for(__method__) { size } - n = @hash.size - super - @keys = nil if @hash.size != n - self - end - - def merge(enum) - @keys = nil - super - end - - def each(&block) - block or return enum_for(__method__) { size } - to_a.each(&block) - self - end - - def to_a - (@keys = @hash.keys).sort! unless @keys - @keys - end - - def freeze - to_a - super - end - - def rehash - @keys = nil - super - end - END - end - # a hack to shut up warning - #remove_method :old_init - - @@setup = true - end - end - end - - def initialize(*args, &block) # :nodoc: - CompiledSortedSet.setup - @keys = nil - super - end -end - -module Enumerable - # Makes a set from the enumerable object with given arguments. - # Needs to +require "set"+ to use this method. - def to_compiled_set(klass = CompiledSet, *args, &block) - klass.new(self, *args, &block) - end -end - -# =begin -# == RestricedSet class -# RestricedSet implements a set with restrictions defined by a given -# block. -# -# === Super class -# Set -# -# === Class Methods -# --- RestricedSet::new(enum = nil) { |o| ... } -# --- RestricedSet::new(enum = nil) { |rset, o| ... } -# Creates a new restricted set containing the elements of the given -# enumerable object. Restrictions are defined by the given block. -# -# If the block's arity is 2, it is called with the RestrictedSet -# itself and an object to see if the object is allowed to be put in -# the set. -# -# Otherwise, the block is called with an object to see if the object -# is allowed to be put in the set. -# -# === Instance Methods -# --- restriction_proc -# Returns the restriction procedure of the set. -# -# =end -# -# class RestricedSet < Set -# def initialize(*args, &block) -# @proc = block or raise ArgumentError, "missing a block" -# -# if @proc.arity == 2 -# instance_eval %{ -# def add(o) -# @hash[o] = true if @proc.call(self, o) -# self -# end -# alias << add -# -# def add?(o) -# if include?(o) || !@proc.call(self, o) -# nil -# else -# @hash[o] = true -# self -# end -# end -# -# def replace(enum) -# enum.respond_to?(:each) or raise ArgumentError, "value must be enumerable" -# clear -# enum.each_entry { |o| add(o) } -# -# self -# end -# -# def merge(enum) -# enum.respond_to?(:each) or raise ArgumentError, "value must be enumerable" -# enum.each_entry { |o| add(o) } -# -# self -# end -# } -# else -# instance_eval %{ -# def add(o) -# if @proc.call(o) -# @hash[o] = true -# end -# self -# end -# alias << add -# -# def add?(o) -# if include?(o) || !@proc.call(o) -# nil -# else -# @hash[o] = true -# self -# end -# end -# } -# end -# -# super(*args) -# end -# -# def restriction_proc -# @proc -# end -# end - -# Tests have been moved to test/test_set.rb. - -### -### CompiledSet benchmark code for 'initialize' (nonempty set). -### - -sets = [] -i = 0 - -while i < 1_000_000 do - sets << CompiledSet.new([1,2,3,4,5]) - - i += 1 -end - -puts i -puts sets.size diff --git a/test/testdata/ruby_benchmark/stripe/csend.rb b/test/testdata/ruby_benchmark/stripe/csend.rb deleted file mode 100644 index 9ae171a092..0000000000 --- a/test/testdata/ruby_benchmark/stripe/csend.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -class A - def foo - 452 - end -end - -nil_ = T.let(nil, T.nilable(A)) -a = T.let(A.new, T.nilable(A)) - -i = 0 -while i < 10_000_000 - - nil_&.foo - a&.foo - - i += 1 -end - -puts i -p nil_&.foo -p a&.foo diff --git a/test/testdata/ruby_benchmark/stripe/dashboard_request_p.rb b/test/testdata/ruby_benchmark/stripe/dashboard_request_p.rb deleted file mode 100644 index aa005d9997..0000000000 --- a/test/testdata/ruby_benchmark/stripe/dashboard_request_p.rb +++ /dev/null @@ -1,146 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -# ::Boolean copied from extn/boolean.rb -module ::Boolean - extend T::Helpers - sealed! -end - -class ::FalseClass - include ::Boolean -end - -class ::TrueClass - include ::Boolean -end -# End code copied from extn/boolean.rb - -module Opus ; end - -# Begin mockup of autogenerated proto stuff -module Opus::Guildhall - module Autogen - module Proto - class MerchantMetadata - extend T::Sig - - sig {params(auth_type: Symbol).void.checked(:never)} - def initialize(auth_type) - @auth_type = auth_type - end - - sig {returns(Symbol).checked(:never)} - attr_reader :auth_type - - module AuthType - extend T::Sig - - UNKNOWN = T.let(0, Integer) - MANAGE = T.let(1, Integer) - GOOGLE_PAY = T.let(2, Integer) - STRIPE_ACCOUNT = T.let(3, Integer) - EPHEMERAL_KEY = T.let(4, Integer) - MERCHANT_KEY = T.let(5, Integer) - LIGHT = T.let(6, Integer) - - sig {params(value: Symbol).returns(T.nilable(Integer)).checked(:never)} - def self.resolve(value) - case value - when :UNKNOWN - UNKNOWN - when :MANAGE - MANAGE - when :GOOGLE_PAY - GOOGLE_PAY - when :STRIPE_ACCOUNT - STRIPE_ACCOUNT - when :EPHEMERAL_KEY - EPHEMERAL_KEY - when :MERCHANT_KEY - MERCHANT_KEY - when :LIGHT - LIGHT - else - nil - end - end - end - end - end - end -end -# End mockup of autogenerated proto stuff - -module Opus::APICore ; end - -# Begin code adapted from lib/api_core/state/guildhall.rb -module Opus::APICore::State - class Guildhall - extend T::Sig - - # Was params(env: Hash).returns(T.nilable(Opus::Guildhall::Autogen::Proto::MerchantMetadata)).checked(:tests) - sig {params(env: T::Hash[String, Opus::Guildhall::Autogen::Proto::MerchantMetadata]).returns(T.nilable(Opus::Guildhall::Autogen::Proto::MerchantMetadata)).checked(:never)} - def self.metadata(env) - # Key was Private::Constants::ENV_GUILDHALL_MERCHANT, which was defined as Opus::Utils::UniqueKey.new('ENV_GUILDHALL_MERCHANT') - env['ENV_GUILDHALL_MERCHANT'] - end - end -end -# End code adapted from lib/api_core/state/guildhall.rb - -# Begin code adapted from lib/api_core/state/type.rb -module Opus::APICore::State - class Type - extend T::Sig - - # Was no sig - sig {params(env: T::Hash[String, Opus::Guildhall::Autogen::Proto::MerchantMetadata]).returns(Boolean).checked(:never)} - private_class_method def self.dashboard_request?(env) - if (guildhall_merchant = Guildhall.metadata(env)) - Opus::Guildhall::Autogen::Proto::MerchantMetadata::AuthType.resolve(guildhall_merchant.auth_type) == Opus::Guildhall::Autogen::Proto::MerchantMetadata::AuthType::MANAGE - elsif env['stripe.api.plausible_session_key'] - # This code may be running before API authentication has run, so we don't yet have stripe.api.auth - # available to us. This is a bit of a hack, since we don't know for certain that this is a dashboard - # request, but that's acceptable. If we hit this condition, the request will either be authenticated as - # a dashboard request, or rejected as a 401. (TBH, Guildhall doesn't do much more checking than this - # for session-authenticated requests, so this is probably fine anyway.) - # A confession: half the reason I'm doing this is for our test suite, because we almost never have - # guildhall data available when running our tests. - true - else - false - end - end - end -end -# End code adapted from lib/api_core/state/type.rb - -# Benchmark code follows. - -class Opus::APICore::State::Type - extend T::Sig - - sig{void} - def self.do_test - # Stuff the hash with some other junk. - env = {} - 1000.times { |i| env["#{i}"] = Opus::Guildhall::Autogen::Proto::MerchantMetadata.new(:UNKNOWN) } - - # Populate the specific key that dashboard_request? will be looking for. - env['ENV_GUILDHALL_MERCHANT'] = Opus::Guildhall::Autogen::Proto::MerchantMetadata.new(:MANAGE) - - i = 0 - - while i < 10_000_000 - dashboard_request?(env) - - i += 1 - end - - puts i - end -end - -Opus::APICore::State::Type.do_test diff --git a/test/testdata/ruby_benchmark/stripe/deep_freeze.rb b/test/testdata/ruby_benchmark/stripe/deep_freeze.rb deleted file mode 100644 index 3a593fcf54..0000000000 --- a/test/testdata/ruby_benchmark/stripe/deep_freeze.rb +++ /dev/null @@ -1,134 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -require 'benchmark' - -class Module - include T::Sig -end - -module Opus; end -module Opus::Utils - module DeepFreeze - # Freeze a given object, and return all sub-items that also need freezing - sig(:final) {params(todo: T::Array[T.untyped], obj: T.untyped).void.checked(:tests)} - def self.freeze_one(todo, obj) - case obj - when Module - # Skip freezing modules/classes, they're very different - when Array, Struct - obj.freeze - # You can't concat a struct, but it has each so you can keep array/struct handling the same - obj.each do |value| - todo << value - end - when Hash - obj.freeze - obj.each do |key, value| - todo << key - todo << value - end - when Range - obj.freeze - todo << obj.begin - todo << obj.end - else - # Default to just freezing all instance variables - obj.freeze - obj.instance_variables.each do |iv| - todo << obj.instance_variable_get(iv) # rubocop:disable PrisonGuard/NoLurkyInstanceVariableAccess - end - end - end - - sig(:final) do - type_parameters(:T) - .params(obj: T.type_parameter(:T)) - .returns(T.type_parameter(:T)) - .checked(:never) - end - def self.freeze_unchecked!(obj) - todo = [T.unsafe(obj)] - seen = {} - - until todo.empty? - o = todo.pop - - case o - when NilClass, TrueClass, FalseClass - # don't need to be frozen - nil - # Short circuit on common classes. - # Dispatch on one class, so that the compiler has more static information per function call - when Symbol - o.freeze - when Numeric - o.freeze - when String - o.freeze - else - # Skip if we've already seen this object - if !seen[o.object_id] - seen[o.object_id] = true - freeze_one(todo, o) - end - end - end - - obj - end - - # Freeze the given object, and everything it contains. Returns the original object. - sig(:final) do - type_parameters(:T) - .params(obj: T.type_parameter(:T)) - .returns(T.type_parameter(:T)) - .checked(:never) # runtime typechecking disabled because of test/functional/api/critical_methods_no_runtime_typing.rb - end - def self.freeze!(obj) - freeze_unchecked!(obj) - end - end -end - -module Opus::Utils::DeepFreeze - class SomeClass - sig {params(foo: T.untyped).void} - def initialize(foo) - @foo = foo - end - end - - sig {params(n: Integer).returns(T::Array[T.untyped])} - def self.generate_deep(n) - o = T.let("foo".dup, T.untyped) - n.times do - o = SomeClass.new(o) - o = [o, "bar".dup] - o = {a: o, b: "iggy".dup} - o = Struct.new(:o, :p).new(o, "p".dup) - o = [1, 2, 3, o, 4] - end - o - end - - GENERATORS = { - nil: [5e5, -> {nil}], - string: [5e5, -> {"foo".dup}], - array: [1e4, -> {Array.new(100) {"foo".dup}}], - recursive: [200, -> {Opus::Utils::DeepFreeze.generate_deep(1)}], - deep: [200, -> {Opus::Utils::DeepFreeze.generate_deep(30)}], - } - - def self.main - GENERATORS.each do |name, (iterations, g)| - objs = 1.upto(iterations).map {g[]} - objs.each {|o| Opus::Utils::DeepFreeze.freeze!(o)} - end - - 0 - end -end - -Opus::Utils::DeepFreeze.main diff --git a/test/testdata/ruby_benchmark/stripe/final_method_call.rb b/test/testdata/ruby_benchmark/stripe/final_method_call.rb deleted file mode 100644 index 8620c79019..0000000000 --- a/test/testdata/ruby_benchmark/stripe/final_method_call.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -class A - extend T::Sig - - sig(:final) {void.checked(:never)} - def self.final_method - end -end - -i = 0 -while i < 10_000_000 - - A.final_method - - i += 1 -end - -puts i diff --git a/test/testdata/ruby_benchmark/stripe/format_tag_cleansing.rb b/test/testdata/ruby_benchmark/stripe/format_tag_cleansing.rb deleted file mode 100644 index c3201bab83..0000000000 --- a/test/testdata/ruby_benchmark/stripe/format_tag_cleansing.rb +++ /dev/null @@ -1,115 +0,0 @@ -# frozen_string_literal: true -# typed: strict -# compiled: true - -# ::Boolean copied from extn/boolean.rb - -module ::Boolean - extend T::Helpers - sealed! -end - -class ::FalseClass - include ::Boolean -end - -class ::TrueClass - include ::Boolean -end - -# End code copied from extn/boolean.rb - -# Begin code adapted from lib/server/lib/stripe-server/metrics.rb - -module M - extend T::Sig - - TagKey = T.type_alias {T.any(String, Symbol)} - TagValue = T.type_alias {T.any(Boolean, Numeric, String, Symbol, NilClass)} - - sig do - params( - key: TagKey, - value: TagValue, - allow_periods: Boolean, - ) - .returns(String) - .checked(:never) - end - private_class_method def self.format_tag(key, value, allow_periods) - key = key.to_s unless key.is_a?(String) - key = cleanse_tag(key, allow_periods) - - case value - when nil - return "#{key}:nil" - when true - return "#{key}:true" - when false - return "#{key}:false" - when String - value = cleanse_tag(value, allow_periods) - else - value = cleanse_tag(value.to_s, allow_periods) - end - - if value.empty? - "#{key}:nil" - else - "#{key}:#{value}" - end - end - - # Per Datadog's Docs: - # Tags must start with a letter, and after that may contain alphanumerics, underscores, minuses, colons, periods and slashes. - # Tags can be up to 200 characters long and support unicode. Tags will be converted to lowercase as well. - # We're being a bit more strict and only allowing alphanumerics and dashes. - # If allow_periods is true, the . character will be permitted as well except as the leading character - sig do - params( - value: String, - allow_periods: Boolean, - ) - .returns(String) - .checked(:never) - end - def self.cleanse_tag(value, allow_periods) - value = value.downcase if value.match?(/[A-Z]/) # We only care about ASCII uppercase since anything else will be stripped below - - # NB: In Ruby 2.6, the "in-place" versions of these methods (e.g. `tr!`) - # still allocate, and don't seem to be smart enough to skip doing so - # even if there's no change to be made. So we don't bother with them - # even in cases where we know it'd be safe to mutate the string. - if allow_periods - value = value.tr("^a-z0-9\-_.", "_") if value.match?(/[^a-z0-9\-_.]/) - value = value.sub('.', '_') if value.start_with?('.') - else - value = value.tr("^a-z0-9\-_", "_") if value.match?(/[^a-z0-9\-_]/) - end - - value - end -end - -# End code adapted from lib/server/lib/stripe-server/metrics.rb - -# The k/v here are chosen to force "cleanse_tag" to do some work. See also format_tag_no_cleansing.rb. -module M - sig{void} - def self.do_test - k = 'helloWorld.thisTag**fortytwo' - v = "{}#*@)*U)QWFNDOJVNZSFJS...)FEWJWF)(#UJ#@)GIJNOIU..___--___%@$(*!@&($+_+_+_s=DF{}{A{}SF{}AF}}" - - i = 0 - - while i < 1_000_000 - format_tag(k, v, true) - - i += 1 - end - - puts i - end -end - -M.do_test diff --git a/test/testdata/ruby_benchmark/stripe/format_tag_no_cleansing.rb b/test/testdata/ruby_benchmark/stripe/format_tag_no_cleansing.rb deleted file mode 100644 index def6b198de..0000000000 --- a/test/testdata/ruby_benchmark/stripe/format_tag_no_cleansing.rb +++ /dev/null @@ -1,115 +0,0 @@ -# frozen_string_literal: true -# typed: strict -# compiled: true - -# ::Boolean copied from extn/boolean.rb - -module ::Boolean - extend T::Helpers - sealed! -end - -class ::FalseClass - include ::Boolean -end - -class ::TrueClass - include ::Boolean -end - -# End code copied from extn/boolean.rb - -# Begin code adapted from lib/server/lib/stripe-server/metrics.rb - -module M - extend T::Sig - - TagKey = T.type_alias {T.any(String, Symbol)} - TagValue = T.type_alias {T.any(Boolean, Numeric, String, Symbol, NilClass)} - - sig do - params( - key: TagKey, - value: TagValue, - allow_periods: Boolean, - ) - .returns(String) - .checked(:never) - end - private_class_method def self.format_tag(key, value, allow_periods) - key = key.to_s unless key.is_a?(String) - key = cleanse_tag(key, allow_periods) - - case value - when nil - return "#{key}:nil" - when true - return "#{key}:true" - when false - return "#{key}:false" - when String - value = cleanse_tag(value, allow_periods) - else - value = cleanse_tag(value.to_s, allow_periods) - end - - if value.empty? - "#{key}:nil" - else - "#{key}:#{value}" - end - end - - # Per Datadog's Docs: - # Tags must start with a letter, and after that may contain alphanumerics, underscores, minuses, colons, periods and slashes. - # Tags can be up to 200 characters long and support unicode. Tags will be converted to lowercase as well. - # We're being a bit more strict and only allowing alphanumerics and dashes. - # If allow_periods is true, the . character will be permitted as well except as the leading character - sig do - params( - value: String, - allow_periods: Boolean, - ) - .returns(String) - .checked(:never) - end - def self.cleanse_tag(value, allow_periods) - value = value.downcase if value.match?(/[A-Z]/) # We only care about ASCII uppercase since anything else will be stripped below - - # NB: In Ruby 2.6, the "in-place" versions of these methods (e.g. `tr!`) - # still allocate, and don't seem to be smart enough to skip doing so - # even if there's no change to be made. So we don't bother with them - # even in cases where we know it'd be safe to mutate the string. - if allow_periods - value = value.tr("^a-z0-9\-_.", "_") if value.match?(/[^a-z0-9\-_.]/) - value = value.sub('.', '_') if value.start_with?('.') - else - value = value.tr("^a-z0-9\-_", "_") if value.match?(/[^a-z0-9\-_]/) - end - - value - end -end - -# End code adapted from lib/server/lib/stripe-server/metrics.rb - -# The k/v here are chosen to make "cleanse_tag" do as little work as possible. See also format_tag_cleansing.rb. -module M - sig{void} - def self.do_test - k = 'hello' - v = 42 - - i = 0 - - while i < 1_000_000 - format_tag(k, v, false) - - i += 1 - end - - puts i - end -end - -M.do_test diff --git a/test/testdata/ruby_benchmark/stripe/hash_delete.rb b/test/testdata/ruby_benchmark/stripe/hash_delete.rb deleted file mode 100644 index 3600fa97b5..0000000000 --- a/test/testdata/ruby_benchmark/stripe/hash_delete.rb +++ /dev/null @@ -1,22 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -h = {} - -i = 0 -while i < 10_000_000 - h[i] = i - i += 2 -end - -i = 0 -z = 0 -while i < 10_000_000 - z += (h.delete(i) || 0) - - i += 1 -end - -puts i -puts z diff --git a/test/testdata/ruby_benchmark/stripe/hash_delete_baseline.rb b/test/testdata/ruby_benchmark/stripe/hash_delete_baseline.rb deleted file mode 100644 index f1c4f1f328..0000000000 --- a/test/testdata/ruby_benchmark/stripe/hash_delete_baseline.rb +++ /dev/null @@ -1,22 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -h = {} - -i = 0 -while i < 10_000_000 - h[i] = i - i += 2 -end - -i = 0 -z = 0 -while i < 10_000_000 - z += i - - i += 1 -end - -puts i -puts z diff --git a/test/testdata/ruby_benchmark/stripe/hash_delete_blk.rb b/test/testdata/ruby_benchmark/stripe/hash_delete_blk.rb deleted file mode 100644 index ba3f9a5807..0000000000 --- a/test/testdata/ruby_benchmark/stripe/hash_delete_blk.rb +++ /dev/null @@ -1,22 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -h = {} - -i = 0 -while i < 10_000_000 - h[i] = i - i += 2 -end - -i = 0 -z = 0 -while i < 10_000_000 - z += h.delete(i) {0} - - i += 1 -end - -puts i -puts z diff --git a/test/testdata/ruby_benchmark/stripe/hash_dig.rb b/test/testdata/ruby_benchmark/stripe/hash_dig.rb deleted file mode 100644 index 68e437ae96..0000000000 --- a/test/testdata/ruby_benchmark/stripe/hash_dig.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -hash = {x: {y: {z: "hi"}}} - -i = 0 -sum = 0 - -while i < 10_000_000 - i += 1 - - sum += hash.dig(:x, :y, :z).length -end - -puts i -puts sum diff --git a/test/testdata/ruby_benchmark/stripe/hash_empty_p.rb b/test/testdata/ruby_benchmark/stripe/hash_empty_p.rb deleted file mode 100644 index 70e18e7956..0000000000 --- a/test/testdata/ruby_benchmark/stripe/hash_empty_p.rb +++ /dev/null @@ -1,12 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -hash = {x: 33, y: 27} - -i = 0 -while i < 100_000_000 do - i += (hash.empty? ? 0 : 1) -end - -p i diff --git a/test/testdata/ruby_benchmark/stripe/hash_fetch_block.rb b/test/testdata/ruby_benchmark/stripe/hash_fetch_block.rb deleted file mode 100644 index 6a25ccdc6a..0000000000 --- a/test/testdata/ruby_benchmark/stripe/hash_fetch_block.rb +++ /dev/null @@ -1,18 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -hash = {a: 10} - -i = 0 -misses = 0 - -while i < 10_000_000 do - i += 1 - - hash.fetch(:x) {misses += 1} - hash.fetch(:a) {misses += 1} -end - -p i -p misses diff --git a/test/testdata/ruby_benchmark/stripe/hash_merge.rb b/test/testdata/ruby_benchmark/stripe/hash_merge.rb deleted file mode 100644 index 3432688d2b..0000000000 --- a/test/testdata/ruby_benchmark/stripe/hash_merge.rb +++ /dev/null @@ -1,18 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -i = 0 -z = 0 - -h1 = {x: 1, y: 2, z: 3, w: 4} -h2 = {x: 3, z: 9, j: 22} - -while i < 10_000_000 - h3 = h1.merge(h2) - z += h3.length - - i += 1 -end - -puts z diff --git a/test/testdata/ruby_benchmark/stripe/hash_merge_baseline.rb b/test/testdata/ruby_benchmark/stripe/hash_merge_baseline.rb deleted file mode 100644 index 4f9dc37bd8..0000000000 --- a/test/testdata/ruby_benchmark/stripe/hash_merge_baseline.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -i = 0 -z = 0 - -h1 = {x: 1, y: 2, z: 3, w: 4} -h2 = {x: 3, z: 9, j: 22} - -while i < 10_000_000 - z += h1.length - - i += 1 -end - -puts z diff --git a/test/testdata/ruby_benchmark/stripe/hash_merge_block.rb b/test/testdata/ruby_benchmark/stripe/hash_merge_block.rb deleted file mode 100644 index 0ffc4d55c7..0000000000 --- a/test/testdata/ruby_benchmark/stripe/hash_merge_block.rb +++ /dev/null @@ -1,18 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -i = 0 -z = 0 - -h1 = {x: 1, y: 2, z: 3, w: 4} -h2 = {x: 3, z: 9, j: 22} - -while i < 10_000_000 - h3 = h1.merge(h2) {|k, o, n| o+n} - z += h3.length - - i += 1 -end - -puts z diff --git a/test/testdata/ruby_benchmark/stripe/hash_select.rb b/test/testdata/ruby_benchmark/stripe/hash_select.rb deleted file mode 100644 index 6c6824b6e7..0000000000 --- a/test/testdata/ruby_benchmark/stripe/hash_select.rb +++ /dev/null @@ -1,15 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -i = 0 -z = 0 - -while i < 1_000_000 - xs = {x: 99, q: 48, j: 21, e: 18}.select { |k, v| k==:x || v%2==0 } - z += xs.length - - i += 1 -end - -puts z diff --git a/test/testdata/ruby_benchmark/stripe/hash_select_baseline.rb b/test/testdata/ruby_benchmark/stripe/hash_select_baseline.rb deleted file mode 100644 index 3a00b17169..0000000000 --- a/test/testdata/ruby_benchmark/stripe/hash_select_baseline.rb +++ /dev/null @@ -1,15 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -i = 0 -z = 0 - -while i < 1_000_000 - xs = {x: 99, q: 48, j: 21, e: 18} - z += xs.length - - i += 1 -end - -puts z diff --git a/test/testdata/ruby_benchmark/stripe/hash_select_enum.rb b/test/testdata/ruby_benchmark/stripe/hash_select_enum.rb deleted file mode 100644 index ed24b7f1c0..0000000000 --- a/test/testdata/ruby_benchmark/stripe/hash_select_enum.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -i = 0 -z = 0 - -while i < 1_000_000 - xs = {x: 99, q: 48, j: 21, e: 18} - e = xs.select - ys = e.each { |k, v| k==:x || v%2==0 } - z += ys.length - - i += 1 -end - -puts z diff --git a/test/testdata/ruby_benchmark/stripe/hash_transform_values.rb b/test/testdata/ruby_benchmark/stripe/hash_transform_values.rb deleted file mode 100644 index 47994f3f26..0000000000 --- a/test/testdata/ruby_benchmark/stripe/hash_transform_values.rb +++ /dev/null @@ -1,18 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -i = 0 - -# 8 is a tipping point on the performance of this benchmark. Initially the compiler is about 2x faster than the -# interpreter, but anything above 8 starts to stretch out the interpreted time substantially, and really show the -# benefit of having compiled blocks. -xs = T.let((:a..).take(8).zip(1..).to_h.compact, T::Hash[Symbol, Integer]) - -while i < 1_000_000 - xs.transform_values{|a| a+1}.length - - i += 1 -end - -puts i diff --git a/test/testdata/ruby_benchmark/stripe/is_a_nil_str_obj.rb b/test/testdata/ruby_benchmark/stripe/is_a_nil_str_obj.rb deleted file mode 100644 index c95c983e02..0000000000 --- a/test/testdata/ruby_benchmark/stripe/is_a_nil_str_obj.rb +++ /dev/null @@ -1,48 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -nil_ = nil -str = '' - -i = 0 - -nil_isa_nil = T.let(nil, T.nilable(T::Boolean)) -str_isa_str = T.let(nil, T.nilable(T::Boolean)) - -str_isa_nil = T.let(nil, T.nilable(T::Boolean)) -nil_isa_str = T.let(nil, T.nilable(T::Boolean)) - -nil_isa_obj = T.let(nil, T.nilable(T::Boolean)) -str_isa_obj = T.let(nil, T.nilable(T::Boolean)) - -while i < 10_000_000 - - # Classes are equal, so ancestor search will exit early (in interpreter) - - nil_isa_nil = nil_.is_a?(NilClass) - str_isa_str = str.is_a?(String) - - # When the result is false the VM never exits early (inspects every ancestor) - - str_isa_nil = str.is_a?(NilClass) - nil_isa_str = nil_.is_a?(String) - - # Classes are not equal, but result is true (partial ancestor search) - - nil_isa_obj = nil_.is_a?(Object) - str_isa_obj = str.is_a?(Object) - - i += 1 -end - -puts "iterations: #{i}" -puts -puts "nil_.is_a?(NilClass) => #{nil_isa_nil}" -puts "str.is_a?(String) => #{str_isa_str}" -puts -puts "str.is_a?(NilClass) => #{str_isa_nil}" -puts "nil_.is_a?(String) => #{nil_isa_str}" -puts -puts "nil_.is_a?(Object) => #{nil_isa_obj}" -puts "str.is_a?(Object) => #{str_isa_obj}" diff --git a/test/testdata/ruby_benchmark/stripe/kernel_freeze.rb b/test/testdata/ruby_benchmark/stripe/kernel_freeze.rb deleted file mode 100644 index 2cb1a362a9..0000000000 --- a/test/testdata/ruby_benchmark/stripe/kernel_freeze.rb +++ /dev/null @@ -1,12 +0,0 @@ -# frozen_string_literal: true -# compiled: true -# typed: true - -i = 0 -while i < 10_000_000 do - [i, i, i, i].freeze - - i += 1 -end - -puts i diff --git a/test/testdata/ruby_benchmark/stripe/kwargs_splat.rb b/test/testdata/ruby_benchmark/stripe/kwargs_splat.rb deleted file mode 100644 index 56c9d51a70..0000000000 --- a/test/testdata/ruby_benchmark/stripe/kwargs_splat.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -def foo(**kwargs) -end - -i = 0 -while i < 1_000_000 - - args = {a: 1, b: 2} - foo(**args) - - i += 1 -end - -puts i diff --git a/test/testdata/ruby_benchmark/stripe/kwargs_splat_pass_through.rb b/test/testdata/ruby_benchmark/stripe/kwargs_splat_pass_through.rb deleted file mode 100644 index 860ee2fdf0..0000000000 --- a/test/testdata/ruby_benchmark/stripe/kwargs_splat_pass_through.rb +++ /dev/null @@ -1,19 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -def bar(**kwargs) -end - -def foo(**args) - bar(**args) -end - -args = {a: 1, b: 2} -i = 0 -while i < 1_000_000 - foo(**args) - i += 1 -end - -puts i diff --git a/test/testdata/ruby_benchmark/stripe/lspace.rb b/test/testdata/ruby_benchmark/stripe/lspace.rb deleted file mode 100644 index 6268a2f886..0000000000 --- a/test/testdata/ruby_benchmark/stripe/lspace.rb +++ /dev/null @@ -1,201 +0,0 @@ -# frozen_string_literal: true -# typed: strict -# compiled: true - -# pay-server does this stuff. It currently doesn't compile (haven't looked into -# why) and the benchmark harness requires single files, because it copies files -# around, any `require_relative` in the code would have its name changed. -# -# Just gonna hope that this code doesn't matter. -# -# class ::Thread -# class << self -# alias_method :new_without_lspace, :new -# alias_method :fork_without_lspace, :fork -# alias_method :start_without_lspace, :start -# end -# copied_lspace = Opus::LSpace.deep_copy_current -# -# T.unsafe(self).new_without_lspace(*args) do -# Opus::LSpace.enter_for_thread(copied_lspace) -# yield(*args) -# end -# end -# -# def self.fork(*args) -# copied_lspace = Opus::LSpace.deep_copy_current -# -# T.unsafe(self).fork_without_lspace(*args) do -# Opus::LSpace.enter_for_thread(copied_lspace) -# yield(*args) -# end -# end -# -# def self.start(*args) -# copied_lspace = Opus::LSpace.deep_copy_current -# -# T.unsafe(self).start_without_lspace(*args) do -# Opus::LSpace.enter_for_thread(copied_lspace) -# yield(*args) -# end -# end -#end - -class ::Exception - T::Sig::WithoutRuntime.sig(:final) {returns(T.nilable(Opus::LSpace::Private::AbstractSpace)).checked(:never)} - attr_accessor :lspace -end -module Opus; end -class Opus::LSpace - extend T::Sig - - class Key; end - - KeyTypes = T.type_alias {T.any(Key, Symbol, String)} - - module Private - class AbstractSpace - extend T::Sig - sig(:final) {returns(T::Hash[KeyTypes, T.untyped]).checked(:never)} - attr_reader :data - - sig {params(data: T::Hash[KeyTypes, T.untyped]).void.checked(:never)} - def initialize(data) - @data = data - end - - sig(:final) {params(key: KeyTypes).returns(T.untyped).checked(:never)} - def [](key) - @data[key] - end - end - - class WritableSpace < AbstractSpace - sig {params(data: T::Hash[KeyTypes, T.untyped]).void.checked(:never)} - def initialize(data) - super - @from_thread = T.let(Thread.current.object_id, Integer) - end - - sig(:final) do - params( - key: KeyTypes, - value: T.untyped, - ) - .void - .checked(:never) - end - def []=(key, value) - # This doesn't capture the entire story, because it's common for people to bypass LSpace#[]= - # and instead mutate the object directly. It's part of the way towards fixing up some of peoples shenanigans though. - if Thread.current.object_id != @from_thread - raise RuntimeError.new("Attempting to set an LSpace from a different thread that created it.") - end - - @data[key] = value - end - end - end - - sig(:final) {params(key: KeyTypes, value: T.untyped).void.checked(:never)} - def self.[]=(key, value) - current[key] = value - end - - sig(:final) {params(key: KeyTypes).returns(T.untyped).checked(:never)} - def self.[](key) - Thread.current[:lspace_internal]&.[](key) - end - - sig(:final) {returns(Private::WritableSpace).checked(:never)} - def self.current - lspace = Thread.current[:lspace] - return lspace if lspace - - lspace = Private::WritableSpace.new({}) - - Thread.current[:lspace] = lspace - Thread.current[:lspace_internal] = lspace.data - lspace - end - - sig(:final) do - type_parameters(:Result) - .params( - data: T::Hash[KeyTypes, T.untyped], - blk: T.proc.returns(T.type_parameter(:Result)), - ) - .returns(T.type_parameter(:Result)) - .checked(:never) - end - def self.with(data, &blk) - # NOTE: This is an inlined version of `override_current`. - # - # We inline for to avoid method call overhead and to save a stack frame. - previous = Thread.current[:lspace] - begin - self.current = Private::WritableSpace.new(current.data.merge(data)) - yield - rescue => ex - ex.lspace ||= current - raise - ensure - self.current = previous - end - end - - sig(:final) {params(lspace: T.nilable(Private::AbstractSpace)).void.checked(:never)} - private_class_method def self.current=(lspace) - Thread.current[:lspace] = lspace - Thread.current[:lspace_internal] = lspace&.data - end - - # Clone the entire LSpace with a deep copy, preventing writes across boundaries. - # Used for things like forking or thread creation. - sig(:final) {returns(T::Hash[KeyTypes, T.untyped]).checked(:tests)} - def self.deep_copy_current - deep_copy(current.data) - end - - # WARNING - # This is only offered as a way of re-entering a cloned LSpace in a Thread or fork. - # You should not be building code that relies on it directly. If you need to re-enter LSpace use `enter`. - sig(:final) {params(data: T::Hash[KeyTypes, T.untyped]).void} - def self.enter_for_thread(data) - if Thread.current[:lspace] - raise RuntimeError.new("You cannot call this if Thread.current has a LSpace in it") - end - - self.current = Private::WritableSpace.new(data) - end - - sig(:final) {params(data: Object).returns(T.untyped).checked(:never)} - private_class_method def self.deep_copy(data) - if data.is_a?(Array) - data.map {|row| deep_copy(row)} - elsif data.is_a?(Hash) - copied = {} - data.each do |key, value| - copied[key] = deep_copy(value) - end - - copied - else - data - end - end -end - - -i = 0 -Opus::LSpace.with(foo: 1) do - while i < 10_000_000 - - # TODO(jez) Benchmark for time to lookup "unknown" key - Opus::LSpace[:foo] - - i += 1 - end -end - -puts i diff --git a/test/testdata/ruby_benchmark/stripe/method_call_with_7_args.rb b/test/testdata/ruby_benchmark/stripe/method_call_with_7_args.rb deleted file mode 100644 index 97bd7f087c..0000000000 --- a/test/testdata/ruby_benchmark/stripe/method_call_with_7_args.rb +++ /dev/null @@ -1,16 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -def foo7(a, b, c, d, e, f, g) -end - -i = 0 -while i < 10_000_000 - - foo7(1, 2, 3, 4, 5, 6, 7) - - i += 1 -end - -puts i diff --git a/test/testdata/ruby_benchmark/stripe/method_call_with_7_args_blk.rb b/test/testdata/ruby_benchmark/stripe/method_call_with_7_args_blk.rb deleted file mode 100644 index b2a3a964e5..0000000000 --- a/test/testdata/ruby_benchmark/stripe/method_call_with_7_args_blk.rb +++ /dev/null @@ -1,16 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -def foo7_blk(a, b, c, d, e, f, g, &blk) -end - -i = 0 -while i < 10_000_000 - - foo7_blk(1, 2, 3, 4, 5, 6, 7) do end - - i += 1 -end - -puts i diff --git a/test/testdata/ruby_benchmark/stripe/nil_p.rb b/test/testdata/ruby_benchmark/stripe/nil_p.rb deleted file mode 100644 index c2de65edfc..0000000000 --- a/test/testdata/ruby_benchmark/stripe/nil_p.rb +++ /dev/null @@ -1,49 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -class GrandParent -end - -class Parent < GrandParent -end - -class Child < Parent -end - -grand_parent = GrandParent.new -parent = Parent.new -child = Child.new - -nil_as_object = T.let(nil, Object) -int = T.let(0, Integer) -str = T.let('', String) -sym = T.let(:'', Symbol) - -i = 0 -while i < 10_000_000 - - grand_parent.nil? - parent.nil? - child.nil? - - nil_as_object.nil? - int.nil? - str.nil? - - sym.nil? - - i += 1 -end - -puts i - -p grand_parent.nil? -p parent.nil? -p child.nil? - -p nil_as_object.nil? -p int.nil? -p str.nil? - -p sym.nil? diff --git a/test/testdata/ruby_benchmark/stripe/prop_const_getter.llo.exp b/test/testdata/ruby_benchmark/stripe/prop_const_getter.llo.exp deleted file mode 100644 index 53beb57185..0000000000 --- a/test/testdata/ruby_benchmark/stripe/prop_const_getter.llo.exp +++ /dev/null @@ -1,1136 +0,0 @@ -; ModuleID = 'payload' -source_filename = "llvm-link" -target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - -%struct.rb_vm_struct = type { i64, %struct.rb_global_vm_lock_struct, %struct.rb_thread_struct*, %struct.rb_thread_struct*, i8*, i64, %union.pthread_mutex_t, %struct.list_head, %struct.list_head, %struct.list_head, %struct.list_head, i64, i32, i32, i8, i32, i64, [5 x i64], i64, i64, i64, i64, i64, i64, i64, %struct.st_table*, %struct.st_table*, %struct.anon.5, %struct.rb_hook_list_struct, %struct.st_table*, %struct.rb_postponed_job_struct*, i32, i32, %struct.list_head, %union.pthread_mutex_t, i64, i64, i64, i64, i64, i32, %struct.st_table*, %struct.rb_objspace*, %struct.rb_at_exit_list*, i64*, %struct.st_table*, %struct.rb_builtin_function*, i32, %struct.anon.6, [29 x i16] } -%struct.rb_global_vm_lock_struct = type { %struct.rb_thread_struct*, %union.pthread_mutex_t, %struct.list_head, %struct.rb_thread_struct*, i32, %union.pthread_cond_t, %union.pthread_cond_t, i32, i32 } -%union.pthread_cond_t = type { %struct.__pthread_cond_s } -%struct.__pthread_cond_s = type { %union.anon, %union.anon, [2 x i32], [2 x i32], i32, i32, [2 x i32] } -%union.anon = type { i64 } -%struct.rb_thread_struct = type { %struct.list_node, i64, %struct.rb_vm_struct*, %struct.rb_execution_context_struct*, i64, %struct.rb_calling_info*, i64, i64, i64, i8, i8, i32, %struct.native_thread_data_struct, i8*, i64, i64, i64, i64, %union.pthread_mutex_t, %struct.rb_unblock_callback, i64, %struct.rb_mutex_struct*, %struct.rb_thread_list_struct*, %union.anon.10, i32, i64, %struct.rb_fiber_struct*, [5 x i8*], i64 } -%struct.list_node = type { %struct.list_node*, %struct.list_node* } -%struct.rb_execution_context_struct = type { i64*, i64, %struct.rb_control_frame_struct*, %struct.rb_vm_tag*, %struct.rb_vm_protect_tag*, i32, i32, %struct.rb_fiber_struct*, %struct.rb_thread_struct*, %struct.st_table*, i64, i64, i64*, i64, %struct.rb_ensure_list*, %struct.rb_trace_arg_struct*, i64, i64, i8, i8, i64, %struct.anon.7 } -%struct.rb_control_frame_struct = type { i64*, i64*, %struct.rb_iseq_struct*, i64, i64*, i8*, i64* } -%struct.rb_iseq_struct = type { i64, i64, %struct.rb_iseq_constant_body*, %union.anon.17 } -%struct.rb_iseq_constant_body = type { i32, i32, i64*, %struct.anon, %struct.rb_iseq_location_struct, %struct.iseq_insn_info, i64*, %struct.iseq_catch_table*, %struct.rb_iseq_struct*, %struct.rb_iseq_struct*, %union.iseq_inline_storage_entry*, %struct.rb_call_data*, %struct.anon.16, i32, i32, i32, i32, i32, i8, i64 } -%struct.anon = type { %struct.anon.0, i32, i32, i32, i32, i32, i32, i32, i64*, %struct.rb_iseq_param_keyword* } -%struct.anon.0 = type { i16, [2 x i8] } -%struct.rb_iseq_param_keyword = type { i32, i32, i32, i32, i64*, i64* } -%struct.rb_iseq_location_struct = type { i64, i64, i64, i64, i32, %struct.rb_code_location_struct } -%struct.rb_code_location_struct = type { %struct.rb_code_position_struct, %struct.rb_code_position_struct } -%struct.rb_code_position_struct = type { i32, i32 } -%struct.iseq_insn_info = type { %struct.iseq_insn_info_entry*, i32*, i32, %struct.succ_index_table* } -%struct.iseq_insn_info_entry = type opaque -%struct.succ_index_table = type opaque -%struct.iseq_catch_table = type opaque -%union.iseq_inline_storage_entry = type { %struct.iseq_inline_cache_entry } -%struct.iseq_inline_cache_entry = type { i64, %struct.rb_cref_struct*, i64 } -%struct.rb_cref_struct = type { i64, i64, i64, %struct.rb_cref_struct*, %struct.rb_scope_visi_struct } -%struct.rb_scope_visi_struct = type { i8, [3 x i8] } -%struct.rb_call_data = type { %struct.rb_call_cache, %struct.rb_call_info } -%struct.rb_call_cache = type { i64, [3 x i64], %struct.rb_callable_method_entry_struct*, i64, i64 (%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_calling_info*, %struct.rb_call_data*)*, %union.anon.15 } -%struct.rb_callable_method_entry_struct = type { i64, i64, %struct.rb_method_definition_struct*, i64, i64 } -%struct.rb_method_definition_struct = type { i64, %union.anon.13, i64, i64 } -%union.anon.13 = type { %struct.rb_method_cfunc_struct } -%struct.rb_method_cfunc_struct = type { i64 (...)*, i64 (i64, i32, i64*, i64 (...)*)*, i32 } -%union.anon.15 = type { i32 } -%struct.rb_call_info = type { i64, i32, i32 } -%struct.anon.16 = type { i64, i64, i64, i64* } -%union.anon.17 = type { %struct.anon.18 } -%struct.anon.18 = type { i64, i32 } -%struct.rb_vm_tag = type { i64, i64, [5 x i8*], %struct.rb_vm_tag*, i32 } -%struct.rb_vm_protect_tag = type { %struct.rb_vm_protect_tag* } -%struct.rb_ensure_list = type { %struct.rb_ensure_list*, %struct.rb_ensure_entry } -%struct.rb_ensure_entry = type { i64, i64 (i64)*, i64 } -%struct.rb_trace_arg_struct = type { i32, %struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, i64, i64, i64, i64, i64, i32, i32, i64 } -%struct.anon.7 = type { i64*, i64*, i64, [1 x %struct.__jmp_buf_tag] } -%struct.__jmp_buf_tag = type { [8 x i64], i32, %struct.__sigset_t } -%struct.__sigset_t = type { [16 x i64] } -%struct.rb_calling_info = type { i64, i64, i32, i32 } -%struct.native_thread_data_struct = type { %struct.list_head, %union.anon.9 } -%union.anon.9 = type { %union.pthread_cond_t } -%struct.rb_unblock_callback = type { void (i8*)*, i8* } -%struct.rb_mutex_struct = type opaque -%struct.rb_thread_list_struct = type { %struct.rb_thread_list_struct*, %struct.rb_thread_struct* } -%union.anon.10 = type { %struct.anon.11 } -%struct.anon.11 = type { i64, i64, i32 } -%struct.rb_fiber_struct = type opaque -%struct.anon.5 = type { [65 x i64] } -%struct.rb_hook_list_struct = type { %struct.rb_event_hook_struct*, i32, i32, i32 } -%struct.rb_event_hook_struct = type opaque -%struct.rb_postponed_job_struct = type opaque -%struct.list_head = type { %struct.list_node } -%union.pthread_mutex_t = type { %struct.__pthread_mutex_s } -%struct.__pthread_mutex_s = type { i32, i32, i32, i32, i32, i16, i16, %struct.__pthread_internal_list } -%struct.__pthread_internal_list = type { %struct.__pthread_internal_list*, %struct.__pthread_internal_list* } -%struct.rb_objspace = type opaque -%struct.rb_at_exit_list = type { void (%struct.rb_vm_struct*)*, %struct.rb_at_exit_list* } -%struct.st_table = type { i8, i8, i8, i32, %struct.st_hash_type*, i64, i64*, i64, i64, %struct.st_table_entry* } -%struct.st_hash_type = type { i32 (i64, i64)*, i64 (i64)* } -%struct.st_table_entry = type opaque -%struct.rb_builtin_function = type opaque -%struct.anon.6 = type { i64, i64, i64, i64 } -%struct.SorbetLineNumberInfo = type { i32, %struct.iseq_insn_info_entry*, i64* } -%struct.FunctionInlineCache = type { %struct.rb_kwarg_call_data } -%struct.rb_kwarg_call_data = type { %struct.rb_call_cache, %struct.rb_call_info_with_kwarg } -%struct.rb_call_info_with_kwarg = type { %struct.rb_call_info, %struct.rb_call_info_kw_arg* } -%struct.rb_call_info_kw_arg = type { i32, [1 x i64] } -%struct.iseq_inline_iv_cache_entry = type { i64, i64 } -%struct.RClass = type { %struct.iseq_inline_iv_cache_entry, i64, %struct.rb_classext_struct*, i64 } -%struct.rb_classext_struct = type { %struct.st_table*, %struct.st_table*, %struct.rb_id_table*, %struct.rb_id_table*, %struct.rb_id_table*, %struct.rb_subclass_entry*, %struct.rb_subclass_entry**, %struct.rb_subclass_entry**, i64, i64, i64 (i64)*, i64 } -%struct.rb_id_table = type opaque -%struct.rb_subclass_entry = type { i64, %struct.rb_subclass_entry* } - -@ruby_current_vm_ptr = external local_unnamed_addr global %struct.rb_vm_struct*, align 8 -@ruby_current_execution_context_ptr = external local_unnamed_addr global %struct.rb_execution_context_struct*, align 8 -@sorbet_getVoidSingleton.name = internal constant [30 x i8] c"T::Private::Types::Void::VOID\00", align 16 -@ruby_vm_global_constant_state = external local_unnamed_addr global i64, align 8 -@.str = private unnamed_addr constant [6 x i8] c"@%li\0B\00", align 1 -@rb_eRuntimeError = external local_unnamed_addr global i64, align 8 -@.str.7 = private unnamed_addr constant [42 x i8] c"unimplemented super with a missing method\00", align 1 -@.str.9 = private unnamed_addr constant [95 x i8] c"sorbet_getBuildSCMRevision: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@.str.10 = private unnamed_addr constant [93 x i8] c"sorbet_getIsReleaseBuild: Shared objects compiled by sorbet_llvm must be run by sorbet_ruby.\00", align 1 -@"stackFramePrecomputed_func_.17$152" = unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"rubyIdPrecomputed_" = internal unnamed_addr global i64 0, align 8 -@"str_" = private unnamed_addr constant [17 x i8] c"\00", align 1 -@"rubyStrFrozen_" = internal unnamed_addr global i64 0, align 8 -@"rubyStrFrozen_test/testdata/ruby_benchmark/stripe/prop_const_getter.rb" = internal unnamed_addr global i64 0, align 8 -@"str_test/testdata/ruby_benchmark/stripe/prop_const_getter.rb" = private unnamed_addr constant [57 x i8] c"test/testdata/ruby_benchmark/stripe/prop_const_getter.rb\00", align 1 -@iseqEncodedArray = internal global [21 x i64] zeroinitializer -@fileLineNumberInfo = internal global %struct.SorbetLineNumberInfo zeroinitializer -@str_MyStruct = private unnamed_addr constant [9 x i8] c"MyStruct\00", align 1 -@"str_T::Struct" = private unnamed_addr constant [10 x i8] c"T::Struct\00", align 1 -@rubyIdPrecomputed_foo = internal unnamed_addr global i64 0, align 8 -@str_foo = private unnamed_addr constant [4 x i8] c"foo\00", align 1 -@ic_new = internal global %struct.FunctionInlineCache zeroinitializer -@rubyIdPrecomputed_new = internal unnamed_addr global i64 0, align 8 -@str_new = private unnamed_addr constant [4 x i8] c"new\00", align 1 -@"rubyIdPrecomputed_<" = internal unnamed_addr global i64 0, align 8 -@"str_<" = private unnamed_addr constant [2 x i8] c"<\00", align 1 -@"ic_<" = internal global %struct.FunctionInlineCache zeroinitializer -@ic_foo = internal global %struct.FunctionInlineCache zeroinitializer -@"rubyIdPrecomputed_+" = internal unnamed_addr global i64 0, align 8 -@"str_+" = private unnamed_addr constant [2 x i8] c"+\00", align 1 -@"ic_+" = internal global %struct.FunctionInlineCache zeroinitializer -@ic_puts = internal global %struct.FunctionInlineCache zeroinitializer -@rubyIdPrecomputed_puts = internal unnamed_addr global i64 0, align 8 -@str_puts = private unnamed_addr constant [5 x i8] c"puts\00", align 1 -@ic_foo.1 = internal global %struct.FunctionInlineCache zeroinitializer -@ic_puts.2 = internal global %struct.FunctionInlineCache zeroinitializer -@"stackFramePrecomputed_func_MyStruct#10initialize" = unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@rubyIdPrecomputed_initialize = internal unnamed_addr global i64 0, align 8 -@str_initialize = private unnamed_addr constant [11 x i8] c"initialize\00", align 1 -@str_sig = private unnamed_addr constant [4 x i8] c"sig\00", align 1 -@str_Integer = private unnamed_addr constant [8 x i8] c"Integer\00", align 1 -@"ivc_@foo" = internal global %struct.iseq_inline_iv_cache_entry zeroinitializer -@"rubyIdPrecomputed_@foo" = internal unnamed_addr global i64 0, align 8 -@"str_@foo" = private unnamed_addr constant [5 x i8] c"@foo\00", align 1 -@"str_" = private unnamed_addr constant [8 x i8] c"\00", align 1 -@"" = internal unnamed_addr global i64 0 -@"stackFramePrecomputed_func_MyStruct.13" = unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"rubyIdPrecomputed_" = internal unnamed_addr global i64 0, align 8 -@"str_" = private unnamed_addr constant [17 x i8] c"\00", align 1 -@"rubyIdPrecomputed_" = internal unnamed_addr global i64 0, align 8 -@"str_" = private unnamed_addr constant [13 x i8] c"\00", align 1 -@"stackFramePrecomputed_func_MyStruct.13$block_1" = unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@"rubyIdPrecomputed_block in " = internal unnamed_addr global i64 0, align 8 -@"str_block in " = private unnamed_addr constant [26 x i8] c"block in \00", align 1 -@"rubyStrFrozen_block in " = internal unnamed_addr global i64 0, align 8 -@"stackFramePrecomputed_func_MyStruct.13$block_2" = unnamed_addr global %struct.rb_iseq_struct* null, align 8 -@ic_params = internal global %struct.FunctionInlineCache zeroinitializer -@rubyIdPrecomputed_params = internal unnamed_addr global i64 0, align 8 -@str_params = private unnamed_addr constant [7 x i8] c"params\00", align 1 -@ic_void = internal global %struct.FunctionInlineCache zeroinitializer -@rubyIdPrecomputed_void = internal unnamed_addr global i64 0, align 8 -@str_void = private unnamed_addr constant [5 x i8] c"void\00", align 1 -@ic_returns = internal global %struct.FunctionInlineCache zeroinitializer -@rubyIdPrecomputed_returns = internal unnamed_addr global i64 0, align 8 -@str_returns = private unnamed_addr constant [8 x i8] c"returns\00", align 1 -@str_normal = private unnamed_addr constant [7 x i8] c"normal\00", align 1 -@rubyIdPrecomputed_without_accessors = internal unnamed_addr global i64 0, align 8 -@str_without_accessors = private unnamed_addr constant [18 x i8] c"without_accessors\00", align 1 -@ic_const = internal global %struct.FunctionInlineCache zeroinitializer -@rubyIdPrecomputed_const = internal unnamed_addr global i64 0, align 8 -@str_const = private unnamed_addr constant [6 x i8] c"const\00", align 1 -@str_attr_reader = private unnamed_addr constant [12 x i8] c"attr_reader\00", align 1 -@guard_epoch_MyStruct = linkonce local_unnamed_addr global i64 0 -@guarded_const_MyStruct = linkonce local_unnamed_addr global i64 0 -@rb_cInteger = external local_unnamed_addr constant i64 -@"guard_epoch_T::Struct" = linkonce local_unnamed_addr global i64 0 -@"guarded_const_T::Struct" = linkonce local_unnamed_addr global i64 0 - -; Function Attrs: nounwind readnone willreturn -declare i64 @rb_id2sym(i64) local_unnamed_addr #0 - -; Function Attrs: cold noreturn -declare void @sorbet_cast_failure(i64, i8*, i8*) local_unnamed_addr #1 - -; Function Attrs: noreturn -declare void @sorbet_raiseArity(i32, i32, i32) local_unnamed_addr #2 - -; Function Attrs: noreturn -declare void @sorbet_raiseMissingKeywords(i64) local_unnamed_addr #2 - -; Function Attrs: noreturn -declare void @sorbet_raiseExtraKeywords(i64) local_unnamed_addr #2 - -declare i64 @sorbet_addMissingKWArg(i64, i64) local_unnamed_addr #3 - -declare %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64, i64, i64, i64, %struct.rb_iseq_struct*, i32, i32, %struct.SorbetLineNumberInfo*, i64*, i32, i32) local_unnamed_addr #3 - -declare void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo*, i64*, i32) local_unnamed_addr #3 - -declare i64 @sorbet_getConstant(i8*, i64) local_unnamed_addr #3 - -declare i64 @sorbet_readRealpath() local_unnamed_addr #3 - -declare %struct.rb_control_frame_struct* @sorbet_pushStaticInitFrame(i64) local_unnamed_addr #3 - -declare void @sorbet_popFrame() local_unnamed_addr #3 - -declare void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache*, i64, i32, i32, i32, i64*) local_unnamed_addr #3 - -declare i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache*, i64) local_unnamed_addr #3 - -declare i64 @sorbet_getPassedBlockHandler(...) local_unnamed_addr #3 - -declare void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct*, %struct.rb_control_frame_struct*, %struct.rb_iseq_struct*) local_unnamed_addr #3 - -declare i64 @sorbet_rb_int_plus_slowpath(i64, i64) local_unnamed_addr #3 - -declare i64 @sorbet_rb_int_lt_slowpath(i64, i64) local_unnamed_addr #3 - -declare void @sorbet_vm_setivar(i64, i64, i64, %struct.iseq_inline_iv_cache_entry*) local_unnamed_addr #3 - -declare void @sorbet_vm_define_method(i64, i8*, i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*)*, i8*, %struct.rb_iseq_struct*, i1 zeroext) local_unnamed_addr #3 - -declare i64 @sorbet_vm_fstring_new(i8*, i64) local_unnamed_addr #3 - -declare i32 @rb_block_given_p() local_unnamed_addr #3 - -declare i64 @rb_block_proc() local_unnamed_addr #3 - -declare i64 @rb_define_class(i8*, i64) local_unnamed_addr #3 - -declare i64 @rb_intern(i8*) local_unnamed_addr #3 - -declare i64 @rb_id2str(i64) local_unnamed_addr #3 - -declare i64 @rb_sprintf(i8*, ...) local_unnamed_addr #3 - -declare i64 @rb_intern_str(i64) local_unnamed_addr #3 - -declare void @rb_add_method(i64, i64, i32, i8*, i32) local_unnamed_addr #3 - -declare i64 @rb_intern2(i8*, i64) local_unnamed_addr #3 - -declare i64 @rb_hash_new_with_size(i64) local_unnamed_addr #3 - -declare void @rb_hash_bulk_insert(i64, i64*, i64) local_unnamed_addr #3 - -declare void @rb_gc_register_mark_object(i64) local_unnamed_addr #3 - -; Function Attrs: noreturn -declare void @rb_raise(i64, i8*, ...) local_unnamed_addr #2 - -declare i64 @rb_int2big(i64) local_unnamed_addr #3 - -; Function Attrs: nofree nosync nounwind readnone speculatable willreturn -declare { i64, i1 } @llvm.sadd.with.overflow.i64(i64, i64) #4 - -declare i64 @rb_hash_lookup2(i64, i64, i64) local_unnamed_addr #3 - -declare i64 @rb_hash_size_num(i64) local_unnamed_addr #3 - -declare i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct*, i32) local_unnamed_addr #3 - -declare %struct.rb_callable_method_entry_struct* @rb_vm_frame_method_entry(%struct.rb_control_frame_struct*) local_unnamed_addr #3 - -declare %struct.rb_callable_method_entry_struct* @rb_callable_method_entry(i64, i64) local_unnamed_addr #3 - -declare i64 @rb_vm_call_kw(%struct.rb_execution_context_struct*, i64, i64, i32, i64*, %struct.rb_callable_method_entry_struct*, i32) local_unnamed_addr #3 - -; Function Attrs: allocsize(0,1) -declare noalias nonnull i8* @ruby_xcalloc(i64, i64) local_unnamed_addr #5 - -; Function Attrs: allocsize(0,1) -declare noalias nonnull i8* @ruby_xmalloc2(i64, i64) local_unnamed_addr #5 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) #6 - -; Function Attrs: nounwind ssp uwtable -define weak i32 @sorbet_getIsReleaseBuild() local_unnamed_addr #7 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([93 x i8], [93 x i8]* @.str.10, i64 0, i64 0)) #15 - unreachable -} - -; Function Attrs: nounwind ssp uwtable -define weak i8* @sorbet_getBuildSCMRevision() local_unnamed_addr #7 { - %1 = load i64, i64* @rb_eRuntimeError, align 8, !tbaa !6 - tail call void (i64, i8*, ...) @rb_raise(i64 %1, i8* noundef getelementptr inbounds ([95 x i8], [95 x i8]* @.str.9, i64 0, i64 0)) #15 - unreachable -} - -; Function Attrs: nounwind sspreq uwtable -define internal fastcc void @"func_MyStruct.13L62"(i64 %selfRaw, %struct.rb_control_frame_struct* %cfp) unnamed_addr #8 !dbg !10 { -functionEntryInitializers: - %stackFrame = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_MyStruct.13", align 8 - %0 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !14 - %1 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %0, i64 0, i32 2 - %2 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %1, align 8, !tbaa !16 - %3 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame, %struct.rb_iseq_struct** %3, align 8, !tbaa !20 - %4 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %2, i64 0, i32 4 - %5 = load i64*, i64** %4, align 8, !tbaa !22 - %6 = load i64, i64* %5, align 8, !tbaa !6 - %7 = and i64 %6, -33 - store i64 %7, i64* %5, align 8, !tbaa !6 - tail call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %0, %struct.rb_control_frame_struct* %2, %struct.rb_iseq_struct* %stackFrame) #16 - %8 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 0 - store i64* getelementptr inbounds ([21 x i64], [21 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %8, align 8, !dbg !23, !tbaa !14 - %9 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !24, !tbaa !14 - %10 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %9, i64 0, i32 5, !dbg !24 - %11 = load i32, i32* %10, align 8, !dbg !24, !tbaa !25 - %12 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %9, i64 0, i32 6, !dbg !24 - %13 = load i32, i32* %12, align 4, !dbg !24, !tbaa !26 - %14 = xor i32 %13, -1, !dbg !24 - %15 = and i32 %14, %11, !dbg !24 - %16 = icmp eq i32 %15, 0, !dbg !24 - br i1 %16, label %rb_vm_check_ints.exit3, label %17, !dbg !24, !prof !27 - -17: ; preds = %functionEntryInitializers - %18 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %9, i64 0, i32 8, !dbg !24 - %19 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %18, align 8, !dbg !24, !tbaa !28 - %20 = tail call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %19, i32 noundef 0) #16, !dbg !24 - br label %rb_vm_check_ints.exit3, !dbg !24 - -rb_vm_check_ints.exit3: ; preds = %functionEntryInitializers, %17 - store i64* getelementptr inbounds ([21 x i64], [21 x i64]* @iseqEncodedArray, i64 0, i64 6), i64** %8, align 8, !dbg !24, !tbaa !14 - %21 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !29, !tbaa !14 - %22 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %21, i64 0, i32 5, !dbg !29 - %23 = load i32, i32* %22, align 8, !dbg !29, !tbaa !25 - %24 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %21, i64 0, i32 6, !dbg !29 - %25 = load i32, i32* %24, align 4, !dbg !29, !tbaa !26 - %26 = xor i32 %25, -1, !dbg !29 - %27 = and i32 %26, %23, !dbg !29 - %28 = icmp eq i32 %27, 0, !dbg !29 - br i1 %28, label %sorbet_setupParamKeywords.exit, label %29, !dbg !29, !prof !27 - -29: ; preds = %rb_vm_check_ints.exit3 - %30 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %21, i64 0, i32 8, !dbg !29 - %31 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %30, align 8, !dbg !29, !tbaa !28 - %32 = tail call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %31, i32 noundef 0) #16, !dbg !29 - br label %sorbet_setupParamKeywords.exit, !dbg !29 - -sorbet_setupParamKeywords.exit: ; preds = %29, %rb_vm_check_ints.exit3 - store i64* getelementptr inbounds ([21 x i64], [21 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %8, align 8, !dbg !29, !tbaa !14 - %33 = load i64, i64* @guard_epoch_MyStruct, align 8, !dbg !24 - %34 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !24, !tbaa !30 - %needTakeSlowPath = icmp ne i64 %33, %34, !dbg !24 - br i1 %needTakeSlowPath, label %35, label %36, !dbg !24, !prof !32 - -35: ; preds = %sorbet_setupParamKeywords.exit - tail call void @const_recompute_MyStruct(), !dbg !24 - br label %36, !dbg !24 - -36: ; preds = %sorbet_setupParamKeywords.exit, %35 - %37 = load i64, i64* @guarded_const_MyStruct, align 8, !dbg !24 - %38 = load i64, i64* @guard_epoch_MyStruct, align 8, !dbg !24 - %39 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !24, !tbaa !30 - %guardUpdated = icmp eq i64 %38, %39, !dbg !24 - tail call void @llvm.assume(i1 %guardUpdated), !dbg !24 - %stackFrame60 = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_MyStruct#10initialize", align 8, !dbg !24 - %40 = tail call noalias nonnull i8* @ruby_xcalloc(i64 noundef 1, i64 noundef 64) #17, !dbg !24 - %41 = bitcast i8* %40 to i16*, !dbg !24 - %42 = load i16, i16* %41, align 8, !dbg !24 - %43 = and i16 %42, -384, !dbg !24 - %44 = or i16 %43, 16, !dbg !24 - store i16 %44, i16* %41, align 8, !dbg !24 - %45 = getelementptr inbounds i8, i8* %40, i64 8, !dbg !24 - %46 = bitcast i8* %45 to i32*, !dbg !24 - %47 = getelementptr inbounds i8, i8* %40, i64 4, !dbg !24 - %48 = bitcast i8* %47 to i32*, !dbg !24 - tail call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %45, i8 0, i64 24, i1 false), !dbg !24 - store i32 1, i32* %48, align 4, !dbg !24, !tbaa !33 - %keyword_table = alloca i64, align 8, !dbg !24 - %rubyId_foo = load i64, i64* @rubyIdPrecomputed_foo, align 8, !dbg !24 - store i64 %rubyId_foo, i64* %keyword_table, align 8, !dbg !24 - %49 = getelementptr inbounds i8, i8* %40, i64 40, !dbg !24 - %50 = bitcast i8* %49 to i32*, !dbg !24 - store i32 1, i32* %50, align 8, !dbg !24, !tbaa !36 - %51 = getelementptr inbounds i8, i8* %40, i64 44, !dbg !24 - %52 = bitcast i8* %51 to i32*, !dbg !24 - store i32 1, i32* %52, align 4, !dbg !24, !tbaa !37 - %53 = getelementptr inbounds i8, i8* %40, i64 48, !dbg !24 - %54 = bitcast i8* %53 to i32*, !dbg !24 - store i32 1, i32* %54, align 8, !dbg !24, !tbaa !38 - %55 = tail call noalias nonnull i8* @ruby_xmalloc2(i64 noundef 1, i64 noundef 8) #17, !dbg !24 - %56 = bitcast i64* %keyword_table to i8*, !dbg !24 - call void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture nonnull writeonly align 1 %55, i8* nocapture noundef nonnull readonly align 8 dereferenceable(8) %56, i64 noundef 8, i1 noundef false) #16, !dbg !24 - %57 = getelementptr inbounds i8, i8* %40, i64 56, !dbg !24 - %58 = bitcast i8* %57 to i8**, !dbg !24 - store i8* %55, i8** %58, align 8, !dbg !24, !tbaa !39 - tail call void @sorbet_vm_define_method(i64 %37, i8* noundef getelementptr inbounds ([11 x i8], [11 x i8]* @str_initialize, i64 0, i64 0), i64 (i32, i64*, i64, %struct.rb_control_frame_struct*, i8*)* noundef @"func_MyStruct#10initialize", i8* nonnull %40, %struct.rb_iseq_struct* %stackFrame60, i1 noundef zeroext false) #16, !dbg !24 - %59 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !24, !tbaa !14 - %60 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %59, i64 0, i32 5, !dbg !24 - %61 = load i32, i32* %60, align 8, !dbg !24, !tbaa !25 - %62 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %59, i64 0, i32 6, !dbg !24 - %63 = load i32, i32* %62, align 4, !dbg !24, !tbaa !26 - %64 = xor i32 %63, -1, !dbg !24 - %65 = and i32 %64, %61, !dbg !24 - %66 = icmp eq i32 %65, 0, !dbg !24 - br i1 %66, label %rb_vm_check_ints.exit1, label %67, !dbg !24, !prof !27 - -67: ; preds = %36 - %68 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %59, i64 0, i32 8, !dbg !24 - %69 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %68, align 8, !dbg !24, !tbaa !28 - %70 = tail call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %69, i32 noundef 0) #16, !dbg !24 - br label %rb_vm_check_ints.exit1, !dbg !24 - -rb_vm_check_ints.exit1: ; preds = %36, %67 - store i64* getelementptr inbounds ([21 x i64], [21 x i64]* @iseqEncodedArray, i64 0, i64 6), i64** %8, align 8, !dbg !24, !tbaa !14 - %rubyId_foo62 = load i64, i64* @rubyIdPrecomputed_foo, align 8, !dbg !40 - %rawSym63 = tail call i64 @rb_id2sym(i64 %rubyId_foo62), !dbg !40 - %71 = load i64, i64* @rb_cInteger, align 8, !dbg !29 - %72 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 1, !dbg !29 - %73 = load i64*, i64** %72, align 8, !dbg !29 - store i64 %selfRaw, i64* %73, align 8, !dbg !29, !tbaa !6 - %74 = getelementptr inbounds i64, i64* %73, i64 1, !dbg !29 - store i64 %rawSym63, i64* %74, align 8, !dbg !29, !tbaa !6 - %75 = getelementptr inbounds i64, i64* %74, i64 1, !dbg !29 - store i64 %71, i64* %75, align 8, !dbg !29, !tbaa !6 - %76 = getelementptr inbounds i64, i64* %75, i64 1, !dbg !29 - store i64 20, i64* %76, align 8, !dbg !29, !tbaa !6 - %77 = getelementptr inbounds i64, i64* %76, i64 1, !dbg !29 - store i64* %77, i64** %72, align 8, !dbg !29 - %send = tail call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_const, i64 0), !dbg !29 - %78 = tail call i64 @rb_intern(i8* noundef getelementptr inbounds ([4 x i8], [4 x i8]* @str_foo, i64 0, i64 0)) #16, !dbg !29 - %79 = tail call i64 @rb_id2str(i64 %78) #16, !dbg !29 - %80 = tail call i64 (i8*, ...) @rb_sprintf(i8* noundef getelementptr inbounds ([6 x i8], [6 x i8]* @.str, i64 0, i64 0), i64 %79) #16, !dbg !29 - %81 = tail call i64 @rb_intern_str(i64 %80) #16, !dbg !29 - %82 = inttoptr i64 %81 to i8*, !dbg !29 - tail call void @rb_add_method(i64 %37, i64 %78, i32 noundef 4, i8* %82, i32 noundef 1) #16, !dbg !29 - %83 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !29, !tbaa !14 - %84 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %83, i64 0, i32 5, !dbg !29 - %85 = load i32, i32* %84, align 8, !dbg !29, !tbaa !25 - %86 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %83, i64 0, i32 6, !dbg !29 - %87 = load i32, i32* %86, align 4, !dbg !29, !tbaa !26 - %88 = xor i32 %87, -1, !dbg !29 - %89 = and i32 %88, %85, !dbg !29 - %90 = icmp eq i32 %89, 0, !dbg !29 - br i1 %90, label %rb_vm_check_ints.exit, label %91, !dbg !29, !prof !27 - -91: ; preds = %rb_vm_check_ints.exit1 - %92 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %83, i64 0, i32 8, !dbg !29 - %93 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %92, align 8, !dbg !29, !tbaa !28 - %94 = tail call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %93, i32 noundef 0) #16, !dbg !29 - br label %rb_vm_check_ints.exit, !dbg !29 - -rb_vm_check_ints.exit: ; preds = %rb_vm_check_ints.exit1, %91 - ret void -} - -; Function Attrs: sspreq -define void @Init_prop_const_getter() local_unnamed_addr #9 { -entry: - %locals.i23.i = alloca i64, align 8 - %locals.i21.i = alloca i64, i32 0, align 8 - %locals.i.i = alloca i64, i32 0, align 8 - %keywords.i = alloca i64, align 8, !dbg !41 - %keywords10.i = alloca i64, align 8, !dbg !43 - %keywords16.i = alloca i64, align 8, !dbg !29 - %realpath = tail call i64 @sorbet_readRealpath() - %0 = bitcast i64* %keywords.i to i8* - call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %0) - %1 = bitcast i64* %keywords10.i to i8* - call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %1) - %2 = bitcast i64* %keywords16.i to i8* - call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %2) - %3 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([17 x i8], [17 x i8]* @"str_", i64 0, i64 0), i64 noundef 16) #16 - store i64 %3, i64* @"rubyIdPrecomputed_", align 8 - %4 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([4 x i8], [4 x i8]* @str_foo, i64 0, i64 0), i64 noundef 3) #16 - store i64 %4, i64* @rubyIdPrecomputed_foo, align 8 - %5 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([4 x i8], [4 x i8]* @str_new, i64 0, i64 0), i64 noundef 3) #16 - store i64 %5, i64* @rubyIdPrecomputed_new, align 8 - %6 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([2 x i8], [2 x i8]* @"str_<", i64 0, i64 0), i64 noundef 1) #16 - store i64 %6, i64* @"rubyIdPrecomputed_<", align 8 - %7 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([2 x i8], [2 x i8]* @"str_+", i64 0, i64 0), i64 noundef 1) #16 - store i64 %7, i64* @"rubyIdPrecomputed_+", align 8 - %8 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([5 x i8], [5 x i8]* @str_puts, i64 0, i64 0), i64 noundef 4) #16 - store i64 %8, i64* @rubyIdPrecomputed_puts, align 8 - %9 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([11 x i8], [11 x i8]* @str_initialize, i64 0, i64 0), i64 noundef 10) #16 - store i64 %9, i64* @rubyIdPrecomputed_initialize, align 8 - %10 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([5 x i8], [5 x i8]* @"str_@foo", i64 0, i64 0), i64 noundef 4) #16 - store i64 %10, i64* @"rubyIdPrecomputed_@foo", align 8 - %11 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([8 x i8], [8 x i8]* @"str_", i64 0, i64 0), i64 noundef 7) #16 - %12 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([17 x i8], [17 x i8]* @"str_", i64 0, i64 0), i64 noundef 16) #16 - store i64 %12, i64* @"rubyIdPrecomputed_", align 8 - %13 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([13 x i8], [13 x i8]* @"str_", i64 0, i64 0), i64 noundef 12) #16 - store i64 %13, i64* @"rubyIdPrecomputed_", align 8 - %14 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([26 x i8], [26 x i8]* @"str_block in ", i64 0, i64 0), i64 noundef 25) #16 - store i64 %14, i64* @"rubyIdPrecomputed_block in ", align 8 - %15 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([7 x i8], [7 x i8]* @str_params, i64 0, i64 0), i64 noundef 6) #16 - store i64 %15, i64* @rubyIdPrecomputed_params, align 8 - %16 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([5 x i8], [5 x i8]* @str_void, i64 0, i64 0), i64 noundef 4) #16 - store i64 %16, i64* @rubyIdPrecomputed_void, align 8 - %17 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([8 x i8], [8 x i8]* @str_returns, i64 0, i64 0), i64 noundef 7) #16 - store i64 %17, i64* @rubyIdPrecomputed_returns, align 8 - %18 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([7 x i8], [7 x i8]* @str_normal, i64 0, i64 0), i64 noundef 6) #16 - %19 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([18 x i8], [18 x i8]* @str_without_accessors, i64 0, i64 0), i64 noundef 17) #16 - store i64 %19, i64* @rubyIdPrecomputed_without_accessors, align 8 - %20 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([6 x i8], [6 x i8]* @str_const, i64 0, i64 0), i64 noundef 5) #16 - store i64 %20, i64* @rubyIdPrecomputed_const, align 8 - %21 = tail call i64 @rb_intern2(i8* noundef getelementptr inbounds ([12 x i8], [12 x i8]* @str_attr_reader, i64 0, i64 0), i64 noundef 11) #16 - %22 = tail call i64 @sorbet_vm_fstring_new(i8* noundef getelementptr inbounds ([17 x i8], [17 x i8]* @"str_", i64 0, i64 0), i64 noundef 16) #16 - tail call void @rb_gc_register_mark_object(i64 %22) #16 - store i64 %22, i64* @"rubyStrFrozen_", align 8 - %23 = tail call i64 @sorbet_vm_fstring_new(i8* noundef getelementptr inbounds ([57 x i8], [57 x i8]* @"str_test/testdata/ruby_benchmark/stripe/prop_const_getter.rb", i64 0, i64 0), i64 noundef 56) #16 - tail call void @rb_gc_register_mark_object(i64 %23) #16 - store i64 %23, i64* @"rubyStrFrozen_test/testdata/ruby_benchmark/stripe/prop_const_getter.rb", align 8 - tail call void @sorbet_initLineNumberInfo(%struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef getelementptr inbounds ([21 x i64], [21 x i64]* @iseqEncodedArray, i32 0, i32 0), i32 noundef 21) - %"rubyId_.i.i" = load i64, i64* @"rubyIdPrecomputed_", align 8 - %"rubyStr_.i.i" = load i64, i64* @"rubyStrFrozen_", align 8 - %"rubyStr_test/testdata/ruby_benchmark/stripe/prop_const_getter.rb.i.i" = load i64, i64* @"rubyStrFrozen_test/testdata/ruby_benchmark/stripe/prop_const_getter.rb", align 8 - %24 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_.i.i", i64 %"rubyId_.i.i", i64 %"rubyStr_test/testdata/ruby_benchmark/stripe/prop_const_getter.rb.i.i", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 0, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals.i.i, i32 noundef 0, i32 noundef 2) - store %struct.rb_iseq_struct* %24, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.17$152", align 8 - %rubyId_new.i = load i64, i64* @rubyIdPrecomputed_new, align 8, !dbg !41 - %rubyId_foo.i = load i64, i64* @rubyIdPrecomputed_foo, align 8, !dbg !41 - %25 = call i64 @rb_id2sym(i64 %rubyId_foo.i) #18, !dbg !41 - store i64 %25, i64* %keywords.i, align 8, !dbg !41 - call void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_new, i64 %rubyId_new.i, i32 noundef 64, i32 noundef 1, i32 noundef 1, i64* noundef nonnull align 8 %keywords.i), !dbg !41 - %"rubyId_<.i" = load i64, i64* @"rubyIdPrecomputed_<", align 8, !dbg !45 - call void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @"ic_<", i64 %"rubyId_<.i", i32 noundef 16, i32 noundef 1, i32 noundef 0, i64* noundef null), !dbg !45 - %rubyId_foo1.i = load i64, i64* @rubyIdPrecomputed_foo, align 8, !dbg !46 - call void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_foo, i64 %rubyId_foo1.i, i32 noundef 16, i32 noundef 0, i32 noundef 0, i64* noundef null), !dbg !46 - %"rubyId_+.i" = load i64, i64* @"rubyIdPrecomputed_+", align 8, !dbg !47 - call void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @"ic_+", i64 %"rubyId_+.i", i32 noundef 16, i32 noundef 1, i32 noundef 0, i64* noundef null), !dbg !47 - %rubyId_puts.i = load i64, i64* @rubyIdPrecomputed_puts, align 8, !dbg !48 - call void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts, i64 %rubyId_puts.i, i32 noundef 20, i32 noundef 1, i32 noundef 0, i64* noundef null), !dbg !48 - %rubyId_foo5.i = load i64, i64* @rubyIdPrecomputed_foo, align 8, !dbg !49 - call void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_foo.1, i64 %rubyId_foo5.i, i32 noundef 16, i32 noundef 0, i32 noundef 0, i64* noundef null), !dbg !49 - %rubyId_puts7.i = load i64, i64* @rubyIdPrecomputed_puts, align 8, !dbg !50 - call void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_puts.2, i64 %rubyId_puts7.i, i32 noundef 20, i32 noundef 1, i32 noundef 0, i64* noundef null), !dbg !50 - %26 = call i64 @sorbet_vm_fstring_new(i8* noundef getelementptr inbounds ([11 x i8], [11 x i8]* @str_initialize, i64 0, i64 0), i64 noundef 10) #16 - call void @rb_gc_register_mark_object(i64 %26) #16 - %rubyId_initialize.i.i = load i64, i64* @rubyIdPrecomputed_initialize, align 8 - %"rubyStr_test/testdata/ruby_benchmark/stripe/prop_const_getter.rb.i20.i" = load i64, i64* @"rubyStrFrozen_test/testdata/ruby_benchmark/stripe/prop_const_getter.rb", align 8 - %27 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %26, i64 %rubyId_initialize.i.i, i64 %"rubyStr_test/testdata/ruby_benchmark/stripe/prop_const_getter.rb.i20.i", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 1, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull %locals.i21.i, i32 noundef 0, i32 noundef 5) - store %struct.rb_iseq_struct* %27, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_MyStruct#10initialize", align 8 - %28 = call i64 @sorbet_getConstant(i8* noundef getelementptr inbounds ([30 x i8], [30 x i8]* @sorbet_getVoidSingleton.name, i64 0, i64 0), i64 noundef 30) #16 - store i64 %28, i64* @"", align 8 - %29 = call i64 @sorbet_vm_fstring_new(i8* noundef getelementptr inbounds ([17 x i8], [17 x i8]* @"str_", i64 0, i64 0), i64 noundef 16) #16 - call void @rb_gc_register_mark_object(i64 %29) #16 - %30 = bitcast i64* %locals.i23.i to i8* - call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %30) - %"rubyId_.i.i" = load i64, i64* @"rubyIdPrecomputed_", align 8 - %"rubyStr_test/testdata/ruby_benchmark/stripe/prop_const_getter.rb.i22.i" = load i64, i64* @"rubyStrFrozen_test/testdata/ruby_benchmark/stripe/prop_const_getter.rb", align 8 - %"rubyId_.i.i" = load i64, i64* @"rubyIdPrecomputed_", align 8 - store i64 %"rubyId_.i.i", i64* %locals.i23.i, align 8 - %31 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %29, i64 %"rubyId_.i.i", i64 %"rubyStr_test/testdata/ruby_benchmark/stripe/prop_const_getter.rb.i22.i", i64 %realpath, %struct.rb_iseq_struct* noundef null, i32 noundef 3, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef nonnull align 8 %locals.i23.i, i32 noundef 1, i32 noundef 4) - store %struct.rb_iseq_struct* %31, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_MyStruct.13", align 8 - call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %30) - %32 = call i64 @sorbet_vm_fstring_new(i8* noundef getelementptr inbounds ([26 x i8], [26 x i8]* @"str_block in ", i64 0, i64 0), i64 noundef 25) #16 - call void @rb_gc_register_mark_object(i64 %32) #16 - store i64 %32, i64* @"rubyStrFrozen_block in ", align 8 - %stackFrame.i.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_MyStruct.13", align 8 - %"rubyId_block in .i.i" = load i64, i64* @"rubyIdPrecomputed_block in ", align 8 - %"rubyStr_test/testdata/ruby_benchmark/stripe/prop_const_getter.rb.i24.i" = load i64, i64* @"rubyStrFrozen_test/testdata/ruby_benchmark/stripe/prop_const_getter.rb", align 8 - %33 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %32, i64 %"rubyId_block in .i.i", i64 %"rubyStr_test/testdata/ruby_benchmark/stripe/prop_const_getter.rb.i24.i", i64 %realpath, %struct.rb_iseq_struct* %stackFrame.i.i, i32 noundef 2, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef null, i32 noundef 0, i32 noundef 4) - store %struct.rb_iseq_struct* %33, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_MyStruct.13$block_1", align 8 - %stackFrame.i25.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_MyStruct.13", align 8 - %"rubyId_block in .i26.i" = load i64, i64* @"rubyIdPrecomputed_block in ", align 8 - %"rubyStr_block in .i27.i" = load i64, i64* @"rubyStrFrozen_block in ", align 8 - %"rubyStr_test/testdata/ruby_benchmark/stripe/prop_const_getter.rb.i28.i" = load i64, i64* @"rubyStrFrozen_test/testdata/ruby_benchmark/stripe/prop_const_getter.rb", align 8 - %34 = call %struct.rb_iseq_struct* @sorbet_allocateRubyStackFrame(i64 %"rubyStr_block in .i27.i", i64 %"rubyId_block in .i26.i", i64 %"rubyStr_test/testdata/ruby_benchmark/stripe/prop_const_getter.rb.i28.i", i64 %realpath, %struct.rb_iseq_struct* %stackFrame.i25.i, i32 noundef 2, i32 noundef 5, %struct.SorbetLineNumberInfo* noundef @fileLineNumberInfo, i64* noundef null, i32 noundef 0, i32 noundef 4) - store %struct.rb_iseq_struct* %34, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_MyStruct.13$block_2", align 8 - %rubyId_params.i = load i64, i64* @rubyIdPrecomputed_params, align 8, !dbg !43 - %rubyId_foo11.i = load i64, i64* @rubyIdPrecomputed_foo, align 8, !dbg !43 - %35 = call i64 @rb_id2sym(i64 %rubyId_foo11.i) #18, !dbg !43 - store i64 %35, i64* %keywords10.i, align 8, !dbg !43 - call void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_params, i64 %rubyId_params.i, i32 noundef 64, i32 noundef 1, i32 noundef 1, i64* noundef nonnull align 8 %keywords10.i), !dbg !43 - %rubyId_void.i = load i64, i64* @rubyIdPrecomputed_void, align 8, !dbg !43 - call void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_void, i64 %rubyId_void.i, i32 noundef 16, i32 noundef 0, i32 noundef 0, i64* noundef null), !dbg !43 - %rubyId_returns.i = load i64, i64* @rubyIdPrecomputed_returns, align 8, !dbg !51 - call void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_returns, i64 %rubyId_returns.i, i32 noundef 16, i32 noundef 1, i32 noundef 0, i64* noundef null), !dbg !51 - %rubyId_const.i = load i64, i64* @rubyIdPrecomputed_const, align 8, !dbg !29 - %rubyId_without_accessors.i = load i64, i64* @rubyIdPrecomputed_without_accessors, align 8, !dbg !29 - %36 = call i64 @rb_id2sym(i64 %rubyId_without_accessors.i) #18, !dbg !29 - store i64 %36, i64* %keywords16.i, align 8, !dbg !29 - call void @sorbet_setupFunctionInlineCache(%struct.FunctionInlineCache* noundef @ic_const, i64 %rubyId_const.i, i32 noundef 68, i32 noundef 3, i32 noundef 1, i64* noundef nonnull align 8 %keywords16.i), !dbg !29 - call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %0) - call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %1) - call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %2) - %37 = load %struct.rb_vm_struct*, %struct.rb_vm_struct** @ruby_current_vm_ptr, align 8, !tbaa !14 - %38 = getelementptr inbounds %struct.rb_vm_struct, %struct.rb_vm_struct* %37, i64 0, i32 18 - %39 = load i64, i64* %38, align 8, !tbaa !53 - %40 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !tbaa !14 - %41 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %40, i64 0, i32 2 - %42 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %41, align 8, !tbaa !16 - %stackFrame.i = load %struct.rb_iseq_struct*, %struct.rb_iseq_struct** @"stackFramePrecomputed_func_.17$152", align 8 - %43 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %42, i64 0, i32 2 - store %struct.rb_iseq_struct* %stackFrame.i, %struct.rb_iseq_struct** %43, align 8, !tbaa !20 - %44 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %42, i64 0, i32 4 - %45 = load i64*, i64** %44, align 8, !tbaa !22 - %46 = load i64, i64* %45, align 8, !tbaa !6 - %47 = and i64 %46, -33 - store i64 %47, i64* %45, align 8, !tbaa !6 - call void @sorbet_setMethodStackFrame(%struct.rb_execution_context_struct* %40, %struct.rb_control_frame_struct* %42, %struct.rb_iseq_struct* %stackFrame.i) #16 - %48 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %42, i64 0, i32 0 - store i64* getelementptr inbounds ([21 x i64], [21 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %48, align 8, !dbg !61, !tbaa !14 - %49 = load i64, i64* @"guard_epoch_T::Struct", align 8, !dbg !62 - %50 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !62, !tbaa !30 - %needTakeSlowPath = icmp ne i64 %49, %50, !dbg !62 - br i1 %needTakeSlowPath, label %51, label %52, !dbg !62, !prof !32 - -51: ; preds = %entry - call void @"const_recompute_T::Struct"(), !dbg !62 - br label %52, !dbg !62 - -52: ; preds = %entry, %51 - %53 = load i64, i64* @"guarded_const_T::Struct", align 8, !dbg !62 - %54 = load i64, i64* @"guard_epoch_T::Struct", align 8, !dbg !62 - %55 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !62, !tbaa !30 - %guardUpdated = icmp eq i64 %54, %55, !dbg !62 - call void @llvm.assume(i1 %guardUpdated), !dbg !62 - %56 = call i64 @rb_define_class(i8* noundef getelementptr inbounds ([9 x i8], [9 x i8]* @str_MyStruct, i64 0, i64 0), i64 %53) #16, !dbg !62 - %57 = call %struct.rb_control_frame_struct* @sorbet_pushStaticInitFrame(i64 %56) #16, !dbg !62 - call fastcc void @"func_MyStruct.13L62"(i64 %56, %struct.rb_control_frame_struct* %57) #16, !dbg !62 - call void @sorbet_popFrame() #16, !dbg !62 - store i64* getelementptr inbounds ([21 x i64], [21 x i64]* @iseqEncodedArray, i64 0, i64 9), i64** %48, align 8, !dbg !63, !tbaa !14 - %58 = load i64, i64* @guard_epoch_MyStruct, align 8, !dbg !41 - %59 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !41, !tbaa !30 - %needTakeSlowPath1 = icmp ne i64 %58, %59, !dbg !41 - br i1 %needTakeSlowPath1, label %60, label %61, !dbg !41, !prof !32 - -60: ; preds = %52 - call void @const_recompute_MyStruct(), !dbg !41 - br label %61, !dbg !41 - -61: ; preds = %52, %60 - %62 = load i64, i64* @guarded_const_MyStruct, align 8, !dbg !41 - %63 = load i64, i64* @guard_epoch_MyStruct, align 8, !dbg !41 - %64 = load i64, i64* @ruby_vm_global_constant_state, align 8, !dbg !41, !tbaa !30 - %guardUpdated2 = icmp eq i64 %63, %64, !dbg !41 - call void @llvm.assume(i1 %guardUpdated2), !dbg !41 - %65 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %42, i64 0, i32 1, !dbg !41 - %66 = load i64*, i64** %65, align 8, !dbg !41 - store i64 %62, i64* %66, align 8, !dbg !41, !tbaa !6 - %67 = getelementptr inbounds i64, i64* %66, i64 1, !dbg !41 - store i64 861, i64* %67, align 8, !dbg !41, !tbaa !6 - %68 = getelementptr inbounds i64, i64* %67, i64 1, !dbg !41 - store i64* %68, i64** %65, align 8, !dbg !41 - %send = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_new, i64 0), !dbg !41 - br label %BB2.i, !dbg !64 - -BB2.i: ; preds = %BB2.i.backedge, %61 - %i.sroa.0.0.i = phi i64 [ 1, %61 ], [ %i.sroa.0.0.i.be, %BB2.i.backedge ], !dbg !61 - store i64* getelementptr inbounds ([21 x i64], [21 x i64]* @iseqEncodedArray, i64 0, i64 12), i64** %48, align 8, !tbaa !14 - %69 = and i64 %i.sroa.0.0.i, 1, !dbg !45 - %70 = icmp eq i64 %69, 0, !dbg !45 - br i1 %70, label %71, label %"fastSymCallIntrinsic_Integer_<.i", !dbg !45, !prof !65 - -71: ; preds = %BB2.i - %72 = and i64 %i.sroa.0.0.i, 7, !dbg !45 - %73 = icmp ne i64 %72, 0, !dbg !45 - %74 = and i64 %i.sroa.0.0.i, -9, !dbg !45 - %75 = icmp eq i64 %74, 0, !dbg !45 - %76 = or i1 %73, %75, !dbg !45 - br i1 %76, label %"alternativeCallIntrinsic_Integer_<.i", label %sorbet_isa_Integer.exit, !dbg !45, !prof !66 - -sorbet_isa_Integer.exit: ; preds = %71 - %77 = inttoptr i64 %i.sroa.0.0.i to %struct.iseq_inline_iv_cache_entry*, !dbg !45 - %78 = getelementptr inbounds %struct.iseq_inline_iv_cache_entry, %struct.iseq_inline_iv_cache_entry* %77, i64 0, i32 0, !dbg !45 - %79 = load i64, i64* %78, align 8, !dbg !45, !tbaa !67 - %80 = and i64 %79, 31, !dbg !45 - %81 = icmp eq i64 %80, 10, !dbg !45 - br i1 %81, label %"fastSymCallIntrinsic_Integer_<.i", label %"alternativeCallIntrinsic_Integer_<.i", !dbg !45, !prof !27 - -BB5.i: ; preds = %afterSend40.i - store i64* getelementptr inbounds ([21 x i64], [21 x i64]* @iseqEncodedArray, i64 0, i64 14), i64** %48, align 8, !tbaa !14 - %82 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %42, i64 0, i32 1, !dbg !46 - %83 = load i64*, i64** %82, align 8, !dbg !46 - store i64 %send, i64* %83, align 8, !dbg !46, !tbaa !6 - %84 = getelementptr inbounds i64, i64* %83, i64 1, !dbg !46 - store i64* %84, i64** %82, align 8, !dbg !46 - %send4 = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_foo, i64 0), !dbg !46 - store i64* getelementptr inbounds ([21 x i64], [21 x i64]* @iseqEncodedArray, i64 0, i64 16), i64** %48, align 8, !dbg !46, !tbaa !14 - br i1 %85, label %"fastSymCallIntrinsic_Integer_+.i", label %"alternativeCallIntrinsic_Integer_+.i", !dbg !47 - -afterSend40.i: ; preds = %110, %sorbet_rb_int_lt.exit.i, %"alternativeCallIntrinsic_Integer_<.i" - %85 = phi i1 [ %88, %"alternativeCallIntrinsic_Integer_<.i" ], [ %93, %sorbet_rb_int_lt.exit.i ], [ %93, %110 ] - %"symIntrinsicRawPhi_<.i" = phi i64 [ %send6, %"alternativeCallIntrinsic_Integer_<.i" ], [ %rawSendResult92.i, %sorbet_rb_int_lt.exit.i ], [ %rawSendResult92.i, %110 ], !dbg !45 - %86 = and i64 %"symIntrinsicRawPhi_<.i", -9, !dbg !45 - %87 = icmp ne i64 %86, 0, !dbg !45 - br i1 %87, label %BB5.i, label %"func_.17$152.exit", !dbg !45 - -"alternativeCallIntrinsic_Integer_<.i": ; preds = %71, %sorbet_isa_Integer.exit - %88 = phi i1 [ %81, %sorbet_isa_Integer.exit ], [ false, %71 ] - %89 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %42, i64 0, i32 1, !dbg !45 - %90 = load i64*, i64** %89, align 8, !dbg !45 - store i64 %i.sroa.0.0.i, i64* %90, align 8, !dbg !45, !tbaa !6 - %91 = getelementptr inbounds i64, i64* %90, i64 1, !dbg !45 - store i64 20000001, i64* %91, align 8, !dbg !45, !tbaa !6 - %92 = getelementptr inbounds i64, i64* %91, i64 1, !dbg !45 - store i64* %92, i64** %89, align 8, !dbg !45 - %send6 = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @"ic_<", i64 0), !dbg !45 - br label %afterSend40.i, !dbg !45 - -"fastSymCallIntrinsic_Integer_<.i": ; preds = %BB2.i, %sorbet_isa_Integer.exit - %93 = phi i1 [ %81, %sorbet_isa_Integer.exit ], [ true, %BB2.i ] - call void @llvm.experimental.noalias.scope.decl(metadata !69) #16, !dbg !45 - %94 = and i64 %i.sroa.0.0.i, 1, !dbg !45 - %95 = icmp eq i64 %94, 0, !dbg !45 - br i1 %95, label %100, label %96, !dbg !45, !prof !72 - -96: ; preds = %"fastSymCallIntrinsic_Integer_<.i" - %97 = ashr i64 %i.sroa.0.0.i, 1, !dbg !45 - %98 = icmp slt i64 %97, 10000000, !dbg !45 - %99 = select i1 %98, i64 20, i64 0, !dbg !45 - br label %sorbet_rb_int_lt.exit.i, !dbg !45 - -100: ; preds = %"fastSymCallIntrinsic_Integer_<.i" - %101 = call i64 @sorbet_rb_int_lt_slowpath(i64 %i.sroa.0.0.i, i64 noundef 20000001) #16, !dbg !45, !noalias !69 - br label %sorbet_rb_int_lt.exit.i, !dbg !45 - -sorbet_rb_int_lt.exit.i: ; preds = %100, %96 - %rawSendResult92.i = phi i64 [ %99, %96 ], [ %101, %100 ] - %102 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !45, !tbaa !14 - %103 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %102, i64 0, i32 5, !dbg !45 - %104 = load i32, i32* %103, align 8, !dbg !45, !tbaa !25 - %105 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %102, i64 0, i32 6, !dbg !45 - %106 = load i32, i32* %105, align 4, !dbg !45, !tbaa !26 - %107 = xor i32 %106, -1, !dbg !45 - %108 = and i32 %107, %104, !dbg !45 - %109 = icmp eq i32 %108, 0, !dbg !45 - br i1 %109, label %afterSend40.i, label %110, !dbg !45, !prof !27 - -110: ; preds = %sorbet_rb_int_lt.exit.i - %111 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %102, i64 0, i32 8, !dbg !45 - %112 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %111, align 8, !dbg !45, !tbaa !28 - %113 = call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %112, i32 noundef 0) #16, !dbg !45 - br label %afterSend40.i, !dbg !45 - -"alternativeCallIntrinsic_Integer_+.i": ; preds = %BB5.i - %114 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %42, i64 0, i32 1, !dbg !47 - %115 = load i64*, i64** %114, align 8, !dbg !47 - store i64 %i.sroa.0.0.i, i64* %115, align 8, !dbg !47, !tbaa !6 - %116 = getelementptr inbounds i64, i64* %115, i64 1, !dbg !47 - store i64 3, i64* %116, align 8, !dbg !47, !tbaa !6 - %117 = getelementptr inbounds i64, i64* %116, i64 1, !dbg !47 - store i64* %117, i64** %114, align 8, !dbg !47 - %send8 = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @"ic_+", i64 0), !dbg !47 - br label %BB2.i.backedge, !dbg !47 - -"fastSymCallIntrinsic_Integer_+.i": ; preds = %BB5.i - call void @llvm.experimental.noalias.scope.decl(metadata !73) #16, !dbg !47 - %118 = and i64 %i.sroa.0.0.i, 1, !dbg !47 - %119 = icmp eq i64 %118, 0, !dbg !47 - br i1 %119, label %128, label %120, !dbg !47, !prof !72 - -120: ; preds = %"fastSymCallIntrinsic_Integer_+.i" - %121 = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %i.sroa.0.0.i, i64 noundef 2) #19, !dbg !47 - %122 = extractvalue { i64, i1 } %121, 1, !dbg !47 - %123 = extractvalue { i64, i1 } %121, 0, !dbg !47 - br i1 %122, label %124, label %sorbet_rb_int_plus.exit.i, !dbg !47 - -124: ; preds = %120 - %125 = ashr i64 %123, 1, !dbg !47 - %126 = xor i64 %125, -9223372036854775808, !dbg !47 - %127 = call i64 @rb_int2big(i64 %126) #16, !dbg !47 - br label %sorbet_rb_int_plus.exit.i, !dbg !47 - -128: ; preds = %"fastSymCallIntrinsic_Integer_+.i" - %129 = call i64 @sorbet_rb_int_plus_slowpath(i64 %i.sroa.0.0.i, i64 noundef 3) #16, !dbg !47, !noalias !73 - br label %sorbet_rb_int_plus.exit.i, !dbg !47 - -sorbet_rb_int_plus.exit.i: ; preds = %128, %124, %120 - %130 = phi i64 [ %129, %128 ], [ %127, %124 ], [ %123, %120 ], !dbg !47 - %131 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !47, !tbaa !14 - %132 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %131, i64 0, i32 5, !dbg !47 - %133 = load i32, i32* %132, align 8, !dbg !47, !tbaa !25 - %134 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %131, i64 0, i32 6, !dbg !47 - %135 = load i32, i32* %134, align 4, !dbg !47, !tbaa !26 - %136 = xor i32 %135, -1, !dbg !47 - %137 = and i32 %136, %133, !dbg !47 - %138 = icmp eq i32 %137, 0, !dbg !47 - br i1 %138, label %BB2.i.backedge, label %139, !dbg !47, !prof !27 - -139: ; preds = %sorbet_rb_int_plus.exit.i - %140 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %131, i64 0, i32 8, !dbg !47 - %141 = load %struct.rb_thread_struct*, %struct.rb_thread_struct** %140, align 8, !dbg !47, !tbaa !28 - %142 = call i32 @rb_threadptr_execute_interrupts(%struct.rb_thread_struct* %141, i32 noundef 0) #16, !dbg !47 - br label %BB2.i.backedge, !dbg !47 - -BB2.i.backedge: ; preds = %139, %sorbet_rb_int_plus.exit.i, %"alternativeCallIntrinsic_Integer_+.i" - %i.sroa.0.0.i.be = phi i64 [ %send8, %"alternativeCallIntrinsic_Integer_+.i" ], [ %130, %sorbet_rb_int_plus.exit.i ], [ %130, %139 ] - br label %BB2.i - -"func_.17$152.exit": ; preds = %afterSend40.i - %i.sroa.0.0.i.lcssa = phi i64 [ %i.sroa.0.0.i, %afterSend40.i ], !dbg !61 - store i64* getelementptr inbounds ([21 x i64], [21 x i64]* @iseqEncodedArray, i64 0, i64 19), i64** %48, align 8, !tbaa !14 - %143 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %42, i64 0, i32 1, !dbg !48 - %144 = load i64*, i64** %143, align 8, !dbg !48 - store i64 %39, i64* %144, align 8, !dbg !48, !tbaa !6 - %145 = getelementptr inbounds i64, i64* %144, i64 1, !dbg !48 - store i64 %i.sroa.0.0.i.lcssa, i64* %145, align 8, !dbg !48, !tbaa !6 - %146 = getelementptr inbounds i64, i64* %145, i64 1, !dbg !48 - store i64* %146, i64** %143, align 8, !dbg !48 - %send10 = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts, i64 0), !dbg !48 - store i64* getelementptr inbounds ([21 x i64], [21 x i64]* @iseqEncodedArray, i64 0, i64 20), i64** %48, align 8, !dbg !48, !tbaa !14 - %147 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %42, i64 0, i32 1, !dbg !49 - %148 = load i64*, i64** %147, align 8, !dbg !49 - store i64 %send, i64* %148, align 8, !dbg !49, !tbaa !6 - %149 = getelementptr inbounds i64, i64* %148, i64 1, !dbg !49 - store i64* %149, i64** %147, align 8, !dbg !49 - %send12 = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_foo.1, i64 0), !dbg !49 - %150 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %42, i64 0, i32 1, !dbg !50 - %151 = load i64*, i64** %150, align 8, !dbg !50 - store i64 %39, i64* %151, align 8, !dbg !50, !tbaa !6 - %152 = getelementptr inbounds i64, i64* %151, i64 1, !dbg !50 - store i64 %send12, i64* %152, align 8, !dbg !50, !tbaa !6 - %153 = getelementptr inbounds i64, i64* %152, i64 1, !dbg !50 - store i64* %153, i64** %150, align 8, !dbg !50 - %send14 = call i64 @sorbet_callFuncWithCache(%struct.FunctionInlineCache* @ic_puts.2, i64 0), !dbg !50 - ret void -} - -; Function Attrs: nounwind sspreq uwtable -define i64 @"func_MyStruct#10initialize"(i32 %argc, i64* nocapture readonly %argArray, i64 %selfRaw, %struct.rb_control_frame_struct* nocapture nonnull writeonly align 8 dereferenceable(8) %cfp, i8* nocapture nofree readnone %0) #8 !dbg !76 { -functionEntryInitializers: - %callArgs = alloca [5 x i64], align 8 - %1 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %cfp, i64 0, i32 0 - store i64* getelementptr inbounds ([21 x i64], [21 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %1, align 8, !tbaa !14 - %hashAttemptReadGuard = icmp ult i32 0, %argc, !dbg !77 - br i1 %hashAttemptReadGuard, label %readKWHashArgCountSuccess, label %fillRequiredArgs, !dbg !77 - -readKWHashArgCountSuccess: ; preds = %functionEntryInitializers - %argsWithoutHashCount = sub nuw i32 %argc, 1, !dbg !77 - %2 = getelementptr i64, i64* %argArray, i32 %argsWithoutHashCount, !dbg !77 - %KWArgHash = load i64, i64* %2, align 8, !dbg !77 - %3 = and i64 %KWArgHash, 7, !dbg !77 - %4 = icmp ne i64 %3, 0, !dbg !77 - %5 = and i64 %KWArgHash, -9, !dbg !77 - %6 = icmp eq i64 %5, 0, !dbg !77 - %7 = or i1 %4, %6, !dbg !77 - br i1 %7, label %argCountFailBlock, label %sorbet_isa_Hash.exit, !dbg !77 - -sorbet_isa_Hash.exit: ; preds = %readKWHashArgCountSuccess - %8 = inttoptr i64 %KWArgHash to %struct.iseq_inline_iv_cache_entry*, !dbg !77 - %9 = getelementptr inbounds %struct.iseq_inline_iv_cache_entry, %struct.iseq_inline_iv_cache_entry* %8, i64 0, i32 0, !dbg !77 - %10 = load i64, i64* %9, align 8, !dbg !77, !tbaa !67 - %11 = and i64 %10, 31, !dbg !77 - %12 = icmp eq i64 %11, 8, !dbg !77 - br i1 %12, label %afterKWHash, label %argCountFailBlock - -afterKWHash: ; preds = %sorbet_isa_Hash.exit - %tooManyArgs = icmp ugt i32 %argsWithoutHashCount, 0, !dbg !77 - br i1 %tooManyArgs, label %argCountFailBlock, label %fillRequiredArgs, !dbg !77, !prof !65 - -argCountFailBlock: ; preds = %readKWHashArgCountSuccess, %sorbet_isa_Hash.exit, %afterKWHash - %argcPhi38 = phi i32 [ %argsWithoutHashCount, %afterKWHash ], [ %argc, %sorbet_isa_Hash.exit ], [ %argc, %readKWHashArgCountSuccess ] - tail call void @sorbet_raiseArity(i32 %argcPhi38, i32 noundef 0, i32 noundef 0) #20, !dbg !77 - unreachable, !dbg !77 - -fillRequiredArgs: ; preds = %functionEntryInitializers, %afterKWHash - %hashArgsPhi43 = phi i64 [ %KWArgHash, %afterKWHash ], [ 52, %functionEntryInitializers ] - %13 = tail call i32 @rb_block_given_p() #16, !dbg !77 - %14 = icmp eq i32 %13, 0, !dbg !77 - br i1 %14, label %sorbet_getMethodBlockAsProc.exit, label %15, !dbg !77 - -15: ; preds = %fillRequiredArgs - %16 = tail call i64 @rb_block_proc() #16, !dbg !77 - br label %sorbet_getMethodBlockAsProc.exit, !dbg !77 - -sorbet_getMethodBlockAsProc.exit: ; preds = %fillRequiredArgs, %15 - %17 = phi i64 [ %16, %15 ], [ 8, %fillRequiredArgs ], !dbg !77 - %rubyId_foo = load i64, i64* @rubyIdPrecomputed_foo, align 8, !dbg !77 - %rawSym = tail call i64 @rb_id2sym(i64 %rubyId_foo), !dbg !77 - %18 = icmp eq i64 %hashArgsPhi43, 52, !dbg !77 - br i1 %18, label %kwArgContinue, label %sorbet_getKWArg.exit, !dbg !77 - -sorbet_getKWArg.exit: ; preds = %sorbet_getMethodBlockAsProc.exit - %19 = tail call i64 @rb_hash_lookup2(i64 %hashArgsPhi43, i64 %rawSym, i64 noundef 52) #16, !dbg !77 - %20 = icmp eq i64 %19, 52, !dbg !77 - br i1 %20, label %kwArgContinue.thread57, label %sorbet_assertAllRequiredKWArgs.exit.thread, !dbg !77 - -kwArgContinue: ; preds = %sorbet_getMethodBlockAsProc.exit - %21 = tail call i64 @sorbet_addMissingKWArg(i64 noundef 52, i64 %rawSym), !dbg !77 - %22 = icmp eq i64 %21, 52, !dbg !77 - br i1 %22, label %sorbet_assertNoExtraKWArg.exit.thread, label %25, !dbg !77, !prof !27 - -sorbet_assertNoExtraKWArg.exit.thread: ; preds = %kwArgContinue - store i64* getelementptr inbounds ([21 x i64], [21 x i64]* @iseqEncodedArray, i64 0, i64 6), i64** %1, align 8, !dbg !78, !tbaa !14 - br label %34, !dbg !79 - -kwArgContinue.thread57: ; preds = %sorbet_getKWArg.exit - %23 = tail call i64 @sorbet_addMissingKWArg(i64 noundef 52, i64 %rawSym), !dbg !77 - %24 = icmp eq i64 %23, 52, !dbg !77 - br i1 %24, label %sorbet_assertAllRequiredKWArgs.exit.thread, label %25, !dbg !77, !prof !27 - -25: ; preds = %kwArgContinue.thread57, %kwArgContinue - %26 = phi i64 [ %23, %kwArgContinue.thread57 ], [ %21, %kwArgContinue ] - tail call void @sorbet_raiseMissingKeywords(i64 %26) #15, !dbg !77 - unreachable, !dbg !77 - -sorbet_assertAllRequiredKWArgs.exit.thread: ; preds = %kwArgContinue.thread57, %sorbet_getKWArg.exit - %foo.sroa.0.05154 = phi i64 [ %19, %sorbet_getKWArg.exit ], [ 8, %kwArgContinue.thread57 ] - %27 = tail call i64 @rb_hash_size_num(i64 %hashArgsPhi43) #16, !dbg !77 - %28 = trunc i64 %27 to i32, !dbg !77 - %29 = sub nsw i32 %28, 1, !dbg !77 - %30 = icmp eq i32 %29, 0, !dbg !77 - br i1 %30, label %sorbet_assertNoExtraKWArg.exit, label %31, !dbg !77, !prof !27 - -31: ; preds = %sorbet_assertAllRequiredKWArgs.exit.thread - tail call void @sorbet_raiseExtraKeywords(i64 %hashArgsPhi43) #15, !dbg !77 - unreachable, !dbg !77 - -sorbet_assertNoExtraKWArg.exit: ; preds = %sorbet_assertAllRequiredKWArgs.exit.thread - %foo.sroa.0.05155 = phi i64 [ %foo.sroa.0.05154, %sorbet_assertAllRequiredKWArgs.exit.thread ] - store i64* getelementptr inbounds ([21 x i64], [21 x i64]* @iseqEncodedArray, i64 0, i64 6), i64** %1, align 8, !dbg !78, !tbaa !14 - %32 = and i64 %foo.sroa.0.05155, 1, !dbg !79 - %33 = icmp eq i64 %32, 0, !dbg !79 - br i1 %33, label %34, label %typeTestSuccess16, !dbg !79, !prof !65 - -34: ; preds = %sorbet_assertNoExtraKWArg.exit.thread, %sorbet_assertNoExtraKWArg.exit - %foo.sroa.0.0515573 = phi i64 [ 8, %sorbet_assertNoExtraKWArg.exit.thread ], [ %foo.sroa.0.05155, %sorbet_assertNoExtraKWArg.exit ] - %35 = and i64 %foo.sroa.0.0515573, 7, !dbg !79 - %36 = icmp ne i64 %35, 0, !dbg !79 - %37 = and i64 %foo.sroa.0.0515573, -9, !dbg !79 - %38 = icmp eq i64 %37, 0, !dbg !79 - %39 = or i1 %36, %38, !dbg !79 - br i1 %39, label %codeRepl64, label %sorbet_isa_Integer.exit, !dbg !79 - -sorbet_isa_Integer.exit: ; preds = %34 - %40 = inttoptr i64 %foo.sroa.0.0515573 to %struct.iseq_inline_iv_cache_entry*, !dbg !79 - %41 = getelementptr inbounds %struct.iseq_inline_iv_cache_entry, %struct.iseq_inline_iv_cache_entry* %40, i64 0, i32 0, !dbg !79 - %42 = load i64, i64* %41, align 8, !dbg !79, !tbaa !67 - %43 = and i64 %42, 31, !dbg !79 - %44 = icmp eq i64 %43, 10, !dbg !79 - br i1 %44, label %typeTestSuccess16, label %codeRepl64, !dbg !79, !prof !27 - -codeRepl64: ; preds = %34, %sorbet_isa_Integer.exit - %foo.sroa.0.0515574 = phi i64 [ %foo.sroa.0.0515573, %34 ], [ %foo.sroa.0.0515573, %sorbet_isa_Integer.exit ] - tail call fastcc void @"func_MyStruct#10initialize.cold.2"(i64 %foo.sroa.0.0515574) #21, !dbg !79 - unreachable - -typeTestSuccess16: ; preds = %sorbet_assertNoExtraKWArg.exit, %sorbet_isa_Integer.exit - %foo.sroa.0.0515572 = phi i64 [ %foo.sroa.0.05155, %sorbet_assertNoExtraKWArg.exit ], [ %foo.sroa.0.0515573, %sorbet_isa_Integer.exit ] - %"rubyId_@foo" = load i64, i64* @"rubyIdPrecomputed_@foo", align 8, !dbg !80 - tail call void @sorbet_vm_setivar(i64 %selfRaw, i64 %"rubyId_@foo", i64 %foo.sroa.0.0515572, %struct.iseq_inline_iv_cache_entry* noundef @"ivc_@foo") #16, !dbg !80 - store i64* getelementptr inbounds ([21 x i64], [21 x i64]* @iseqEncodedArray, i64 0, i64 5), i64** %1, align 8, !dbg !80, !tbaa !14 - %rubyId_foo20 = load i64, i64* @rubyIdPrecomputed_foo, align 8, !dbg !77 - %rawSym21 = tail call i64 @rb_id2sym(i64 %rubyId_foo20), !dbg !77 - %passedBlockHandler = tail call i64 (...) @sorbet_getPassedBlockHandler(), !dbg !77 - %callArgs0Addr = getelementptr [5 x i64], [5 x i64]* %callArgs, i32 0, i64 0, !dbg !77 - store i64 %rawSym21, i64* %callArgs0Addr, align 8, !dbg !77 - %callArgs1Addr = getelementptr [5 x i64], [5 x i64]* %callArgs, i32 0, i64 1, !dbg !77 - store i64 %foo.sroa.0.0515572, i64* %callArgs1Addr, align 8, !dbg !77 - %45 = getelementptr [5 x i64], [5 x i64]* %callArgs, i64 0, i64 0, !dbg !77 - %46 = tail call i64 @rb_hash_new_with_size(i64 noundef 1) #16, !dbg !77 - call void @rb_hash_bulk_insert(i64 noundef 2, i64* noundef nonnull %45, i64 %46) #16, !dbg !77 - store i64 %46, i64* %callArgs0Addr, align 8, !dbg !77 - call void @llvm.experimental.noalias.scope.decl(metadata !81), !dbg !77 - %47 = load %struct.rb_execution_context_struct*, %struct.rb_execution_context_struct** @ruby_current_execution_context_ptr, align 8, !dbg !77, !tbaa !14, !noalias !81 - %48 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %47, i64 0, i32 2, !dbg !77 - %49 = load %struct.rb_control_frame_struct*, %struct.rb_control_frame_struct** %48, align 8, !dbg !77, !tbaa !16, !noalias !81 - %50 = getelementptr inbounds %struct.rb_control_frame_struct, %struct.rb_control_frame_struct* %49, i64 0, i32 3, !dbg !77 - %51 = load i64, i64* %50, align 8, !dbg !77, !tbaa !84, !noalias !81 - %52 = call %struct.rb_callable_method_entry_struct* @rb_vm_frame_method_entry(%struct.rb_control_frame_struct* %49) #16, !dbg !77, !noalias !81 - %53 = getelementptr inbounds %struct.rb_callable_method_entry_struct, %struct.rb_callable_method_entry_struct* %52, i64 0, i32 1, !dbg !77 - %54 = load i64, i64* %53, align 8, !dbg !77, !tbaa !85, !noalias !81 - %55 = inttoptr i64 %54 to %struct.RClass*, !dbg !77 - %56 = getelementptr inbounds %struct.RClass, %struct.RClass* %55, i64 0, i32 2, !dbg !77 - %57 = load %struct.rb_classext_struct*, %struct.rb_classext_struct** %56, align 8, !dbg !77, !tbaa !87, !noalias !81 - %58 = getelementptr inbounds %struct.rb_classext_struct, %struct.rb_classext_struct* %57, i64 0, i32 8, !dbg !77 - %59 = load i64, i64* %58, align 8, !dbg !77, !tbaa !89, !noalias !81 - %60 = inttoptr i64 %59 to %struct.RClass*, !dbg !77 - %61 = getelementptr inbounds %struct.RClass, %struct.RClass* %60, i64 0, i32 1, !dbg !77 - %62 = load i64, i64* %61, align 8, !dbg !77, !tbaa !91, !noalias !81 - %63 = getelementptr inbounds %struct.rb_callable_method_entry_struct, %struct.rb_callable_method_entry_struct* %52, i64 0, i32 2, !dbg !77 - %64 = load %struct.rb_method_definition_struct*, %struct.rb_method_definition_struct** %63, align 8, !dbg !77, !tbaa !92, !noalias !81 - %65 = getelementptr inbounds %struct.rb_method_definition_struct, %struct.rb_method_definition_struct* %64, i64 0, i32 2, !dbg !77 - %66 = load i64, i64* %65, align 8, !dbg !77, !tbaa !93, !noalias !81 - %67 = call %struct.rb_callable_method_entry_struct* @rb_callable_method_entry(i64 %62, i64 %66) #16, !dbg !77, !noalias !81 - %68 = icmp eq %struct.rb_callable_method_entry_struct* %67, null, !dbg !77 - br i1 %68, label %69, label %sorbet_callSuperBlockHandler.exit, !dbg !77 - -69: ; preds = %typeTestSuccess16 - %70 = load i64, i64* @rb_eRuntimeError, align 8, !dbg !77, !tbaa !6 - call void (i64, i8*, ...) @rb_raise(i64 %70, i8* noundef getelementptr inbounds ([42 x i8], [42 x i8]* @.str.7, i64 0, i64 0)) #15, !dbg !77 - unreachable, !dbg !77 - -sorbet_callSuperBlockHandler.exit: ; preds = %typeTestSuccess16 - %71 = getelementptr inbounds %struct.rb_execution_context_struct, %struct.rb_execution_context_struct* %47, i64 0, i32 17, !dbg !77 - store i64 %passedBlockHandler, i64* %71, align 8, !dbg !77, !tbaa !95, !noalias !81 - %72 = call i64 @rb_vm_call_kw(%struct.rb_execution_context_struct* nonnull %47, i64 %51, i64 %66, i32 noundef 1, i64* noundef nonnull %45, %struct.rb_callable_method_entry_struct* nonnull %67, i32 noundef 1) #16, !dbg !77 - %"" = load i64, i64* @"", align 8 - ret i64 %"" -} - -; Function Attrs: inaccessiblememonly nofree nosync nounwind willreturn -declare void @llvm.experimental.noalias.scope.decl(metadata) #10 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #6 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn -declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #6 - -; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly -declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #11 - -; Function Attrs: cold minsize noreturn nounwind sspreq uwtable -define internal fastcc void @"func_MyStruct#10initialize.cold.2"(i64 %foo.sroa.0.05155) unnamed_addr #12 !dbg !96 { -newFuncRoot: - tail call void @sorbet_cast_failure(i64 %foo.sroa.0.05155, i8* noundef getelementptr inbounds ([4 x i8], [4 x i8]* @str_sig, i64 0, i64 0), i8* noundef getelementptr inbounds ([8 x i8], [8 x i8]* @str_Integer, i64 0, i64 0)) #20, !dbg !98 - unreachable, !dbg !98 -} - -; Function Attrs: nofree nosync nounwind willreturn -declare void @llvm.assume(i1 noundef) #13 - -; Function Attrs: ssp -define linkonce void @const_recompute_MyStruct() local_unnamed_addr #14 { - %1 = tail call i64 @sorbet_getConstant(i8* getelementptr inbounds ([9 x i8], [9 x i8]* @str_MyStruct, i64 0, i64 0), i64 8) - store i64 %1, i64* @guarded_const_MyStruct, align 8 - %2 = load i64, i64* @ruby_vm_global_constant_state, align 8, !tbaa !30 - store i64 %2, i64* @guard_epoch_MyStruct, align 8 - ret void -} - -; Function Attrs: ssp -define linkonce void @"const_recompute_T::Struct"() local_unnamed_addr #14 { - %1 = tail call i64 @sorbet_getConstant(i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"str_T::Struct", i64 0, i64 0), i64 9) - store i64 %1, i64* @"guarded_const_T::Struct", align 8 - %2 = load i64, i64* @ruby_vm_global_constant_state, align 8, !tbaa !30 - store i64 %2, i64* @"guard_epoch_T::Struct", align 8 - ret void -} - -attributes #0 = { nounwind readnone willreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #1 = { cold noreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #2 = { noreturn "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #3 = { "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #4 = { nofree nosync nounwind readnone speculatable willreturn } -attributes #5 = { allocsize(0,1) "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #6 = { argmemonly nofree nosync nounwind willreturn } -attributes #7 = { nounwind ssp uwtable "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #8 = { nounwind sspreq uwtable } -attributes #9 = { sspreq } -attributes #10 = { inaccessiblememonly nofree nosync nounwind willreturn } -attributes #11 = { argmemonly nofree nosync nounwind willreturn writeonly } -attributes #12 = { cold minsize noreturn nounwind sspreq uwtable } -attributes #13 = { nofree nosync nounwind willreturn } -attributes #14 = { ssp } -attributes #15 = { noreturn nounwind } -attributes #16 = { nounwind } -attributes #17 = { nounwind allocsize(0,1) } -attributes #18 = { nounwind readnone willreturn } -attributes #19 = { nounwind willreturn } -attributes #20 = { noreturn } -attributes #21 = { noinline } - -!llvm.module.flags = !{!0, !1, !2} -!llvm.dbg.cu = !{!3} - -!0 = !{i32 2, !"Debug Info Version", i32 3} -!1 = !{i32 4, !"cf-protection-return", i32 1} -!2 = !{i32 4, !"cf-protection-branch", i32 1} -!3 = distinct !DICompileUnit(language: DW_LANG_C, file: !4, producer: "Sorbet LLVM", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !5) -!4 = !DIFile(filename: "test/testdata/ruby_benchmark/stripe/prop_const_getter.rb", directory: ".") -!5 = !{} -!6 = !{!7, !7, i64 0} -!7 = !{!"long", !8, i64 0} -!8 = !{!"omnipotent char", !9, i64 0} -!9 = !{!"Simple C/C++ TBAA"} -!10 = distinct !DISubprogram(name: "MyStruct.", linkageName: "func_MyStruct.13L62", scope: null, file: !4, line: 5, type: !11, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!11 = !DISubroutineType(types: !12) -!12 = !{!13} -!13 = !DIBasicType(name: "VALUE", size: 64, encoding: DW_ATE_signed) -!14 = !{!15, !15, i64 0} -!15 = !{!"any pointer", !8, i64 0} -!16 = !{!17, !15, i64 16} -!17 = !{!"rb_execution_context_struct", !15, i64 0, !7, i64 8, !15, i64 16, !15, i64 24, !15, i64 32, !18, i64 40, !18, i64 44, !15, i64 48, !15, i64 56, !15, i64 64, !7, i64 72, !7, i64 80, !15, i64 88, !7, i64 96, !15, i64 104, !15, i64 112, !7, i64 120, !7, i64 128, !8, i64 136, !8, i64 137, !7, i64 144, !19, i64 152} -!18 = !{!"int", !8, i64 0} -!19 = !{!"", !15, i64 0, !15, i64 8, !7, i64 16, !8, i64 24} -!20 = !{!21, !15, i64 16} -!21 = !{!"rb_control_frame_struct", !15, i64 0, !15, i64 8, !15, i64 16, !7, i64 24, !15, i64 32, !15, i64 40, !15, i64 48} -!22 = !{!21, !15, i64 32} -!23 = !DILocation(line: 0, scope: !10) -!24 = !DILocation(line: 5, column: 1, scope: !10) -!25 = !{!17, !18, i64 40} -!26 = !{!17, !18, i64 44} -!27 = !{!"branch_weights", i32 2000, i32 1} -!28 = !{!17, !15, i64 56} -!29 = !DILocation(line: 6, column: 3, scope: !10) -!30 = !{!31, !31, i64 0} -!31 = !{!"long long", !8, i64 0} -!32 = !{!"branch_weights", i32 1, i32 10000} -!33 = !{!34, !18, i64 4} -!34 = !{!"rb_sorbet_param_struct", !35, i64 0, !18, i64 4, !18, i64 8, !18, i64 12, !18, i64 16, !18, i64 20, !18, i64 24, !18, i64 28, !15, i64 32, !18, i64 40, !18, i64 44, !18, i64 48, !18, i64 52, !15, i64 56} -!35 = !{!"", !18, i64 0, !18, i64 0, !18, i64 0, !18, i64 0, !18, i64 0, !18, i64 0, !18, i64 0, !18, i64 0, !18, i64 1, !18, i64 1} -!36 = !{!34, !18, i64 40} -!37 = !{!34, !18, i64 44} -!38 = !{!34, !18, i64 48} -!39 = !{!34, !15, i64 56} -!40 = !DILocation(line: 6, column: 9, scope: !10) -!41 = !DILocation(line: 9, column: 13, scope: !42) -!42 = distinct !DISubprogram(name: ".", linkageName: "func_.17$152", scope: null, file: !4, line: 5, type: !11, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!43 = !DILocation(line: 5, column: 1, scope: !44) -!44 = distinct !DISubprogram(name: "MyStruct.", linkageName: "func_MyStruct.13L62$block_1", scope: !10, file: !4, line: 5, type: !11, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!45 = !DILocation(line: 12, column: 7, scope: !42) -!46 = !DILocation(line: 14, column: 3, scope: !42) -!47 = !DILocation(line: 16, column: 3, scope: !42) -!48 = !DILocation(line: 19, column: 1, scope: !42) -!49 = !DILocation(line: 20, column: 6, scope: !42) -!50 = !DILocation(line: 20, column: 1, scope: !42) -!51 = !DILocation(line: 6, column: 3, scope: !52) -!52 = distinct !DISubprogram(name: "MyStruct.", linkageName: "func_MyStruct.13L62$block_2", scope: !10, file: !4, line: 5, type: !11, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!53 = !{!54, !7, i64 400} -!54 = !{!"rb_vm_struct", !7, i64 0, !55, i64 8, !15, i64 192, !15, i64 200, !15, i64 208, !31, i64 216, !8, i64 224, !56, i64 264, !56, i64 280, !56, i64 296, !56, i64 312, !7, i64 328, !18, i64 336, !18, i64 340, !18, i64 344, !18, i64 344, !18, i64 344, !18, i64 344, !18, i64 348, !7, i64 352, !8, i64 360, !7, i64 400, !7, i64 408, !7, i64 416, !7, i64 424, !7, i64 432, !7, i64 440, !7, i64 448, !15, i64 456, !15, i64 464, !58, i64 472, !59, i64 992, !15, i64 1016, !15, i64 1024, !18, i64 1032, !18, i64 1036, !56, i64 1040, !8, i64 1056, !7, i64 1096, !7, i64 1104, !7, i64 1112, !7, i64 1120, !7, i64 1128, !18, i64 1136, !15, i64 1144, !15, i64 1152, !15, i64 1160, !15, i64 1168, !15, i64 1176, !15, i64 1184, !18, i64 1192, !60, i64 1200, !8, i64 1232} -!55 = !{!"rb_global_vm_lock_struct", !15, i64 0, !8, i64 8, !56, i64 48, !15, i64 64, !18, i64 72, !8, i64 80, !8, i64 128, !18, i64 176, !18, i64 180} -!56 = !{!"list_head", !57, i64 0} -!57 = !{!"list_node", !15, i64 0, !15, i64 8} -!58 = !{!"", !8, i64 0} -!59 = !{!"rb_hook_list_struct", !15, i64 0, !18, i64 8, !18, i64 12, !18, i64 16} -!60 = !{!"", !7, i64 0, !7, i64 8, !7, i64 16, !7, i64 24} -!61 = !DILocation(line: 0, scope: !42) -!62 = !DILocation(line: 5, column: 1, scope: !42) -!63 = !DILocation(line: 5, column: 18, scope: !42) -!64 = !DILocation(line: 11, column: 5, scope: !42) -!65 = !{!"branch_weights", i32 1, i32 2000} -!66 = !{!"branch_weights", i32 1073205, i32 2146410443} -!67 = !{!68, !7, i64 0} -!68 = !{!"RBasic", !7, i64 0, !7, i64 8} -!69 = !{!70} -!70 = distinct !{!70, !71, !"sorbet_rb_int_lt: argument 0"} -!71 = distinct !{!71, !"sorbet_rb_int_lt"} -!72 = !{!"branch_weights", i32 4001, i32 4000000} -!73 = !{!74} -!74 = distinct !{!74, !75, !"sorbet_rb_int_plus: argument 0"} -!75 = distinct !{!75, !"sorbet_rb_int_plus"} -!76 = distinct !DISubprogram(name: "MyStruct#initialize", linkageName: "func_MyStruct#10initialize", scope: null, file: !4, line: 5, type: !11, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !5) -!77 = !DILocation(line: 5, column: 1, scope: !76) -!78 = !DILocation(line: 0, scope: !76) -!79 = !DILocation(line: 6, column: 3, scope: !76) -!80 = !DILocation(line: 6, column: 10, scope: !76) -!81 = !{!82} -!82 = distinct !{!82, !83, !"sorbet_callSuperBlockHandler: argument 0"} -!83 = distinct !{!83, !"sorbet_callSuperBlockHandler"} -!84 = !{!21, !7, i64 24} -!85 = !{!86, !7, i64 8} -!86 = !{!"rb_callable_method_entry_struct", !7, i64 0, !7, i64 8, !15, i64 16, !7, i64 24, !7, i64 32} -!87 = !{!88, !15, i64 24} -!88 = !{!"RClass", !68, i64 0, !7, i64 16, !15, i64 24, !31, i64 32} -!89 = !{!90, !7, i64 64} -!90 = !{!"rb_classext_struct", !15, i64 0, !15, i64 8, !15, i64 16, !15, i64 24, !15, i64 32, !15, i64 40, !15, i64 48, !15, i64 56, !7, i64 64, !7, i64 72, !15, i64 80, !7, i64 88} -!91 = !{!88, !7, i64 16} -!92 = !{!86, !15, i64 16} -!93 = !{!94, !7, i64 32} -!94 = !{!"rb_method_definition_struct", !8, i64 0, !18, i64 0, !18, i64 4, !8, i64 8, !7, i64 32, !7, i64 40} -!95 = !{!17, !7, i64 128} -!96 = distinct !DISubprogram(name: "func_MyStruct#10initialize.cold.2", linkageName: "func_MyStruct#10initialize.cold.2", scope: null, file: !4, type: !97, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !3, retainedNodes: !5) -!97 = !DISubroutineType(types: !5) -!98 = !DILocation(line: 6, column: 3, scope: !96) diff --git a/test/testdata/ruby_benchmark/stripe/prop_const_getter.rb b/test/testdata/ruby_benchmark/stripe/prop_const_getter.rb deleted file mode 100644 index 514a5b7bfb..0000000000 --- a/test/testdata/ruby_benchmark/stripe/prop_const_getter.rb +++ /dev/null @@ -1,20 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -class MyStruct < T::Struct - const :foo, Integer -end - -my_struct = MyStruct.new(foo: 430) - -i = 0 -while i < 10_000_000 - - my_struct.foo - - i += 1 -end - -puts i -puts my_struct.foo diff --git a/test/testdata/ruby_benchmark/stripe/prop_const_getter_force_ivar.rb b/test/testdata/ruby_benchmark/stripe/prop_const_getter_force_ivar.rb deleted file mode 100644 index f1147345e3..0000000000 --- a/test/testdata/ruby_benchmark/stripe/prop_const_getter_force_ivar.rb +++ /dev/null @@ -1,24 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -class MyStruct < T::Struct - const :foo, Integer - - # This is here to trick the compiler into redefining the method using - # `attr_reader`, which the VM special cases. - self.send(:attr_reader, :foo) -end - -my_struct = MyStruct.new(foo: 430) - -i = 0 -while i < 10_000_000 - - my_struct.foo - - i += 1 -end - -puts i -puts my_struct.foo diff --git a/test/testdata/ruby_benchmark/stripe/returns_nilclass/no_sig.rb b/test/testdata/ruby_benchmark/stripe/returns_nilclass/no_sig.rb deleted file mode 100644 index 1b50c3f585..0000000000 --- a/test/testdata/ruby_benchmark/stripe/returns_nilclass/no_sig.rb +++ /dev/null @@ -1,20 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -extend T::Sig - -def returns_nilclass - nil -end - -i = 0 -while i < 10_000_000 - - self.returns_nilclass - - i += 1 -end - -puts i -puts returns_nilclass diff --git a/test/testdata/ruby_benchmark/stripe/returns_nilclass/sig_checked.rb b/test/testdata/ruby_benchmark/stripe/returns_nilclass/sig_checked.rb deleted file mode 100644 index 777805359f..0000000000 --- a/test/testdata/ruby_benchmark/stripe/returns_nilclass/sig_checked.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -extend T::Sig - -sig {returns(NilClass)} -def returns_nilclass - nil -end - -i = 0 -while i < 10_000_000 - - self.returns_nilclass - - i += 1 -end - -puts i -puts returns_nilclass diff --git a/test/testdata/ruby_benchmark/stripe/returns_nilclass/sig_unchecked.rb b/test/testdata/ruby_benchmark/stripe/returns_nilclass/sig_unchecked.rb deleted file mode 100644 index c4eefe96e9..0000000000 --- a/test/testdata/ruby_benchmark/stripe/returns_nilclass/sig_unchecked.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -extend T::Sig - -sig {returns(NilClass).checked(:never)} -def returns_nilclass - nil -end - -i = 0 -while i < 10_000_000 - - self.returns_nilclass - - i += 1 -end - -puts i -puts returns_nilclass diff --git a/test/testdata/ruby_benchmark/stripe/returns_nilclass/sig_unchecked_final.rb b/test/testdata/ruby_benchmark/stripe/returns_nilclass/sig_unchecked_final.rb deleted file mode 100644 index f0189b13d0..0000000000 --- a/test/testdata/ruby_benchmark/stripe/returns_nilclass/sig_unchecked_final.rb +++ /dev/null @@ -1,20 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -extend T::Sig - -sig(:final) {returns(NilClass).checked(:never)} -def returns_nilclass - nil -end - -i = 0 -while i < 10_000_000 - i += 1 - - self.returns_nilclass -end - -puts i -puts returns_nilclass diff --git a/test/testdata/ruby_benchmark/stripe/string_subclass_to_s.rb b/test/testdata/ruby_benchmark/stripe/string_subclass_to_s.rb deleted file mode 100644 index e1ebdd801b..0000000000 --- a/test/testdata/ruby_benchmark/stripe/string_subclass_to_s.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true -# compiled: true -# typed: true - -class StringSubclass < String; end - -x = StringSubclass.new - -i = 0 - -while i < 10_000_000 - x.to_s - - i += 1 -end - -puts x diff --git a/test/testdata/ruby_benchmark/stripe/string_to_s.rb b/test/testdata/ruby_benchmark/stripe/string_to_s.rb deleted file mode 100644 index 60e50d923c..0000000000 --- a/test/testdata/ruby_benchmark/stripe/string_to_s.rb +++ /dev/null @@ -1,15 +0,0 @@ -# frozen_string_literal: true -# compiled: true -# typed: true - -x = "hi" - -i = 0 - -while i < 10_000_000 - x.to_s - - i += 1 -end - -puts x diff --git a/test/testdata/ruby_benchmark/stripe/sum_first_ten_million.rb b/test/testdata/ruby_benchmark/stripe/sum_first_ten_million.rb deleted file mode 100644 index 1dbde46b9c..0000000000 --- a/test/testdata/ruby_benchmark/stripe/sum_first_ten_million.rb +++ /dev/null @@ -1,10 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -xs = Array.new(10_000_000) {|i| i} -sum = 0 -xs.each do |i| - sum += i -end -puts sum diff --git a/test/testdata/ruby_benchmark/stripe/t_let.rb b/test/testdata/ruby_benchmark/stripe/t_let.rb deleted file mode 100644 index 29b36a9409..0000000000 --- a/test/testdata/ruby_benchmark/stripe/t_let.rb +++ /dev/null @@ -1,15 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -x = T.unsafe(nil) - -i = 0 -while i < 10_000_000 - - T.let(x, NilClass) - - i += 1 -end - -puts i diff --git a/test/testdata/ruby_benchmark/stripe/t_must_intrinsic.rb b/test/testdata/ruby_benchmark/stripe/t_must_intrinsic.rb deleted file mode 100644 index 3406875485..0000000000 --- a/test/testdata/ruby_benchmark/stripe/t_must_intrinsic.rb +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -def test(a, b) - i = 0 - while i < 1_000_000 - T.must(a) - T.must(b) - i += 1 - end -end - -test(true, false) diff --git a/test/testdata/ruby_benchmark/stripe/t_struct_creation.rb b/test/testdata/ruby_benchmark/stripe/t_struct_creation.rb deleted file mode 100644 index 30925e32b7..0000000000 --- a/test/testdata/ruby_benchmark/stripe/t_struct_creation.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -class TestClass < T::Struct - prop :int, Integer - prop :str, String -end - -i = 0 -while i < 10_000_000 - TestClass.new(int: 5, str: "foo") - - i += 1 -end - -puts i diff --git a/test/testdata/ruby_benchmark/stripe/triple_eq.rb b/test/testdata/ruby_benchmark/stripe/triple_eq.rb deleted file mode 100644 index 77d2677bf7..0000000000 --- a/test/testdata/ruby_benchmark/stripe/triple_eq.rb +++ /dev/null @@ -1,15 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -x = T.unsafe(nil) - -i = 0 -while i < 10_000_000 - - NilClass.===(x) - - i += 1 -end - -puts i diff --git a/test/testdata/ruby_benchmark/stripe/typed_array_aref.rb b/test/testdata/ruby_benchmark/stripe/typed_array_aref.rb deleted file mode 100644 index ddbbdcdd31..0000000000 --- a/test/testdata/ruby_benchmark/stripe/typed_array_aref.rb +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -xs = T.let([2], T::Array[Integer]) - -i = 0 -while i < 10_000_000 - xs[0] - i += 1 -end - -puts xs[0] diff --git a/test/testdata/ruby_benchmark/stripe/typed_array_subclass_aref.rb b/test/testdata/ruby_benchmark/stripe/typed_array_subclass_aref.rb deleted file mode 100644 index 0a6feb9381..0000000000 --- a/test/testdata/ruby_benchmark/stripe/typed_array_subclass_aref.rb +++ /dev/null @@ -1,16 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -class MyArray < Array -end - -xs = T.let(MyArray.new([2]), MyArray) - -i = 0 -while i < 10_000_000 - xs[0] - i += 1 -end - -puts xs[0] diff --git a/test/testdata/ruby_benchmark/stripe/typed_hash_aref.rb b/test/testdata/ruby_benchmark/stripe/typed_hash_aref.rb deleted file mode 100644 index 627a37f37b..0000000000 --- a/test/testdata/ruby_benchmark/stripe/typed_hash_aref.rb +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -xs = T.let({key: :val}, T::Hash[Symbol, Symbol]) - -i = 0 -while i < 10_000_000 - xs[:key] - i += 1 -end - -puts xs[:key] diff --git a/test/testdata/ruby_benchmark/stripe/untyped_array_aref.rb b/test/testdata/ruby_benchmark/stripe/untyped_array_aref.rb deleted file mode 100644 index 9f9dc695cf..0000000000 --- a/test/testdata/ruby_benchmark/stripe/untyped_array_aref.rb +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -xs = T.let([2], T.untyped) - -i = 0 -while i < 10_000_000 - xs[0] - i += 1 -end - -puts xs[0] diff --git a/test/testdata/ruby_benchmark/stripe/untyped_array_subclass_aref.rb b/test/testdata/ruby_benchmark/stripe/untyped_array_subclass_aref.rb deleted file mode 100644 index c26cc105bf..0000000000 --- a/test/testdata/ruby_benchmark/stripe/untyped_array_subclass_aref.rb +++ /dev/null @@ -1,15 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -class MyArray < Array; end - -xs = T.let(MyArray.new([2]), T.untyped) - -i = 0 -while i < 10_000_000 - xs[0] - i += 1 -end - -puts xs[0] diff --git a/test/testdata/ruby_benchmark/stripe/untyped_hash_aref.rb b/test/testdata/ruby_benchmark/stripe/untyped_hash_aref.rb deleted file mode 100644 index 4a10f40064..0000000000 --- a/test/testdata/ruby_benchmark/stripe/untyped_hash_aref.rb +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -xs = T.let({key: :val}, T.untyped) - -i = 0 -while i < 10_000_000 - xs[:key] - i += 1 -end - -puts xs[:key] diff --git a/test/testdata/ruby_benchmark/stripe/void.rb b/test/testdata/ruby_benchmark/stripe/void.rb deleted file mode 100644 index 456be3ebc6..0000000000 --- a/test/testdata/ruby_benchmark/stripe/void.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -class Main - extend T::Sig - sig {void.checked(:never)} - def self.main - nil - end -end - -i = 0 -while i < 10_000_000 - - Main.main - - i += 1 -end - -puts i diff --git a/test/testdata/ruby_benchmark/stripe/while_100_000_000.rb b/test/testdata/ruby_benchmark/stripe/while_100_000_000.rb deleted file mode 100644 index 6262d34b1e..0000000000 --- a/test/testdata/ruby_benchmark/stripe/while_100_000_000.rb +++ /dev/null @@ -1,18 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -# Many of our benchmarks use a while loop with `+=` over 100M items. -# -# Use this benchmark as a baseline to check how much time is spent in an -# operation vs spent in the while loop & loop counter. - -i = 0 -while i < 100_000_000 - - # ... potential extra stuff ... - - i += 1 -end - -puts i diff --git a/test/testdata/ruby_benchmark/stripe/while_10_000_000.rb b/test/testdata/ruby_benchmark/stripe/while_10_000_000.rb deleted file mode 100644 index 06f03bcc9b..0000000000 --- a/test/testdata/ruby_benchmark/stripe/while_10_000_000.rb +++ /dev/null @@ -1,18 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -# Many of our benchmarks use a while loop with `+=` over 10M items. -# -# Use this benchmark as a baseline to check how much time is spent in an -# operation vs spent in the while loop & loop counter. - -i = 0 -while i < 10_000_000 - - # ... potential extra stuff ... - - i += 1 -end - -puts i diff --git a/test/testdata/ruby_benchmark/stripe/while_1_000_000.rb b/test/testdata/ruby_benchmark/stripe/while_1_000_000.rb deleted file mode 100644 index 7e30986e73..0000000000 --- a/test/testdata/ruby_benchmark/stripe/while_1_000_000.rb +++ /dev/null @@ -1,18 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -# Many of our benchmarks use a while loop with `+=` over 1M items. -# -# Use this benchmark as a baseline to check how much time is spent in an -# operation vs spent in the while loop & loop counter. - -i = 0 -while i < 1_000_000 - - # ... potential extra stuff ... - - i += 1 -end - -puts i diff --git a/test/testdata/ruby_benchmark/stripe/while_6_000_000.rb b/test/testdata/ruby_benchmark/stripe/while_6_000_000.rb deleted file mode 100644 index b97b365d48..0000000000 --- a/test/testdata/ruby_benchmark/stripe/while_6_000_000.rb +++ /dev/null @@ -1,18 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true - -# Many of our benchmarks use a while loop with `+=` over 6M items. -# -# Use this benchmark as a baseline to check how much time is spent in an -# operation vs spent in the while loop & loop counter. - -i = 0 -while i < 6_000_000 - - # ... potential extra stuff ... - - i += 1 -end - -puts i diff --git a/test/testdata/ruby_benchmark/time_strptime.yml b/test/testdata/ruby_benchmark/time_strptime.yml deleted file mode 100644 index 8d89ebb7a7..0000000000 --- a/test/testdata/ruby_benchmark/time_strptime.yml +++ /dev/null @@ -1,13 +0,0 @@ -prelude: | - require 'time' -benchmark: - - Time.strptime("28/Aug/2005:06:54:20 +0000", "%d/%b/%Y:%T %z") - - Time.strptime("1", "%s") - - Time.strptime("0 +0100", "%s %z") - - Time.strptime("0 UTC", "%s %z") - - Time.strptime("1.5", "%s.%N") - - Time.strptime("1.000000000001", "%s.%N") - - Time.strptime("20010203 -0200", "%Y%m%d %z") - - Time.strptime("20010203 UTC", "%Y%m%d %z") - - Time.strptime("2018-365", "%Y-%j") - - Time.strptime("2018-091", "%Y-%j") diff --git a/test/testdata/ruby_benchmark/time_subsec.rb b/test/testdata/ruby_benchmark/time_subsec.rb deleted file mode 100644 index df3f211151..0000000000 --- a/test/testdata/ruby_benchmark/time_subsec.rb +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true -# typed: strong -# compiled: true -t = Time.now -4000000.times { t.subsec } diff --git a/test/testdata/ruby_benchmark/too_slow/io_select3.rb b/test/testdata/ruby_benchmark/too_slow/io_select3.rb deleted file mode 100644 index 9667527efb..0000000000 --- a/test/testdata/ruby_benchmark/too_slow/io_select3.rb +++ /dev/null @@ -1,24 +0,0 @@ -# frozen_string_literal: true -# typed: strict -# compiled: true -# IO.select performance. a lot of fd - -ios = [] -nr = 100 -if defined?(Process::RLIMIT_NOFILE) - max = Process.getrlimit(Process::RLIMIT_NOFILE)[0] -else - max = 64 -end -puts "max fd: #{max} (results not apparent with <= 1024 max fd)" - -(max - 10).times do - r, w = IO.pipe - r.close - ios.push w -end - -nr.times do - IO.select nil, ios -end - diff --git a/test/testdata/ruby_benchmark/too_slow/so_nbody.rb b/test/testdata/ruby_benchmark/too_slow/so_nbody.rb deleted file mode 100644 index 0d6ecba589..0000000000 --- a/test/testdata/ruby_benchmark/too_slow/so_nbody.rb +++ /dev/null @@ -1,151 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -# The Computer Language Shootout -# http://shootout.alioth.debian.org -# -# Optimized for Ruby by Jesse Millikan -# From version ported by Michael Neumann from the C gcc version, -# which was written by Christoph Bauer. - -SOLAR_MASS = 4 * Math::PI**2 -DAYS_PER_YEAR = 365.24 - -def _puts *args -end - -class Planet - attr_accessor :x, :y, :z, :vx, :vy, :vz, :mass - - def initialize(x, y, z, vx, vy, vz, mass) - @x, @y, @z = x, y, z - @vx, @vy, @vz = vx * DAYS_PER_YEAR, vy * DAYS_PER_YEAR, vz * DAYS_PER_YEAR - @mass = mass * SOLAR_MASS - end - - def move_from_i(bodies, nbodies, dt, i) - while i < nbodies - b2 = bodies[i] - dx = @x - b2.x - dy = @y - b2.y - dz = @z - b2.z - - distance = Math.sqrt(dx * dx + dy * dy + dz * dz) - mag = dt / (distance * distance * distance) - b_mass_mag, b2_mass_mag = @mass * mag, b2.mass * mag - - @vx -= dx * b2_mass_mag - @vy -= dy * b2_mass_mag - @vz -= dz * b2_mass_mag - b2.vx += dx * b_mass_mag - b2.vy += dy * b_mass_mag - b2.vz += dz * b_mass_mag - i += 1 - end - - @x += dt * @vx - @y += dt * @vy - @z += dt * @vz - end -end - -def energy(bodies) - e = 0.0 - nbodies = bodies.size - - for i in 0 ... nbodies - b = bodies[i] - e += 0.5 * b.mass * (b.vx * b.vx + b.vy * b.vy + b.vz * b.vz) - for j in (i + 1) ... nbodies - b2 = bodies[j] - dx = b.x - b2.x - dy = b.y - b2.y - dz = b.z - b2.z - distance = Math.sqrt(dx * dx + dy * dy + dz * dz) - e -= (b.mass * b2.mass) / distance - end - end - e -end - -def offset_momentum(bodies) - px, py, pz = 0.0, 0.0, 0.0 - - for b in bodies - m = b.mass - px += b.vx * m - py += b.vy * m - pz += b.vz * m - end - - b = bodies[0] - b.vx = - px / SOLAR_MASS - b.vy = - py / SOLAR_MASS - b.vz = - pz / SOLAR_MASS -end - -BODIES = [ - # sun - Planet.new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0), - - # jupiter - Planet.new( - 4.84143144246472090e+00, - -1.16032004402742839e+00, - -1.03622044471123109e-01, - 1.66007664274403694e-03, - 7.69901118419740425e-03, - -6.90460016972063023e-05, - 9.54791938424326609e-04), - - # saturn - Planet.new( - 8.34336671824457987e+00, - 4.12479856412430479e+00, - -4.03523417114321381e-01, - -2.76742510726862411e-03, - 4.99852801234917238e-03, - 2.30417297573763929e-05, - 2.85885980666130812e-04), - - # uranus - Planet.new( - 1.28943695621391310e+01, - -1.51111514016986312e+01, - -2.23307578892655734e-01, - 2.96460137564761618e-03, - 2.37847173959480950e-03, - -2.96589568540237556e-05, - 4.36624404335156298e-05), - - # neptune - Planet.new( - 1.53796971148509165e+01, - -2.59193146099879641e+01, - 1.79258772950371181e-01, - 2.68067772490389322e-03, - 1.62824170038242295e-03, - -9.51592254519715870e-05, - 5.15138902046611451e-05) -] - -init = 200_000 # ARGV[0] -n = Integer(init) - -offset_momentum(BODIES) - -puts "%.9f" % energy(BODIES) - -nbodies = BODIES.size -dt = 0.01 - -n.times do - i = 0 - while i < nbodies - b = BODIES[i] - b.move_from_i(BODIES, nbodies, dt, i + 1) - i += 1 - end -end - -puts "%.9f" % energy(BODIES) diff --git a/test/testdata/ruby_benchmark/vm1_attr_ivar.yml b/test/testdata/ruby_benchmark/vm1_attr_ivar.yml deleted file mode 100644 index f714dd9bd9..0000000000 --- a/test/testdata/ruby_benchmark/vm1_attr_ivar.yml +++ /dev/null @@ -1,14 +0,0 @@ -prelude: | - class C - attr_reader :a, :b - def initialize - @a = nil - @b = nil - end - end - obj = C.new -benchmark: - vm1_attr_ivar: | - j = obj.a - k = obj.b -loop_count: 30000000 diff --git a/test/testdata/ruby_benchmark/vm1_attr_ivar_set.yml b/test/testdata/ruby_benchmark/vm1_attr_ivar_set.yml deleted file mode 100644 index f383e59ef4..0000000000 --- a/test/testdata/ruby_benchmark/vm1_attr_ivar_set.yml +++ /dev/null @@ -1,14 +0,0 @@ -prelude: | - class C - attr_accessor :a, :b - def initialize - @a = nil - @b = nil - end - end - obj = C.new -benchmark: - vm1_attr_ivar_set: | - obj.a = 1 - obj.b = 2 -loop_count: 30000000 diff --git a/test/testdata/ruby_benchmark/vm1_block.yml b/test/testdata/ruby_benchmark/vm1_block.yml deleted file mode 100644 index ac7c940f93..0000000000 --- a/test/testdata/ruby_benchmark/vm1_block.yml +++ /dev/null @@ -1,9 +0,0 @@ -prelude: | - def m - yield - end -benchmark: - vm1_block: | - m{ - } -loop_count: 30000000 diff --git a/test/testdata/ruby_benchmark/vm1_blockparam.yml b/test/testdata/ruby_benchmark/vm1_blockparam.yml deleted file mode 100644 index 947b8c53d5..0000000000 --- a/test/testdata/ruby_benchmark/vm1_blockparam.yml +++ /dev/null @@ -1,7 +0,0 @@ -prelude: | - def m &b - end -benchmark: - vm1_blockparam: | - m{} -loop_count: 30000000 diff --git a/test/testdata/ruby_benchmark/vm1_blockparam_call.yml b/test/testdata/ruby_benchmark/vm1_blockparam_call.yml deleted file mode 100644 index e2817a3ce2..0000000000 --- a/test/testdata/ruby_benchmark/vm1_blockparam_call.yml +++ /dev/null @@ -1,8 +0,0 @@ -prelude: | - def m &b - b.call - end -benchmark: - vm1_blockparam_call: | - m{} -loop_count: 30000000 diff --git a/test/testdata/ruby_benchmark/vm1_blockparam_pass.yml b/test/testdata/ruby_benchmark/vm1_blockparam_pass.yml deleted file mode 100644 index ca1bef3369..0000000000 --- a/test/testdata/ruby_benchmark/vm1_blockparam_pass.yml +++ /dev/null @@ -1,12 +0,0 @@ -prelude: | - def bp_yield - yield - end - - def bp_pass &b - bp_yield &b - end -benchmark: - vm1_blockparam_pass: | - bp_pass{} -loop_count: 30000000 diff --git a/test/testdata/ruby_benchmark/vm1_blockparam_yield.yml b/test/testdata/ruby_benchmark/vm1_blockparam_yield.yml deleted file mode 100644 index 56ae617798..0000000000 --- a/test/testdata/ruby_benchmark/vm1_blockparam_yield.yml +++ /dev/null @@ -1,8 +0,0 @@ -prelude: | - def bp_yield &b - yield - end -benchmark: - vm1_blockparam_yield: | - bp_yield{} -loop_count: 30000000 diff --git a/test/testdata/ruby_benchmark/vm1_const.yml b/test/testdata/ruby_benchmark/vm1_const.yml deleted file mode 100644 index b98db1545c..0000000000 --- a/test/testdata/ruby_benchmark/vm1_const.yml +++ /dev/null @@ -1,7 +0,0 @@ -prelude: | - Const = 1 -benchmark: - vm1_const: | - j = Const - k = Const -loop_count: 30000000 diff --git a/test/testdata/ruby_benchmark/vm1_ensure.yml b/test/testdata/ruby_benchmark/vm1_ensure.yml deleted file mode 100644 index afbbe38bec..0000000000 --- a/test/testdata/ruby_benchmark/vm1_ensure.yml +++ /dev/null @@ -1,14 +0,0 @@ -# Not utilizing loop_count since using it for this is too unstable for now -benchmark: - vm1_ensure: | - i = 0 - while i<30_000_000 - i += 1 - begin - begin - ensure - end - ensure - end - end -loop_count: 1 diff --git a/test/testdata/ruby_benchmark/vm1_float_simple.yml b/test/testdata/ruby_benchmark/vm1_float_simple.yml deleted file mode 100644 index 4e9ad1852b..0000000000 --- a/test/testdata/ruby_benchmark/vm1_float_simple.yml +++ /dev/null @@ -1,8 +0,0 @@ -prelude: | - f = 0.0 -benchmark: - vm1_float_simple: | - f += 0.1; f -= 0.1 - f += 0.1; f -= 0.1 - f += 0.1; f -= 0.1 -loop_count: 30000000 diff --git a/test/testdata/ruby_benchmark/vm1_gc_short_lived.yml b/test/testdata/ruby_benchmark/vm1_gc_short_lived.yml deleted file mode 100644 index 8fdcb7371d..0000000000 --- a/test/testdata/ruby_benchmark/vm1_gc_short_lived.yml +++ /dev/null @@ -1,9 +0,0 @@ -benchmark: - vm1_gc_short_lived: | - a = '' # short-lived String - b = '' - c = '' - d = '' - e = '' - f = '' -loop_count: 30000000 diff --git a/test/testdata/ruby_benchmark/vm1_gc_short_with_complex_long.yml b/test/testdata/ruby_benchmark/vm1_gc_short_with_complex_long.yml deleted file mode 100644 index c22ea74a60..0000000000 --- a/test/testdata/ruby_benchmark/vm1_gc_short_with_complex_long.yml +++ /dev/null @@ -1,25 +0,0 @@ -prelude: | - def nested_hash h, n - if n == 0 - '' - else - 10.times{ - h[Object.new] = nested_hash(h, n-1) - } - end - end - - long_lived = Hash.new - nested_hash long_lived, 6 - - GC.start - GC.start -benchmark: - vm1_gc_short_with_complex_long: | - a = '' # short-lived String - b = '' - c = '' - d = '' - e = '' - f = '' -loop_count: 30000000 diff --git a/test/testdata/ruby_benchmark/vm1_gc_short_with_long.yml b/test/testdata/ruby_benchmark/vm1_gc_short_with_long.yml deleted file mode 100644 index c731aae548..0000000000 --- a/test/testdata/ruby_benchmark/vm1_gc_short_with_long.yml +++ /dev/null @@ -1,13 +0,0 @@ -prelude: | - long_lived = Array.new(1_000_000){|i| "#{i}"} - GC.start - GC.start -benchmark: - vm1_gc_short_with_long: | - a = '' # short-lived String - b = '' - c = '' - d = '' - e = '' - f = '' -loop_count: 30000000 diff --git a/test/testdata/ruby_benchmark/vm1_gc_short_with_symbol.yml b/test/testdata/ruby_benchmark/vm1_gc_short_with_symbol.yml deleted file mode 100644 index 7fc1abedd8..0000000000 --- a/test/testdata/ruby_benchmark/vm1_gc_short_with_symbol.yml +++ /dev/null @@ -1,13 +0,0 @@ -prelude: | - 50_000.times{|i| sym = "sym#{i}".to_sym} - GC.start - GC.start -benchmark: - vm1_gc_short_with_symbol: | - a = '' # short-lived String - b = '' - c = '' - d = '' - e = '' - f = '' -loop_count: 30000000 diff --git a/test/testdata/ruby_benchmark/vm1_gc_wb_ary.yml b/test/testdata/ruby_benchmark/vm1_gc_wb_ary.yml deleted file mode 100644 index 50fb4b6f84..0000000000 --- a/test/testdata/ruby_benchmark/vm1_gc_wb_ary.yml +++ /dev/null @@ -1,12 +0,0 @@ -prelude: | - short_lived_ary = [] - - if RUBY_VERSION >= "2.2.0" - GC.start(full_mark: false, immediate_mark: true, immediate_sweep: true) - end - - short_lived = '' -benchmark: - vm1_gc_wb_ary: | - short_lived_ary[0] = short_lived # write barrier -loop_count: 30000000 diff --git a/test/testdata/ruby_benchmark/vm1_gc_wb_ary_promoted.yml b/test/testdata/ruby_benchmark/vm1_gc_wb_ary_promoted.yml deleted file mode 100644 index cf9b5de005..0000000000 --- a/test/testdata/ruby_benchmark/vm1_gc_wb_ary_promoted.yml +++ /dev/null @@ -1,15 +0,0 @@ -prelude: | - long_lived = [] - - if RUBY_VERSION > "2.2.0" - 3.times{ GC.start(full_mark: false, immediate_mark: true, immediate_sweep: true) } - elsif - GC.start - end - - short_lived = '' - -benchmark: - vm1_gc_wb_ary_promoted: | - long_lived[0] = short_lived # write barrier -loop_count: 30000000 diff --git a/test/testdata/ruby_benchmark/vm1_gc_wb_obj.yml b/test/testdata/ruby_benchmark/vm1_gc_wb_obj.yml deleted file mode 100644 index 9dc08e7e1a..0000000000 --- a/test/testdata/ruby_benchmark/vm1_gc_wb_obj.yml +++ /dev/null @@ -1,15 +0,0 @@ -prelude: | - class C - attr_accessor :foo - end - short_lived_obj = C.new - - if RUBY_VERSION >= "2.2.0" - GC.start(full_mark: false, immediate_mark: true, immediate_sweep: true) - end - - short_lived = '' -benchmark: - vm1_gc_wb_obj: | - short_lived_obj.foo = short_lived # write barrier -loop_count: 30000000 diff --git a/test/testdata/ruby_benchmark/vm1_gc_wb_obj_promoted.yml b/test/testdata/ruby_benchmark/vm1_gc_wb_obj_promoted.yml deleted file mode 100644 index 26859d2a52..0000000000 --- a/test/testdata/ruby_benchmark/vm1_gc_wb_obj_promoted.yml +++ /dev/null @@ -1,17 +0,0 @@ -prelude: | - class C - attr_accessor :foo - end - long_lived = C.new - - if RUBY_VERSION >= "2.2.0" - 3.times{ GC.start(full_mark: false, immediate_mark: true, immediate_sweep: true) } - elsif - GC.start - end - - short_lived = '' -benchmark: - vm1_gc_wb_obj_promoted: | - long_lived.foo = short_lived # write barrier -loop_count: 30000000 diff --git a/test/testdata/ruby_benchmark/vm1_ivar.yml b/test/testdata/ruby_benchmark/vm1_ivar.yml deleted file mode 100644 index 7aa6fac729..0000000000 --- a/test/testdata/ruby_benchmark/vm1_ivar.yml +++ /dev/null @@ -1,6 +0,0 @@ -prelude: "@a = 1\n" -benchmark: - vm1_ivar: | - j = @a - k = @a -loop_count: 30000000 diff --git a/test/testdata/ruby_benchmark/vm1_ivar_set.yml b/test/testdata/ruby_benchmark/vm1_ivar_set.yml deleted file mode 100644 index 6f19412d16..0000000000 --- a/test/testdata/ruby_benchmark/vm1_ivar_set.yml +++ /dev/null @@ -1,5 +0,0 @@ -benchmark: - vm1_ivar_set: | - @a = 1 - @b = 2 -loop_count: 30000000 diff --git a/test/testdata/ruby_benchmark/vm1_length.yml b/test/testdata/ruby_benchmark/vm1_length.yml deleted file mode 100644 index a18e2ca2e6..0000000000 --- a/test/testdata/ruby_benchmark/vm1_length.yml +++ /dev/null @@ -1,8 +0,0 @@ -prelude: | - a = 'abc' - b = [1, 2, 3] -benchmark: - vm1_length: | - a.length - b.length -loop_count: 30000000 diff --git a/test/testdata/ruby_benchmark/vm1_lvar_init.yml b/test/testdata/ruby_benchmark/vm1_lvar_init.yml deleted file mode 100644 index 10e2becef9..0000000000 --- a/test/testdata/ruby_benchmark/vm1_lvar_init.yml +++ /dev/null @@ -1,21 +0,0 @@ -# while loop cost is not removed because `i` is used in the script -benchmark: - vm1_lvar_init: | - def m v - unless v - # unreachable code - v1 = v2 = v3 = v4 = v5 = v6 = v7 = v8 = v9 = v10 = - v11 = v12 = v13 = v14 = v15 = v16 = v17 = v18 = v19 = v20 = - v21 = v22 = v23 = v24 = v25 = v26 = v27 = v28 = v29 = v30 = - v31 = v32 = v33 = v34 = v35 = v36 = v37 = v38 = v39 = v40 = - v41 = v42 = v43 = v44 = v45 = v46 = v47 = v48 = v49 = v50 = 1 - end - end - - i = 0 - - while i<30_000_000 - i += 1 - m i - end -loop_count: 1 diff --git a/test/testdata/ruby_benchmark/vm1_lvar_set.yml b/test/testdata/ruby_benchmark/vm1_lvar_set.yml deleted file mode 100644 index df8f6b6ea4..0000000000 --- a/test/testdata/ruby_benchmark/vm1_lvar_set.yml +++ /dev/null @@ -1,4 +0,0 @@ -benchmark: - vm1_lvar_set: | - a = b = c = d = e = f = g = h = j = k = l = m = n = o = p = q = r = 1 -loop_count: 30000000 diff --git a/test/testdata/ruby_benchmark/vm1_neq.yml b/test/testdata/ruby_benchmark/vm1_neq.yml deleted file mode 100644 index 65a8128dda..0000000000 --- a/test/testdata/ruby_benchmark/vm1_neq.yml +++ /dev/null @@ -1,7 +0,0 @@ -prelude: | - obj1 = Object.new - obj2 = Object.new -benchmark: - vm1_neq: | - obj1 != obj2 -loop_count: 30000000 diff --git a/test/testdata/ruby_benchmark/vm1_not.yml b/test/testdata/ruby_benchmark/vm1_not.yml deleted file mode 100644 index 0fb7b282a9..0000000000 --- a/test/testdata/ruby_benchmark/vm1_not.yml +++ /dev/null @@ -1,6 +0,0 @@ -prelude: | - obj = Object.new -benchmark: - vm1_not: | - !obj -loop_count: 30000000 diff --git a/test/testdata/ruby_benchmark/vm1_rescue.yml b/test/testdata/ruby_benchmark/vm1_rescue.yml deleted file mode 100644 index a175b823af..0000000000 --- a/test/testdata/ruby_benchmark/vm1_rescue.yml +++ /dev/null @@ -1,6 +0,0 @@ -benchmark: - vm1_rescue: | - begin - rescue - end -loop_count: 30000000 diff --git a/test/testdata/ruby_benchmark/vm1_simplereturn.yml b/test/testdata/ruby_benchmark/vm1_simplereturn.yml deleted file mode 100644 index 3564aac7e2..0000000000 --- a/test/testdata/ruby_benchmark/vm1_simplereturn.yml +++ /dev/null @@ -1,7 +0,0 @@ -prelude: | - def m - return 1 - end -benchmark: - vm1_simplereturn: m -loop_count: 30000000 diff --git a/test/testdata/ruby_benchmark/vm1_swap.yml b/test/testdata/ruby_benchmark/vm1_swap.yml deleted file mode 100644 index fed87ccd62..0000000000 --- a/test/testdata/ruby_benchmark/vm1_swap.yml +++ /dev/null @@ -1,7 +0,0 @@ -prelude: | - a = 1 - b = 2 -benchmark: - vm1_swap: | - a, b = b, a -loop_count: 30000000 diff --git a/test/testdata/ruby_benchmark/vm1_yield.yml b/test/testdata/ruby_benchmark/vm1_yield.yml deleted file mode 100644 index ae1f9316f9..0000000000 --- a/test/testdata/ruby_benchmark/vm1_yield.yml +++ /dev/null @@ -1,13 +0,0 @@ -# while loop cost is not removed due to benchmark_driver.gem's limitation -benchmark: - vm1_yield: | - def m - i = 0 - while i<30_000_000 - i += 1 - yield - end - end - - m{} -loop_count: 1 diff --git a/test/testdata/ruby_benchmark/vm2_array.yml b/test/testdata/ruby_benchmark/vm2_array.yml deleted file mode 100644 index 7373098d5e..0000000000 --- a/test/testdata/ruby_benchmark/vm2_array.yml +++ /dev/null @@ -1,4 +0,0 @@ -benchmark: - vm2_array: | - a = [1,2,3,4,5,6,7,8,9,10] -loop_count: 6000000 diff --git a/test/testdata/ruby_benchmark/vm2_bigarray.yml b/test/testdata/ruby_benchmark/vm2_bigarray.yml deleted file mode 100644 index 2ad6da3905..0000000000 --- a/test/testdata/ruby_benchmark/vm2_bigarray.yml +++ /dev/null @@ -1,105 +0,0 @@ -benchmark: - vm2_bigarray: | - a = [ - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - 1,2,3,4,5,6,7,8,9,10, - ] -loop_count: 6000000 diff --git a/test/testdata/ruby_benchmark/vm2_bighash.yml b/test/testdata/ruby_benchmark/vm2_bighash.yml deleted file mode 100644 index e9154e4ba9..0000000000 --- a/test/testdata/ruby_benchmark/vm2_bighash.yml +++ /dev/null @@ -1,4 +0,0 @@ -benchmark: - vm2_bighash: | - a = {0=>0, 1=>1, 2=>2, 3=>3, 4=>4, 5=>5, 6=>6, 7=>7, 8=>8, 9=>9, 10=>10, 11=>11, 12=>12, 13=>13, 14=>14, 15=>15, 16=>16, 17=>17, 18=>18, 19=>19, 20=>20, 21=>21, 22=>22, 23=>23, 24=>24, 25=>25, 26=>26, 27=>27, 28=>28, 29=>29, 30=>30, 31=>31, 32=>32, 33=>33, 34=>34, 35=>35, 36=>36, 37=>37, 38=>38, 39=>39, 40=>40, 41=>41, 42=>42, 43=>43, 44=>44, 45=>45, 46=>46, 47=>47, 48=>48, 49=>49, 50=>50, 51=>51, 52=>52, 53=>53, 54=>54, 55=>55, 56=>56, 57=>57, 58=>58, 59=>59, 60=>60, 61=>61, 62=>62, 63=>63, 64=>64, 65=>65, 66=>66, 67=>67, 68=>68, 69=>69, 70=>70, 71=>71, 72=>72, 73=>73, 74=>74, 75=>75, 76=>76, 77=>77, 78=>78, 79=>79, 80=>80, 81=>81, 82=>82, 83=>83, 84=>84, 85=>85, 86=>86, 87=>87, 88=>88, 89=>89, 90=>90, 91=>91, 92=>92, 93=>93, 94=>94, 95=>95, 96=>96, 97=>97, 98=>98, 99=>99, 100=>100, 101=>101, 102=>102, 103=>103, 104=>104, 105=>105, 106=>106, 107=>107, 108=>108, 109=>109, 110=>110, 111=>111, 112=>112, 113=>113, 114=>114, 115=>115, 116=>116, 117=>117, 118=>118, 119=>119, 120=>120, 121=>121, 122=>122, 123=>123, 124=>124, 125=>125, 126=>126, 127=>127, 128=>128, 129=>129, 130=>130, 131=>131, 132=>132, 133=>133, 134=>134, 135=>135, 136=>136, 137=>137, 138=>138, 139=>139, 140=>140, 141=>141, 142=>142, 143=>143, 144=>144, 145=>145, 146=>146, 147=>147, 148=>148, 149=>149, 150=>150, 151=>151, 152=>152, 153=>153, 154=>154, 155=>155, 156=>156, 157=>157, 158=>158, 159=>159, 160=>160, 161=>161, 162=>162, 163=>163, 164=>164, 165=>165, 166=>166, 167=>167, 168=>168, 169=>169, 170=>170, 171=>171, 172=>172, 173=>173, 174=>174, 175=>175, 176=>176, 177=>177, 178=>178, 179=>179, 180=>180, 181=>181, 182=>182, 183=>183, 184=>184, 185=>185, 186=>186, 187=>187, 188=>188, 189=>189, 190=>190, 191=>191, 192=>192, 193=>193, 194=>194, 195=>195, 196=>196, 197=>197, 198=>198, 199=>199, 200=>200, 201=>201, 202=>202, 203=>203, 204=>204, 205=>205, 206=>206, 207=>207, 208=>208, 209=>209, 210=>210, 211=>211, 212=>212, 213=>213, 214=>214, 215=>215, 216=>216, 217=>217, 218=>218, 219=>219, 220=>220, 221=>221, 222=>222, 223=>223, 224=>224, 225=>225, 226=>226, 227=>227, 228=>228, 229=>229, 230=>230, 231=>231, 232=>232, 233=>233, 234=>234, 235=>235, 236=>236, 237=>237, 238=>238, 239=>239, 240=>240, 241=>241, 242=>242, 243=>243, 244=>244, 245=>245, 246=>246, 247=>247, 248=>248, 249=>249, 250=>250, 251=>251, 252=>252, 253=>253, 254=>254, 255=>255, 256=>256, 257=>257, 258=>258, 259=>259, 260=>260, 261=>261, 262=>262, 263=>263, 264=>264, 265=>265, 266=>266, 267=>267, 268=>268, 269=>269, 270=>270, 271=>271, 272=>272, 273=>273, 274=>274, 275=>275, 276=>276, 277=>277, 278=>278, 279=>279, 280=>280, 281=>281, 282=>282, 283=>283, 284=>284, 285=>285, 286=>286, 287=>287, 288=>288, 289=>289, 290=>290, 291=>291, 292=>292, 293=>293, 294=>294, 295=>295, 296=>296, 297=>297, 298=>298, 299=>299, 300=>300, 301=>301, 302=>302, 303=>303, 304=>304, 305=>305, 306=>306, 307=>307, 308=>308, 309=>309, 310=>310, 311=>311, 312=>312, 313=>313, 314=>314, 315=>315, 316=>316, 317=>317, 318=>318, 319=>319, 320=>320, 321=>321, 322=>322, 323=>323, 324=>324, 325=>325, 326=>326, 327=>327, 328=>328, 329=>329, 330=>330, 331=>331, 332=>332, 333=>333, 334=>334, 335=>335, 336=>336, 337=>337, 338=>338, 339=>339, 340=>340, 341=>341, 342=>342, 343=>343, 344=>344, 345=>345, 346=>346, 347=>347, 348=>348, 349=>349, 350=>350, 351=>351, 352=>352, 353=>353, 354=>354, 355=>355, 356=>356, 357=>357, 358=>358, 359=>359, 360=>360, 361=>361, 362=>362, 363=>363, 364=>364, 365=>365, 366=>366, 367=>367, 368=>368, 369=>369, 370=>370, 371=>371, 372=>372, 373=>373, 374=>374, 375=>375, 376=>376, 377=>377, 378=>378, 379=>379, 380=>380, 381=>381, 382=>382, 383=>383, 384=>384, 385=>385, 386=>386, 387=>387, 388=>388, 389=>389, 390=>390, 391=>391, 392=>392, 393=>393, 394=>394, 395=>395, 396=>396, 397=>397, 398=>398, 399=>399, 400=>400, 401=>401, 402=>402, 403=>403, 404=>404, 405=>405, 406=>406, 407=>407, 408=>408, 409=>409, 410=>410, 411=>411, 412=>412, 413=>413, 414=>414, 415=>415, 416=>416, 417=>417, 418=>418, 419=>419, 420=>420, 421=>421, 422=>422, 423=>423, 424=>424, 425=>425, 426=>426, 427=>427, 428=>428, 429=>429, 430=>430, 431=>431, 432=>432, 433=>433, 434=>434, 435=>435, 436=>436, 437=>437, 438=>438, 439=>439, 440=>440, 441=>441, 442=>442, 443=>443, 444=>444, 445=>445, 446=>446, 447=>447, 448=>448, 449=>449, 450=>450, 451=>451, 452=>452, 453=>453, 454=>454, 455=>455, 456=>456, 457=>457, 458=>458, 459=>459, 460=>460, 461=>461, 462=>462, 463=>463, 464=>464, 465=>465, 466=>466, 467=>467, 468=>468, 469=>469, 470=>470, 471=>471, 472=>472, 473=>473, 474=>474, 475=>475, 476=>476, 477=>477, 478=>478, 479=>479, 480=>480, 481=>481, 482=>482, 483=>483, 484=>484, 485=>485, 486=>486, 487=>487, 488=>488, 489=>489, 490=>490, 491=>491, 492=>492, 493=>493, 494=>494, 495=>495, 496=>496, 497=>497, 498=>498, 499=>499, 500=>500,} -loop_count: 60000 diff --git a/test/testdata/ruby_benchmark/vm2_case.yml b/test/testdata/ruby_benchmark/vm2_case.yml deleted file mode 100644 index 7716783c09..0000000000 --- a/test/testdata/ruby_benchmark/vm2_case.yml +++ /dev/null @@ -1,13 +0,0 @@ -benchmark: - vm2_case: | - case :foo - when :bar - raise - when :baz - raise - when :boo - raise - when :foo - # noop - end -loop_count: 6000000 diff --git a/test/testdata/ruby_benchmark/vm2_case_lit.yml b/test/testdata/ruby_benchmark/vm2_case_lit.yml deleted file mode 100644 index c49b8dfe5e..0000000000 --- a/test/testdata/ruby_benchmark/vm2_case_lit.yml +++ /dev/null @@ -1,23 +0,0 @@ -# loop_count is not utilized since `i` is involved in the script -benchmark: - vm2_case_lit: | - i = 0 - @ret = [ "foo", true, false, :sym, 6, nil, 0.1, 0xffffffffffffffff ] - def foo(i) - @ret[i % @ret.size] - end - - while i<6_000_000 - case foo(i) - when "foo" then :foo - when true then true - when false then false - when :sym then :sym - when 6 then :fix - when nil then nil - when 0.1 then :float - when 0xffffffffffffffff then :big - end - i += 1 - end -loop_count: 1 diff --git a/test/testdata/ruby_benchmark/vm2_defined_method.yml b/test/testdata/ruby_benchmark/vm2_defined_method.yml deleted file mode 100644 index e1b0d55674..0000000000 --- a/test/testdata/ruby_benchmark/vm2_defined_method.yml +++ /dev/null @@ -1,8 +0,0 @@ -prelude: | - class Object - define_method(:m){} - end -benchmark: - vm2_defined_method: | - m; m; m; m; m; m; m; m; -loop_count: 6000000 diff --git a/test/testdata/ruby_benchmark/vm2_dstr.yml b/test/testdata/ruby_benchmark/vm2_dstr.yml deleted file mode 100644 index f8bd6e0133..0000000000 --- a/test/testdata/ruby_benchmark/vm2_dstr.yml +++ /dev/null @@ -1,6 +0,0 @@ -prelude: | - x = y = 'z' -benchmark: - vm2_dstr: | - str = "foo#{x}bar#{y}baz" -loop_count: 6000000 diff --git a/test/testdata/ruby_benchmark/vm2_eval.yml b/test/testdata/ruby_benchmark/vm2_eval.yml deleted file mode 100644 index d506a9c079..0000000000 --- a/test/testdata/ruby_benchmark/vm2_eval.yml +++ /dev/null @@ -1,4 +0,0 @@ -benchmark: - vm2_eval: | - eval("1") -loop_count: 6000000 diff --git a/test/testdata/ruby_benchmark/vm2_fiber_switch.yml b/test/testdata/ruby_benchmark/vm2_fiber_switch.yml deleted file mode 100644 index f3e4c91283..0000000000 --- a/test/testdata/ruby_benchmark/vm2_fiber_switch.yml +++ /dev/null @@ -1,9 +0,0 @@ -prelude: | - # based on benchmark for [ruby-core:65518] [Feature #10341] by Knut Franke - fib = Fiber.new do - loop { Fiber.yield } - end -benchmark: - vm2_fiber_switch: | - fib.resume -loop_count: 6000000 diff --git a/test/testdata/ruby_benchmark/vm2_freezestring.yml b/test/testdata/ruby_benchmark/vm2_freezestring.yml deleted file mode 100644 index b78af91a20..0000000000 --- a/test/testdata/ruby_benchmark/vm2_freezestring.yml +++ /dev/null @@ -1,10 +0,0 @@ -prelude: | - class String - def freeze - -self - end - end -benchmark: - vm2_freezestring: | - "tXnL1BP5T1WPXMjuFNLQtallEtRcay1t2lHtJSrlVsDgvunlbtfpr/DGdH0NGYE9".freeze -loop_count: 6000000 diff --git a/test/testdata/ruby_benchmark/vm2_method.yml b/test/testdata/ruby_benchmark/vm2_method.yml deleted file mode 100644 index cc7b9b28ff..0000000000 --- a/test/testdata/ruby_benchmark/vm2_method.yml +++ /dev/null @@ -1,8 +0,0 @@ -prelude: | - def m - nil - end -benchmark: - vm2_method: | - m; m; m; m; m; m; m; m; -loop_count: 6000000 diff --git a/test/testdata/ruby_benchmark/vm2_method_missing.yml b/test/testdata/ruby_benchmark/vm2_method_missing.yml deleted file mode 100644 index cbfb794b25..0000000000 --- a/test/testdata/ruby_benchmark/vm2_method_missing.yml +++ /dev/null @@ -1,11 +0,0 @@ -prelude: | - class C - def method_missing mid - end - end - - obj = C.new -benchmark: - vm2_method_missing: | - obj.m; obj.m; obj.m; obj.m; obj.m; obj.m; obj.m; obj.m; -loop_count: 6000000 diff --git a/test/testdata/ruby_benchmark/vm2_method_with_block.yml b/test/testdata/ruby_benchmark/vm2_method_with_block.yml deleted file mode 100644 index 6e522adccc..0000000000 --- a/test/testdata/ruby_benchmark/vm2_method_with_block.yml +++ /dev/null @@ -1,8 +0,0 @@ -prelude: | - def m - nil - end -benchmark: - vm2_method_with_block: | - m{}; m{}; m{}; m{}; m{}; m{}; m{}; m{}; -loop_count: 6000000 diff --git a/test/testdata/ruby_benchmark/vm2_module_ann_const_set.yml b/test/testdata/ruby_benchmark/vm2_module_ann_const_set.yml deleted file mode 100644 index b0becd9d3d..0000000000 --- a/test/testdata/ruby_benchmark/vm2_module_ann_const_set.yml +++ /dev/null @@ -1,4 +0,0 @@ -benchmark: - vm2_module_ann_const_set: | - Module.new.const_set(:X, Module.new) -loop_count: 6000000 diff --git a/test/testdata/ruby_benchmark/vm2_module_const_set.yml b/test/testdata/ruby_benchmark/vm2_module_const_set.yml deleted file mode 100644 index 05a640069c..0000000000 --- a/test/testdata/ruby_benchmark/vm2_module_const_set.yml +++ /dev/null @@ -1,8 +0,0 @@ -prelude: | - module M - end - $VERBOSE = nil -benchmark: - vm2_module_const_set: | - M.const_set(:X, Module.new) -loop_count: 6000000 diff --git a/test/testdata/ruby_benchmark/vm2_mutex.yml b/test/testdata/ruby_benchmark/vm2_mutex.yml deleted file mode 100644 index c40a90444a..0000000000 --- a/test/testdata/ruby_benchmark/vm2_mutex.yml +++ /dev/null @@ -1,8 +0,0 @@ -prelude: | - require 'thread' - - m = Thread::Mutex.new -benchmark: - vm2_mutex: | - m.synchronize{} -loop_count: 6000000 diff --git a/test/testdata/ruby_benchmark/vm2_newlambda.yml b/test/testdata/ruby_benchmark/vm2_newlambda.yml deleted file mode 100644 index 93133f9f30..0000000000 --- a/test/testdata/ruby_benchmark/vm2_newlambda.yml +++ /dev/null @@ -1,4 +0,0 @@ -benchmark: - vm2_newlambda: | - lambda {} -loop_count: 6000000 diff --git a/test/testdata/ruby_benchmark/vm2_poly_method.yml b/test/testdata/ruby_benchmark/vm2_poly_method.yml deleted file mode 100644 index 0104bdfb66..0000000000 --- a/test/testdata/ruby_benchmark/vm2_poly_method.yml +++ /dev/null @@ -1,24 +0,0 @@ -# loop_count is not utilized since `i` is involved in the script -benchmark: - vm2_poly_method: | - class C1 - def m - 1 - end - end - class C2 - def m - 2 - end - end - - o1 = C1.new - o2 = C2.new - - i = 0 - while i<6_000_000 - o = (i % 2 == 0) ? o1 : o2 - o.m; o.m; o.m; o.m; o.m; o.m; o.m; o.m - i += 1 - end -loop_count: 1 diff --git a/test/testdata/ruby_benchmark/vm2_poly_method_ov.yml b/test/testdata/ruby_benchmark/vm2_poly_method_ov.yml deleted file mode 100644 index 3748073ba2..0000000000 --- a/test/testdata/ruby_benchmark/vm2_poly_method_ov.yml +++ /dev/null @@ -1,24 +0,0 @@ -# loop_count is not utilized since `i` is involved in the script -benchmark: - vm2_poly_method_ov: | - class C1 - def m - 1 - end - end - class C2 - def m - 2 - end - end - - o1 = C1.new - o2 = C2.new - - i = 0 - while i<6_000_000 - o = (i % 2 == 0) ? o1 : o2 - # o.m; o.m; o.m; o.m; o.m; o.m; o.m; o.m - i += 1 - end -loop_count: 1 diff --git a/test/testdata/ruby_benchmark/vm2_poly_singleton.yml b/test/testdata/ruby_benchmark/vm2_poly_singleton.yml deleted file mode 100644 index e58d7bfb37..0000000000 --- a/test/testdata/ruby_benchmark/vm2_poly_singleton.yml +++ /dev/null @@ -1,18 +0,0 @@ -# loop_count is not utilized since `i` is involved in the script -benchmark: - vm2_poly_singleton: | - class C1 - def m; 1; end - end - - o1 = C1.new - o2 = C1.new - o2.singleton_class - - i = 0 - while i<6_000_000 # benchmark loop 2 - o = (i % 2 == 0) ? o1 : o2 - o.m; o.m; o.m; o.m; o.m; o.m; o.m; o.m - i += 1 - end -loop_count: 1 diff --git a/test/testdata/ruby_benchmark/vm2_proc.yml b/test/testdata/ruby_benchmark/vm2_proc.yml deleted file mode 100644 index 5c36e936d9..0000000000 --- a/test/testdata/ruby_benchmark/vm2_proc.yml +++ /dev/null @@ -1,12 +0,0 @@ -prelude: | - def m &b - b - end - - pr = m{ - a = 1 - } -benchmark: - vm2_proc: | - pr.call -loop_count: 6000000 diff --git a/test/testdata/ruby_benchmark/vm2_raise1.yml b/test/testdata/ruby_benchmark/vm2_raise1.yml deleted file mode 100644 index f6eb308968..0000000000 --- a/test/testdata/ruby_benchmark/vm2_raise1.yml +++ /dev/null @@ -1,16 +0,0 @@ -prelude: | - def rec n - if n > 0 - rec n-1 - else - raise - end - end -benchmark: - vm2_raise1: | - begin - rec 1 - rescue - # ignore - end -loop_count: 6000000 diff --git a/test/testdata/ruby_benchmark/vm2_raise2.yml b/test/testdata/ruby_benchmark/vm2_raise2.yml deleted file mode 100644 index 7d51b1b314..0000000000 --- a/test/testdata/ruby_benchmark/vm2_raise2.yml +++ /dev/null @@ -1,16 +0,0 @@ -prelude: | - def rec n - if n > 0 - rec n-1 - else - raise - end - end -benchmark: - vm2_raise2: | - begin - rec 10 - rescue - # ignore - end -loop_count: 6000000 diff --git a/test/testdata/ruby_benchmark/vm2_regexp.yml b/test/testdata/ruby_benchmark/vm2_regexp.yml deleted file mode 100644 index 0f3968a99b..0000000000 --- a/test/testdata/ruby_benchmark/vm2_regexp.yml +++ /dev/null @@ -1,6 +0,0 @@ -prelude: | - str = 'xxxhogexxx' -benchmark: - vm2_regexp: | - /hoge/ =~ str -loop_count: 6000000 diff --git a/test/testdata/ruby_benchmark/vm2_send.yml b/test/testdata/ruby_benchmark/vm2_send.yml deleted file mode 100644 index 44a12a27d9..0000000000 --- a/test/testdata/ruby_benchmark/vm2_send.yml +++ /dev/null @@ -1,11 +0,0 @@ -prelude: | - class C - def m - end - end - - o = C.new -benchmark: - vm2_send: | - o.__send__ :m -loop_count: 6000000 diff --git a/test/testdata/ruby_benchmark/vm2_string_literal.yml b/test/testdata/ruby_benchmark/vm2_string_literal.yml deleted file mode 100644 index 54b0aec1fe..0000000000 --- a/test/testdata/ruby_benchmark/vm2_string_literal.yml +++ /dev/null @@ -1,4 +0,0 @@ -benchmark: - vm2_string_literal: | - x = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" -loop_count: 6000000 diff --git a/test/testdata/ruby_benchmark/vm2_struct_big_aref_hi.yml b/test/testdata/ruby_benchmark/vm2_struct_big_aref_hi.yml deleted file mode 100644 index eed1846d28..0000000000 --- a/test/testdata/ruby_benchmark/vm2_struct_big_aref_hi.yml +++ /dev/null @@ -1,7 +0,0 @@ -prelude: | - s = Struct.new(*('a'..'z').map { |x| x.to_sym }) - x = s.new -benchmark: - vm2_struct_big_aref_hi: | - x.z # x[25] -loop_count: 6000000 diff --git a/test/testdata/ruby_benchmark/vm2_struct_big_aref_lo.yml b/test/testdata/ruby_benchmark/vm2_struct_big_aref_lo.yml deleted file mode 100644 index 0915435b76..0000000000 --- a/test/testdata/ruby_benchmark/vm2_struct_big_aref_lo.yml +++ /dev/null @@ -1,7 +0,0 @@ -prelude: | - s = Struct.new(*('a'..'z').map { |x| x.to_sym }) - x = s.new -benchmark: - vm2_struct_big_aref_lo: | - x.k # x[10] -loop_count: 6000000 diff --git a/test/testdata/ruby_benchmark/vm2_struct_big_aset.yml b/test/testdata/ruby_benchmark/vm2_struct_big_aset.yml deleted file mode 100644 index 6af50103d3..0000000000 --- a/test/testdata/ruby_benchmark/vm2_struct_big_aset.yml +++ /dev/null @@ -1,11 +0,0 @@ -# loop_count is not utilized since `i` is involved in the script -benchmark: - vm2_struct_big_aset: | - s = Struct.new(*('a'..'z').map { |x| x.to_sym }) - x = s.new - i = 0 - while i<6_000_000 - i += 1 - x.k = i # x[10] = i - end -loop_count: 1 diff --git a/test/testdata/ruby_benchmark/vm2_struct_big_href_hi.yml b/test/testdata/ruby_benchmark/vm2_struct_big_href_hi.yml deleted file mode 100644 index 60aa7fddf3..0000000000 --- a/test/testdata/ruby_benchmark/vm2_struct_big_href_hi.yml +++ /dev/null @@ -1,7 +0,0 @@ -prelude: | - s = Struct.new(*('a'..'z').map { |x| x.to_sym }) - x = s.new -benchmark: - vm2_struct_big_href_hi: | - x[:z] -loop_count: 6000000 diff --git a/test/testdata/ruby_benchmark/vm2_struct_big_href_lo.yml b/test/testdata/ruby_benchmark/vm2_struct_big_href_lo.yml deleted file mode 100644 index c55c0bd16c..0000000000 --- a/test/testdata/ruby_benchmark/vm2_struct_big_href_lo.yml +++ /dev/null @@ -1,7 +0,0 @@ -prelude: | - s = Struct.new(*('a'..'z').map { |x| x.to_sym }) - x = s.new -benchmark: - vm2_struct_big_href_lo: | - x[:k] -loop_count: 6000000 diff --git a/test/testdata/ruby_benchmark/vm2_struct_big_hset.yml b/test/testdata/ruby_benchmark/vm2_struct_big_hset.yml deleted file mode 100644 index d199c5bd47..0000000000 --- a/test/testdata/ruby_benchmark/vm2_struct_big_hset.yml +++ /dev/null @@ -1,11 +0,0 @@ -# loop_count is not utilized since `i` is involved in the script -benchmark: - vm2_struct_big_hset: | - s = Struct.new(*('a'..'z').map { |x| x.to_sym }) - x = s.new - i = 0 - while i<6_000_000 - i += 1 - x[:k] = i - end -loop_count: 1 diff --git a/test/testdata/ruby_benchmark/vm2_struct_small_aref.yml b/test/testdata/ruby_benchmark/vm2_struct_small_aref.yml deleted file mode 100644 index 83381bed3a..0000000000 --- a/test/testdata/ruby_benchmark/vm2_struct_small_aref.yml +++ /dev/null @@ -1,7 +0,0 @@ -prelude: | - s = Struct.new(:a, :b, :c) - x = s.new -benchmark: - vm2_struct_small_aref: | - x.a -loop_count: 6000000 diff --git a/test/testdata/ruby_benchmark/vm2_struct_small_aset.yml b/test/testdata/ruby_benchmark/vm2_struct_small_aset.yml deleted file mode 100644 index 3e84a61dd0..0000000000 --- a/test/testdata/ruby_benchmark/vm2_struct_small_aset.yml +++ /dev/null @@ -1,11 +0,0 @@ -# loop_count is not utilized since `i` is involved in the script -benchmark: - vm2_struct_small_aset: | - s = Struct.new(:a, :b, :c) - x = s.new - i = 0 - while i<6_000_000 - i += 1 - x.a = i - end -loop_count: 1 diff --git a/test/testdata/ruby_benchmark/vm2_struct_small_href.yml b/test/testdata/ruby_benchmark/vm2_struct_small_href.yml deleted file mode 100644 index b744f070d1..0000000000 --- a/test/testdata/ruby_benchmark/vm2_struct_small_href.yml +++ /dev/null @@ -1,7 +0,0 @@ -prelude: | - s = Struct.new(:a, :b, :c) - x = s.new -benchmark: - vm2_struct_small_href: | - x[:a] -loop_count: 6000000 diff --git a/test/testdata/ruby_benchmark/vm2_struct_small_hset.yml b/test/testdata/ruby_benchmark/vm2_struct_small_hset.yml deleted file mode 100644 index d43845d6e0..0000000000 --- a/test/testdata/ruby_benchmark/vm2_struct_small_hset.yml +++ /dev/null @@ -1,7 +0,0 @@ -prelude: | - s = Struct.new(:a, :b, :c) - x = s.new -benchmark: - vm2_struct_small_hset: | - x[:a] = 1 -loop_count: 6000000 diff --git a/test/testdata/ruby_benchmark/vm2_super.yml b/test/testdata/ruby_benchmark/vm2_super.yml deleted file mode 100644 index 674743762a..0000000000 --- a/test/testdata/ruby_benchmark/vm2_super.yml +++ /dev/null @@ -1,17 +0,0 @@ -prelude: | - class C - def m - 1 - end - end - - class CC < C - def m - super() - end - end - - obj = CC.new -benchmark: - vm2_super: obj.m -loop_count: 6000000 diff --git a/test/testdata/ruby_benchmark/vm2_unif1.yml b/test/testdata/ruby_benchmark/vm2_unif1.yml deleted file mode 100644 index caef13279f..0000000000 --- a/test/testdata/ruby_benchmark/vm2_unif1.yml +++ /dev/null @@ -1,7 +0,0 @@ -prelude: | - def m a, b - end -benchmark: - vm2_unif1: | - m 100, 200 -loop_count: 6000000 diff --git a/test/testdata/ruby_benchmark/vm2_zsuper.yml b/test/testdata/ruby_benchmark/vm2_zsuper.yml deleted file mode 100644 index f760cfd48e..0000000000 --- a/test/testdata/ruby_benchmark/vm2_zsuper.yml +++ /dev/null @@ -1,18 +0,0 @@ -prelude: | - class C - def m a - 1 - end - end - - class CC < C - def m a - super - end - end - - obj = CC.new -benchmark: - vm2_zsuper: | - obj.m 10 -loop_count: 6000000 diff --git a/test/testdata/ruby_benchmark/vm3_gc_old_immediate.rb b/test/testdata/ruby_benchmark/vm3_gc_old_immediate.rb deleted file mode 100644 index 68de5d36b9..0000000000 --- a/test/testdata/ruby_benchmark/vm3_gc_old_immediate.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true -# typed: strong -# compiled: true -old_object = Array.new(1_000_000){''} -30_000.times do - GC.start(full_mark: false, immediate_sweep: true) -end diff --git a/test/testdata/ruby_benchmark/vm3_gc_old_lazy.rb b/test/testdata/ruby_benchmark/vm3_gc_old_lazy.rb deleted file mode 100644 index 3d35e8fd68..0000000000 --- a/test/testdata/ruby_benchmark/vm3_gc_old_lazy.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true -# typed: strong -# compiled: true -old_object = Array.new(1_000_000){''} -30_000.times do - GC.start(full_mark: false, immediate_sweep: false) -end diff --git a/test/testdata/ruby_benchmark/vm_symbol_block_pass.rb b/test/testdata/ruby_benchmark/vm_symbol_block_pass.rb deleted file mode 100644 index 9351716858..0000000000 --- a/test/testdata/ruby_benchmark/vm_symbol_block_pass.rb +++ /dev/null @@ -1,16 +0,0 @@ -# frozen_string_literal: true -# typed: true -# compiled: true -class C - 1000.times {|i| - eval("def i#{i};end") - } -end - -c = C.new -m = C.instance_methods(false) -5_000.times do - m.each do |n| - c.tap(&n) - end -end diff --git a/test/testdata/ruby_benchmark/vm_thread_alive_check1.rb b/test/testdata/ruby_benchmark/vm_thread_alive_check1.rb deleted file mode 100644 index dbcc91754e..0000000000 --- a/test/testdata/ruby_benchmark/vm_thread_alive_check1.rb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true -# typed: strict -# compiled: true -5_000.times{ - t = Thread.new{} - while t.alive? - Thread.pass - end -} diff --git a/test/testdata/ruby_benchmark/vm_thread_close.rb b/test/testdata/ruby_benchmark/vm_thread_close.rb deleted file mode 100644 index bdc94e6eb5..0000000000 --- a/test/testdata/ruby_benchmark/vm_thread_close.rb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true -# typed: strong -# compiled: true -1000.times { Thread.new { sleep } } -i = 0 -while i<100_000 # benchmark loop 3 - i += 1 - IO.pipe.each(&:close) -end diff --git a/test/testdata/ruby_benchmark/vm_thread_create_join.rb b/test/testdata/ruby_benchmark/vm_thread_create_join.rb deleted file mode 100644 index 3608a02a0a..0000000000 --- a/test/testdata/ruby_benchmark/vm_thread_create_join.rb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true -# typed: strong -# compiled: true -i = 0 -while i<100_000 # benchmark loop 3 - i += 1 - Thread.new{ - }.join -end diff --git a/test/testdata/ruby_benchmark/vm_thread_mutex1.rb b/test/testdata/ruby_benchmark/vm_thread_mutex1.rb deleted file mode 100644 index ec8987608a..0000000000 --- a/test/testdata/ruby_benchmark/vm_thread_mutex1.rb +++ /dev/null @@ -1,24 +0,0 @@ -# frozen_string_literal: true -# typed: strong -# compiled: true -# one thread, one mutex (no contention) - -require 'thread' -m = Thread::Mutex.new -r = 0 -max = 2000 -lmax = max * max -(1..1).map{ - Thread.new{ - i = 0 - while i 0 - q.push true - n -= 1 - end - q.push nil -} - -consumer.join diff --git a/test/testdata/ruby_benchmark/vm_thread_sized_queue2.rb b/test/testdata/ruby_benchmark/vm_thread_sized_queue2.rb deleted file mode 100644 index 069d03d564..0000000000 --- a/test/testdata/ruby_benchmark/vm_thread_sized_queue2.rb +++ /dev/null @@ -1,26 +0,0 @@ -# frozen_string_literal: true -# typed: strict -# compiled: true -require 'thread' -# one producer, many consumers -n = 1_000_000 -m = 10 -q = Thread::SizedQueue.new(100) -consumers = m.times.map do - Thread.new do - while q.pop - # consuming - end - end -end - -producer = Thread.new do - while n > 0 - q.push true - n -= 1 - end - m.times { q.push nil } -end - -producer.join -consumers.each(&:join) diff --git a/test/testdata/ruby_benchmark/vm_thread_sized_queue3.rb b/test/testdata/ruby_benchmark/vm_thread_sized_queue3.rb deleted file mode 100644 index e53802eb74..0000000000 --- a/test/testdata/ruby_benchmark/vm_thread_sized_queue3.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true -# typed: strict -# compiled: true -require 'thread' -# many producers, one consumer -n = 1_000_000 -m = 10 -q = Thread::SizedQueue.new(100) -consumer = Thread.new do - while q.pop - # consuming - end -end - -producers = m.times.map do - Thread.new do - while n > 0 - q.push true - n -= 1 - end - end -end -producers.each(&:join) -q.push nil -consumer.join diff --git a/test/testdata/ruby_benchmark/vm_thread_sized_queue4.rb b/test/testdata/ruby_benchmark/vm_thread_sized_queue4.rb deleted file mode 100644 index 6d30612e80..0000000000 --- a/test/testdata/ruby_benchmark/vm_thread_sized_queue4.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true -# typed: strict -# compiled: true -require 'thread' -# many producers, many consumers -nr = 1_000_000 -n = 10 -m = 10 -q = Thread::SizedQueue.new(100) -consumers = n.times.map do - Thread.new do - while q.pop - # consuming - end - end -end - -producers = m.times.map do - Thread.new do - while nr > 0 - q.push true - nr -= 1 - end - end -end - -producers.each(&:join) -n.times { q.push nil } -consumers.each(&:join) diff --git a/test/testdata/substitutions/double_subsitutions.rb b/test/testdata/substitutions/double_substitutions.rb similarity index 100% rename from test/testdata/substitutions/double_subsitutions.rb rename to test/testdata/substitutions/double_substitutions.rb diff --git a/test/testdata/todo/block_in_class.rb.flatten-tree.exp b/test/testdata/todo/block_in_class.rb.flatten-tree.exp index 103e559a44..0d6c22a63b 100644 --- a/test/testdata/todo/block_in_class.rb.flatten-tree.exp +++ b/test/testdata/todo/block_in_class.rb.flatten-tree.exp @@ -1,12 +1,9 @@ begin - class <>> < (::) + + def self.<$CENSORED>() - begin - - ::Sorbet::Private::Static.keep_for_ide(::C) - - end + end end class ::C<> < (::) diff --git a/test/validate_exp.sh b/test/validate_exp.sh deleted file mode 100755 index 2a23544a7d..0000000000 --- a/test/validate_exp.sh +++ /dev/null @@ -1,128 +0,0 @@ -#!/bin/bash - -set -euo pipefail - -# --- begin runfiles.bash initialization --- {{{ -# Copy-pasted from Bazel's Bash runfiles library https://github.com/bazelbuild/bazel/blob/defd737761be2b154908646121de47c30434ed51/tools/bash/runfiles/runfiles.bash -if [[ ! -d "${RUNFILES_DIR:-/dev/null}" && ! -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then - if [[ -f "$0.runfiles_manifest" ]]; then - export RUNFILES_MANIFEST_FILE="$0.runfiles_manifest" - elif [[ -f "$0.runfiles/MANIFEST" ]]; then - export RUNFILES_MANIFEST_FILE="$0.runfiles/MANIFEST" - elif [[ -f "$0.runfiles/bazel_tools/tools/bash/runfiles/runfiles.bash" ]]; then - export RUNFILES_DIR="$0.runfiles" - fi -fi -if [[ -f "${RUNFILES_DIR:-/dev/null}/bazel_tools/tools/bash/runfiles/runfiles.bash" ]]; then - # shellcheck disable=SC1090,SC1091 - source "${RUNFILES_DIR}/bazel_tools/tools/bash/runfiles/runfiles.bash" -elif [[ -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then - # shellcheck disable=SC1090 - source "$(grep -m1 "^bazel_tools/tools/bash/runfiles/runfiles.bash " \ - "$RUNFILES_MANIFEST_FILE" | cut -d ' ' -f 2-)" -else - echo >&2 "ERROR: cannot find @bazel_tools//tools/bash/runfiles:runfiles.bash" - exit 1 -fi -# --- end runfiles.bash initialization --- }}} - -# shellcheck source-path=SCRIPTDIR/.. -source "test/logging.sh" - -# Argument Parsing ############################################################# - -# Positional arguments -build_dir=${1/--build_dir=/} -expected_failure= -case "${2/--expected_failure=/}" in - True) expected_failure=1;; - False);; - *) fatal "Expected --expected_failure=(True|False), got $2";; -esac -shift 2 - -# sources make up the remaining argumenets -rb=( "$@" ) - -# Environment Setup ############################################################ - -root=$PWD - -llvm_diff_path="$root/external/llvm_toolchain_12_0_0/bin/llvm-diff" - -ruby="$(rlocation sorbet_ruby_2_7_for_compiler/ruby)" -diff_diff="$(rlocation com_stripe_ruby_typer/test/diff-diff.rb)" - -diff_dir="$(mktemp -d)" - -cleanup() { - rm -rf "$diff_dir" -} -trap cleanup EXIT - -# Main ######################################################################### - -info "Checking Build:" -pushd "$build_dir/" > /dev/null - -something_failed() { - if [ -n "${expected_failure}" ]; then - success "Disabled test failed as expected." - info "* $1" - echo "" - info "To make this failing test fail the build, move it out of the disabled folder" - echo "" - exit 0 - else - echo "" - error "Test failed:" - error "* $1" - echo "" - exit 1 - fi -} - -exts=("opt.ll") -for ext in "${exts[@]}"; do - exp="$root/${rb[0]%.rb}.$ext.exp" - if [ -f "$exp" ]; then - actual="${rb[0]}.$ext" - if [ ! -f "$actual" ]; then - fatal "No LLVMIR found at" "$actual" - fi - if [[ "$OSTYPE" != "darwin"* ]]; then - diff_out="${diff_dir}/${ext}.diff" - - # NOTE: because of https://bugs.llvm.org/show_bug.cgi?id=48137, we pipe - # the output of llvm-diff through the diff-diff.rb script to clean out - # spurious errors from its output. This will also take over returning the - # correct exit code if it discovers that all of the differences were - # related to the bug. - if ($llvm_diff_path "$exp" "$actual" 2>&1 || true) | "$ruby" "$diff_diff" > "$diff_out" ; then - if grep "exists only in" "$diff_out" > /dev/null ; then - cat "$diff_out" - info "If this was an expected difference, you need to run tools/scripts/update_compiler_exp.sh" - something_failed "$(basename "$exp")" - else - if [ -n "${expected_failure}" ]; then - echo "" - error "Disabled test did not fail." - info "This could mean that a recent change has made this test start passing." - info "If that's the case, great! Please move this test out of the disabled folder to catch future regressions." - echo "" - exit 1 - else - success "* $(basename "$exp")" - fi - fi - else - cat "$diff_out" - info "If this was an expected difference, you need to run tools/scripts/update_compiler_exp.sh" - something_failed "$(basename "$exp")" - fi - fi - fi -done -popd > /dev/null - -success "Test passed" diff --git a/third_party/blake2.BUILD b/third_party/blake2.BUILD index 45d4390e8b..2f0b09a846 100644 --- a/third_party/blake2.BUILD +++ b/third_party/blake2.BUILD @@ -1,19 +1,39 @@ # note: this isn't the blake2 used in prod builds -# this one is only used in webasm mode +# this one is only used in webasm/aarch64 mode # prod builds use libb2 cc_library( - name = "com_github_blake2_blake2", + name = "com_github_blake2_blake2_ref", srcs = [ "ref/blake2s-ref.c", "ref/blake2b-ref.c", - ] + glob(["src/*.h"]), + ] + glob(["ref/*.h"]), hdrs = [ "ref/blake2.h", "ref/blake2-impl.h", ], includes = [ - "src", + "ref", + ], + linkstatic = select({ + "@com_stripe_ruby_typer//tools/config:linkshared": 0, + "//conditions:default": 1, + }), + visibility = ["//visibility:public"], +) + +cc_library( + name = "com_github_blake2_blake2_neon", + srcs = [ + "neon/blake2b-neon.c", + "neon/blake2s-neon.c", + ] + glob(["neon/*.h"]), + hdrs = [ + "neon/blake2.h", + "neon/blake2-impl.h", + ], + includes = [ + "neon", ], linkstatic = select({ "@com_stripe_ruby_typer//tools/config:linkshared": 0, diff --git a/third_party/com_google_protobuf/cpp_opts.bzl.patch b/third_party/com_google_protobuf/cpp_opts.bzl.patch new file mode 100644 index 0000000000..1e6fb5a0fa --- /dev/null +++ b/third_party/com_google_protobuf/cpp_opts.bzl.patch @@ -0,0 +1,12 @@ +diff --git build_defs/cpp_opts.bzl build_defs/cpp_opts.bzl +index f667a4088..160ec467f 100644 +--- build_defs/cpp_opts.bzl ++++ build_defs/cpp_opts.bzl +@@ -22,6 +22,7 @@ COPTS = select({ + "-Woverloaded-virtual", + "-Wno-sign-compare", + "-Wno-nonnull", ++ "-Wno-missing-field-initializers", + "-Werror", + ], + }) diff --git a/third_party/emscripten-clang.BUILD b/third_party/emscripten-clang.BUILD deleted file mode 100644 index b8eb018737..0000000000 --- a/third_party/emscripten-clang.BUILD +++ /dev/null @@ -1,6 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -filegroup( - name = "all", - srcs = glob(["**/*"]), -) diff --git a/third_party/emscripten-toolchain.BUILD b/third_party/emscripten-toolchain.BUILD deleted file mode 100644 index b8eb018737..0000000000 --- a/third_party/emscripten-toolchain.BUILD +++ /dev/null @@ -1,6 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -filegroup( - name = "all", - srcs = glob(["**/*"]), -) diff --git a/third_party/emscripten_toolchain/emcc.py.patch b/third_party/emscripten_toolchain/emcc.py.patch deleted file mode 100644 index 7e3a9e821c..0000000000 --- a/third_party/emscripten_toolchain/emcc.py.patch +++ /dev/null @@ -1,20 +0,0 @@ ---- emcc.py -+++ emcc.py -@@ -789,7 +789,7 @@ - newargs[i] = newargs[i + 1] = '' - if key == 'WASM_BACKEND=1': - exit_with_error('do not set -s WASM_BACKEND, instead set EMCC_WASM_BACKEND=1 in the environment') -- newargs = [arg for arg in newargs if arg is not ''] -+ newargs = [arg for arg in newargs if arg != ''] - - settings_key_changes = set() - for s in settings_changes: -@@ -900,7 +900,7 @@ - - original_input_files = input_files[:] - -- newargs = [a for a in newargs if a is not ''] -+ newargs = [a for a in newargs if a != ''] - - # -c means do not link in gcc, and for us, the parallel is to not go all the way to JS, but stop at bitcode - has_dash_c = '-c' in newargs diff --git a/third_party/emscripten_toolchain/tools_shared.py.patch b/third_party/emscripten_toolchain/tools_shared.py.patch deleted file mode 100644 index dac19e1bad..0000000000 --- a/third_party/emscripten_toolchain/tools_shared.py.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- tools/shared.py -+++ tools/shared.py -@@ -1658,7 +1658,7 @@ - raise - if 'EMMAKEN_JUST_CONFIGURE' in env: - del env['EMMAKEN_JUST_CONFIGURE'] -- if res.returncode is not 0: -+ if res.returncode != 0: - logger.error('Configure step failed with non-zero return code: %s. Command line: %s at %s' % (res.returncode, ' '.join(args), os.getcwd())) - raise subprocess.CalledProcessError(cmd=args, returncode=res.returncode) - diff --git a/third_party/emsdk/emscripten_config.patch b/third_party/emsdk/emscripten_config.patch new file mode 100644 index 0000000000..72ec56b4cc --- /dev/null +++ b/third_party/emsdk/emscripten_config.patch @@ -0,0 +1,21 @@ +diff --git emscripten_toolchain/emscripten_config emscripten_toolchain/emscripten_config +index 648a8fe..0b7688f 100644 +--- emscripten_toolchain/emscripten_config ++++ emscripten_toolchain/emscripten_config +@@ -9,7 +9,15 @@ FROZEN_CACHE = True + + system = platform.system() + +-machine = "arm64" if platform.machine() in ('arm64', 'aarch64') else "amd64" ++# TODO(jez) We can remove this hack when we use the arm64 version of Bazel on ++# macOS to drive our build (right now we always use the x86_64 version of Bazel ++# to drive the macOS build). ++if system == "Darwin": ++ machine = "arm64" ++elif platform.machine() in ('arm64', 'aarch64'): ++ machine = "arm64" ++else: ++ machine = "amd64" + nodejs_binary = "bin/nodejs/node.exe" if(system =="Windows") else "bin/node" + NODE_JS = ROOT_DIR + "/external/nodejs_{}_{}/{}".format(system.lower(), machine, nodejs_binary) + diff --git a/third_party/externals.bzl b/third_party/externals.bzl index 4b297eb7a4..1fab0a7225 100644 --- a/third_party/externals.bzl +++ b/third_party/externals.bzl @@ -5,26 +5,22 @@ load("//third_party:test_gem_data.bzl", "gem_build_info") # We define our externals here instead of directly in WORKSPACE def register_sorbet_dependencies(): - # At some point the builtin @platforms package will be removed, and we'll no longer be able to refer to - # @platforms//os:macos etc. The long-term workaround for this is to depend directly on bazelbuild/platforms as - # @platforms. See https://github.com/bazelbuild/bazel/issues/8622 for more information. http_archive( name = "platforms", - urls = _github_public_urls("bazelbuild/platforms/archive/d4c9d7f51a7c403814b60f66d20eeb425fbaaacb.zip"), - sha256 = "a5058ac93023092c406432ec650f30ec5a8c75d8b9d13c73150f60a9050a5663", - strip_prefix = "platforms-d4c9d7f51a7c403814b60f66d20eeb425fbaaacb", + url = "https://github.com/bazelbuild/platforms/releases/download/0.0.10/platforms-0.0.10.tar.gz", + sha256 = "218efe8ee736d26a3572663b374a253c012b716d8af0c07e842e82f238a0a7ee", ) http_archive( name = "doctest", - urls = _github_public_urls("doctest/doctest/archive/v2.4.9.zip"), + url = "https://github.com/doctest/doctest/archive/v2.4.9.zip", sha256 = "88a552f832ef3e4e7b733f9ab4eff5d73d7c37e75bebfef4a3339bf52713350d", strip_prefix = "doctest-2.4.9", ) http_archive( name = "dtl", - urls = _github_public_urls("cubicdaiya/dtl/archive/v1.19.tar.gz"), + url = "https://github.com/cubicdaiya/dtl/archive/v1.19.tar.gz", sha256 = "f47b99dd11e5d771ad32a8dc960db4ab2fbe349fb0346fa0795f53c846a99c5d", build_file = "@com_stripe_ruby_typer//third_party:dtl.BUILD", strip_prefix = "dtl-1.19", @@ -32,7 +28,7 @@ def register_sorbet_dependencies(): http_archive( name = "yaml_cpp", - urls = _github_public_urls("jbeder/yaml-cpp/archive/yaml-cpp-0.6.3.zip"), + url = "https://github.com/jbeder/yaml-cpp/archive/yaml-cpp-0.6.3.zip", sha256 = "7c0ddc08a99655508ae110ba48726c67e4a10b290c214aed866ce4bbcbe3e84c", build_file = "@com_stripe_ruby_typer//third_party:yaml_cpp.BUILD", strip_prefix = "yaml-cpp-yaml-cpp-0.6.3", @@ -40,7 +36,7 @@ def register_sorbet_dependencies(): http_archive( name = "spdlog", - urls = _github_public_urls("gabime/spdlog/archive/eb3220622e73a4889eee355ffa37972b3cac3df5.zip"), # v1.9.2 + url = "https://github.com/gabime/spdlog/archive/eb3220622e73a4889eee355ffa37972b3cac3df5.zip", # v1.9.2 sha256 = "b7570488bdd94ab6d3653bc324d8ed7976d9f3a2f035eb2e969ebcaad3b0d5c7", build_file = "@com_stripe_ruby_typer//third_party:spdlog.BUILD", strip_prefix = "spdlog-eb3220622e73a4889eee355ffa37972b3cac3df5", @@ -50,10 +46,10 @@ def register_sorbet_dependencies(): # `@zlib` if it's present. http_archive( name = "zlib", - urls = _github_public_urls("madler/zlib/archive/cacf7f1d4e3d44d871b605da3b647f07d718623f.zip"), + url = "https://github.com/madler/zlib/archive/v1.3.1.zip", build_file = "@com_stripe_ruby_typer//third_party:zlib.BUILD", - sha256 = "1cce3828ec2ba80ff8a4cac0ab5aa03756026517154c4b450e617ede751d41bd", - strip_prefix = "zlib-cacf7f1d4e3d44d871b605da3b647f07d718623f", + sha256 = "50b24b47bf19e1f35d2a21ff36d2a366638cdf958219a66f30ce0861201760e6", + strip_prefix = "zlib-1.3.1", ) # proto_library, cc_proto_library, and java_proto_library rules implicitly @@ -61,14 +57,17 @@ def register_sorbet_dependencies(): # This statement defines the @com_google_protobuf repo. http_archive( name = "com_google_protobuf", - urls = _github_public_urls("protocolbuffers/protobuf/archive/v3.14.0.zip"), - sha256 = "bf0e5070b4b99240183b29df78155eee335885e53a8af8683964579c214ad301", - strip_prefix = "protobuf-3.14.0", + url = "https://github.com/protocolbuffers/protobuf/archive/v3.27.0.zip", + sha256 = "913530eba097b17f58b9087fe9c4944de87b56913e3e340b91e317d1e6763dde", + strip_prefix = "protobuf-3.27.0", + patches = [ + "@com_stripe_ruby_typer//third_party:com_google_protobuf/cpp_opts.bzl.patch", + ], ) http_archive( name = "libprotobuf-mutator", - urls = _github_public_urls("google/libprotobuf-mutator/archive/68e10c13248517c5bcd531d0e02be483da83fc13.zip"), + url = "https://github.com/google/libprotobuf-mutator/archive/68e10c13248517c5bcd531d0e02be483da83fc13.zip", sha256 = "8684276b996e8d8541ed3703420f9dbcc17702bd7b13c6f3d9c13a4656597c76", build_file = "@com_stripe_ruby_typer//third_party:libprotobuf-mutator.BUILD", strip_prefix = "libprotobuf-mutator-68e10c13248517c5bcd531d0e02be483da83fc13", @@ -76,7 +75,7 @@ def register_sorbet_dependencies(): http_archive( name = "lmdb", - urls = _github_public_urls("DarkDimius/lmdb/archive/75766ec2b663b360be8eea9730a7adc0d252ce7e.zip"), + url = "https://github.com/DarkDimius/lmdb/archive/75766ec2b663b360be8eea9730a7adc0d252ce7e.zip", sha256 = "bd120470d62c6f3433f80bb9841f09f158924081eb0c3236da6e8d1a0976eccc", build_file = "@com_stripe_ruby_typer//third_party:lmdb.BUILD", strip_prefix = "lmdb-75766ec2b663b360be8eea9730a7adc0d252ce7e", @@ -84,7 +83,7 @@ def register_sorbet_dependencies(): http_archive( name = "rapidjson", - urls = _github_public_urls("Tencent/rapidjson/archive/f376690822cbc2d17044e626be5df21f7d91ca8f.zip"), + url = "https://github.com/Tencent/rapidjson/archive/f376690822cbc2d17044e626be5df21f7d91ca8f.zip", sha256 = "9425276583dff9020cee6332472b0cf247ae325cb5f26dbe157183f747da3910", build_file = "@com_stripe_ruby_typer//third_party:rapidjson.BUILD", strip_prefix = "rapidjson-f376690822cbc2d17044e626be5df21f7d91ca8f", @@ -92,7 +91,7 @@ def register_sorbet_dependencies(): http_archive( name = "lz4", - urls = _github_public_urls("lz4/lz4/archive/v1.9.3.zip"), + url = "https://github.com/lz4/lz4/archive/v1.9.3.zip", sha256 = "4ec935d99aa4950eadfefbd49c9fad863185ac24c32001162c44a683ef61b580", build_file = "@com_stripe_ruby_typer//third_party:lz4.BUILD", strip_prefix = "lz4-1.9.3", @@ -100,7 +99,7 @@ def register_sorbet_dependencies(): http_archive( name = "pdqsort", - urls = _github_public_urls("orlp/pdqsort/archive/08879029ab8dcb80a70142acb709e3df02de5d37.zip"), + url = "https://github.com/orlp/pdqsort/archive/08879029ab8dcb80a70142acb709e3df02de5d37.zip", sha256 = "ad8c9cd3d1abe5d566bad341bcce327a2e897b64236a7f9e74f4b9b0e7e5dc39", build_file = "@com_stripe_ruby_typer//third_party:pdqsort.BUILD", strip_prefix = "pdqsort-08879029ab8dcb80a70142acb709e3df02de5d37", @@ -108,7 +107,7 @@ def register_sorbet_dependencies(): http_archive( name = "jemalloc", - urls = _github_public_urls("jemalloc/jemalloc/archive/20f9802e4f25922884448d9581c66d76cc905c0c.zip"), # 5.3 + url = "https://github.com/jemalloc/jemalloc/archive/20f9802e4f25922884448d9581c66d76cc905c0c.zip", # 5.3 sha256 = "1cc1ec93701868691c73b371eb87e5452257996279a42303a91caad355374439", build_file = "@com_stripe_ruby_typer//third_party:jemalloc.BUILD", strip_prefix = "jemalloc-20f9802e4f25922884448d9581c66d76cc905c0c", @@ -116,7 +115,7 @@ def register_sorbet_dependencies(): http_archive( name = "mimalloc", - urls = _github_public_urls("microsoft/mimalloc/archive/refs/tags/v2.1.2.zip"), # 2.1.2 + url = "https://github.com/microsoft/mimalloc/archive/refs/tags/v2.1.2.zip", # 2.1.2 sha256 = "86281c918921c1007945a8a31e5ad6ae9af77e510abfec20d000dd05d15123c7", build_file = "@com_stripe_ruby_typer//third_party:mimalloc.BUILD", strip_prefix = "mimalloc-2.1.2", @@ -124,7 +123,7 @@ def register_sorbet_dependencies(): http_archive( name = "concurrentqueue", - urls = _github_public_urls("cameron314/concurrentqueue/archive/79cec4c3bf1ca23ea4a03adfcd3c2c3659684dd2.zip"), + url = "https://github.com/cameron314/concurrentqueue/archive/79cec4c3bf1ca23ea4a03adfcd3c2c3659684dd2.zip", sha256 = "a78ff232e2996927ad6fbd015d1f15dfb20bf524a87ce2893e64dbbe1f04051e", build_file = "@com_stripe_ruby_typer//third_party:concurrentqueue.BUILD", strip_prefix = "concurrentqueue-79cec4c3bf1ca23ea4a03adfcd3c2c3659684dd2", @@ -132,7 +131,7 @@ def register_sorbet_dependencies(): http_archive( name = "statsd", - urls = _github_public_urls("romanbsd/statsd-c-client/archive/08ecca678345f157e72a1db1446facb403cbeb65.zip"), + url = "https://github.com/romanbsd/statsd-c-client/archive/08ecca678345f157e72a1db1446facb403cbeb65.zip", sha256 = "825395556fb553383173e47dbce98165981d100587993292ec9d174ec40a7ba1", build_file = "@com_stripe_ruby_typer//third_party:statsd.BUILD", strip_prefix = "statsd-c-client-08ecca678345f157e72a1db1446facb403cbeb65", @@ -140,7 +139,7 @@ def register_sorbet_dependencies(): http_archive( name = "cxxopts", - urls = _github_public_urls("jarro2783/cxxopts/archive/c74846a891b3cc3bfa992d588b1295f528d43039.zip"), + url = "https://github.com/jarro2783/cxxopts/archive/c74846a891b3cc3bfa992d588b1295f528d43039.zip", sha256 = "4ba2b0a3c94e61501b974118a0fe171cd658f8efdd941e9ad82e71f48a98933a", build_file = "@com_stripe_ruby_typer//third_party:cxxopts.BUILD", strip_prefix = "cxxopts-c74846a891b3cc3bfa992d588b1295f528d43039", @@ -148,7 +147,7 @@ def register_sorbet_dependencies(): http_archive( name = "rang", - urls = _github_public_urls("agauniyal/rang/archive/v3.1.0.zip"), + url = "https://github.com/agauniyal/rang/archive/v3.1.0.zip", sha256 = "658adeb8a36d36981d4339fc839f2deedc0e75cb421db1982041d8a0a255835d", build_file = "@com_stripe_ruby_typer//third_party:rang.BUILD", strip_prefix = "rang-3.1.0", @@ -156,7 +155,7 @@ def register_sorbet_dependencies(): http_archive( name = "xxhash", - urls = _github_public_urls("Cyan4973/xxHash/archive/v0.8.0.zip"), + url = "https://github.com/Cyan4973/xxHash/archive/v0.8.0.zip", sha256 = "064333c754f166837bbefefa497642a60b3f8035e54bae52eb304d3cb3ceb655", build_file = "@com_stripe_ruby_typer//third_party:xxhash.BUILD", strip_prefix = "xxHash-0.8.0", @@ -164,14 +163,14 @@ def register_sorbet_dependencies(): http_archive( name = "com_google_absl", - urls = _github_public_urls("abseil/abseil-cpp/archive/8910297baf87e1777c4fd30fb0693eecf9f2c134.zip"), - sha256 = "c43b8cd8e306e7fe3f006d880181d60db59a3bae6b6bc725da86a28a6b0f9f30", - strip_prefix = "abseil-cpp-8910297baf87e1777c4fd30fb0693eecf9f2c134", + url = "https://github.com/abseil/abseil-cpp/archive/20240116.2.zip", + sha256 = "69909dd729932cbbabb9eeaff56179e8d124515f5d3ac906663d573d700b4c7d", + strip_prefix = "abseil-cpp-20240116.2", ) http_archive( name = "com_grail_bazel_compdb", - urls = _github_public_urls("grailbio/bazel-compilation-database/archive/6b9329e37295eab431f82af5fe24219865403e0f.zip"), + url = "https://github.com/grailbio/bazel-compilation-database/archive/6b9329e37295eab431f82af5fe24219865403e0f.zip", sha256 = "6cf0dc4b40023a26787cd7cdb629dccd26e2208c8a2f19e1dde4ca10c109c86c", strip_prefix = "bazel-compilation-database-6b9329e37295eab431f82af5fe24219865403e0f", ) @@ -183,38 +182,33 @@ def register_sorbet_dependencies(): urls = ["https://github.com/bazelbuild/rules_cc/archive/726dd8157557f1456b3656e26ab21a1646653405.tar.gz"], ) - # NOTE: we use the sorbet branch for development to keep our changes rebasable on grailio/bazel-toolchain + # TODO(jez) We keep our changes on the `sorbet` branch of `sorbet/bazel-toolchain` + # The `master` branch is the commit of `bazel-contrib/toolchains_llvm` that we're based on + # In 2ddd7d791 (#7912) we upgraded the toolchain. Our old toolchain patches are on the `sorbet-old-toolchain` branch http_archive( - name = "com_grail_bazel_toolchain", - urls = _github_public_urls("sorbet/bazel-toolchain/archive/4124470e037b4464a88db71c8565ad44af29664d.zip"), - sha256 = "23e9aa7318a6c3bfb2712c4c85e731f1cde8bfdd0d84b39aab6b8746ab7c391e", - strip_prefix = "bazel-toolchain-4124470e037b4464a88db71c8565ad44af29664d", + name = "toolchains_llvm", + url = "https://github.com/sorbet/bazel-toolchain/archive/8d9165fd3560f6ff50bc4794972f714f4ba2adaa.tar.gz", + sha256 = "238b5a777bbfac3d5ec35cbd45ae2b84ca4118aa8f902ab27894912352e94658", + strip_prefix = "bazel-toolchain-8d9165fd3560f6ff50bc4794972f714f4ba2adaa", ) http_archive( name = "io_bazel_rules_go", - urls = _github_public_urls("bazelbuild/rules_go/archive/dd4fb4f8128b83f189f7bdda663e65b915a6d3c4.zip"), - sha256 = "ea702009018b6a5d6665808a4d1f54e2f40a2938e3946e98de00d38b34fd8a27", - strip_prefix = "rules_go-dd4fb4f8128b83f189f7bdda663e65b915a6d3c4", - ) - - http_archive( - name = "build_bazel_rules_nodejs", - sha256 = "e79c08a488cc5ac40981987d862c7320cee8741122a2649e9b08e850b6f20442", - urls = _github_public_urls("bazelbuild/rules_nodejs/releases/download/3.8.0/rules_nodejs-3.8.0.tar.gz"), + sha256 = "d6ab6b57e48c09523e93050f13698f708428cfd5e619252e369d377af6597707", + url = "https://github.com/bazelbuild/rules_go/releases/download/v0.43.0/rules_go-v0.43.0.zip", ) http_archive( name = "com_github_bazelbuild_buildtools", - urls = _github_public_urls("bazelbuild/buildtools/archive/5bcc31df55ec1de770cb52887f2e989e7068301f.zip"), + url = "https://github.com/bazelbuild/buildtools/archive/5bcc31df55ec1de770cb52887f2e989e7068301f.zip", sha256 = "875d0c49953e221cfc35d2a3846e502f366dfa4024b271fa266b186ca4664b37", strip_prefix = "buildtools-5bcc31df55ec1de770cb52887f2e989e7068301f", ) - # optimized version of blake2 hashing algorithm + # optimized version of blake2 hashing algorithm, using SSE vector extensions http_archive( name = "com_github_blake2_libb2", - urls = _github_public_urls("BLAKE2/libb2/archive/fa83ddbe179912e9a7a57edf0333b33f6ff83056.zip"), + url = "https://github.com/BLAKE2/libb2/archive/fa83ddbe179912e9a7a57edf0333b33f6ff83056.zip", sha256 = "dd25f7ac53371c2a15761fc1689d04de2ff948ac7e213a10d13961e24b0c9ae6", build_file = "@com_stripe_ruby_typer//third_party:libb2.BUILD", strip_prefix = "libb2-fa83ddbe179912e9a7a57edf0333b33f6ff83056", @@ -223,7 +217,7 @@ def register_sorbet_dependencies(): # portable reference implementation of blake2 http_archive( name = "com_github_blake2_blake2", - urls = _github_public_urls("BLAKE2/BLAKE2/archive/997fa5ba1e14b52c554fb03ce39e579e6f27b90c.zip"), + url = "https://github.com/BLAKE2/BLAKE2/archive/997fa5ba1e14b52c554fb03ce39e579e6f27b90c.zip", sha256 = "56dafe9512f65728ce7abc78900272f8bf8e95ca04439b362d2dc461927b2a17", build_file = "@com_stripe_ruby_typer//third_party:blake2.BUILD", strip_prefix = "BLAKE2-997fa5ba1e14b52c554fb03ce39e579e6f27b90c", @@ -231,7 +225,7 @@ def register_sorbet_dependencies(): http_archive( name = "com_github_ludocode_mpack", - urls = _github_public_urls("ludocode/mpack/archive/6883f6f8067d5c6dbaaddeaa47aaaaa763eea51c.zip"), + url = "https://github.com/ludocode/mpack/archive/6883f6f8067d5c6dbaaddeaa47aaaaa763eea51c.zip", sha256 = "fb184dc169722cecf9b47bece308f70861787f4615ebdbee7383b6434cfdbc0d", build_file = "@com_stripe_ruby_typer//third_party:mpack.BUILD", strip_prefix = "mpack-6883f6f8067d5c6dbaaddeaa47aaaaa763eea51c", @@ -239,63 +233,47 @@ def register_sorbet_dependencies(): http_archive( name = "com_github_d_bahr_crcpp", - urls = _github_public_urls("d-bahr/CRCpp/archive/51fbc35ef892e98abe91a51f7320749c929d72bd.zip"), + url = "https://github.com/d-bahr/CRCpp/archive/51fbc35ef892e98abe91a51f7320749c929d72bd.zip", sha256 = "57c4c127b5aa4451556969d6929cf9465a5d5481b3442ddb878d95296caeee4b", build_file = "@com_stripe_ruby_typer//third_party:crcpp.BUILD", strip_prefix = "CRCpp-51fbc35ef892e98abe91a51f7320749c929d72bd", ) http_archive( - name = "emscripten_toolchain", - urls = _github_public_urls("kripken/emscripten/archive/1.38.25.tar.gz"), - build_file = "@com_stripe_ruby_typer//third_party:emscripten-toolchain.BUILD", - sha256 = "4d6fa350895fabc25b89ce5f9dcb528e719e7c2bf7dacab2a3e3cc818ecd7019", - strip_prefix = "emscripten-1.38.25", + name = "emsdk", + sha256 = "47515d522229a103b7d9f34eacc1d88ac355b22fd754d13417a2191fd9d77d5f", + strip_prefix = "emsdk-3.1.59/bazel", + url = "https://github.com/emscripten-core/emsdk/archive/3.1.59.tar.gz", patches = [ - "@com_stripe_ruby_typer//third_party:emscripten_toolchain/emcc.py.patch", - "@com_stripe_ruby_typer//third_party:emscripten_toolchain/tools_shared.py.patch", + # (cd ~/stripe/github/emsdk/bazel && \ + # git diff --relative --no-prefix > ~/stripe/sorbet/third_party/emsdk/emscripten_config.patch) + "@com_stripe_ruby_typer//third_party:emsdk/emscripten_config.patch", ], ) - http_archive( - name = "emscripten_clang_linux", - urls = _emscripten_urls("linux/emscripten-llvm-e1.38.25.tar.gz"), - build_file = "@com_stripe_ruby_typer//third_party:emscripten-clang.BUILD", - sha256 = "0e9a5a114a60c21604f4038b573109bd31424aeba275b4173480485ca0a56ba4", - strip_prefix = "emscripten-llvm-e1.38.25", - ) - - http_archive( - name = "emscripten_clang_darwin", - urls = _emscripten_urls("mac/emscripten-llvm-e1.38.25.tar.gz"), - build_file = "@com_stripe_ruby_typer//third_party:emscripten-clang.BUILD", - sha256 = "01519125c613d0b013193eaf5ac5031e6ec34aac2451c357fd4097874ceee38c", - strip_prefix = "emscripten-llvm-e1.38.25", - ) - http_archive( name = "rules_ragel", - urls = _github_public_urls("jmillikin/rules_ragel/archive/f99f17fcad2e155646745f4827ac636a3b5d4d15.zip"), + url = "https://github.com/jmillikin/rules_ragel/archive/f99f17fcad2e155646745f4827ac636a3b5d4d15.zip", sha256 = "f957682c6350b2e4484c433c7f45d427a86de5c8751a0d2a9836f36995fe0320", strip_prefix = "rules_ragel-f99f17fcad2e155646745f4827ac636a3b5d4d15", ) http_archive( name = "rules_bison", - urls = _github_public_urls("jmillikin/rules_bison/archive/478079b28605a38000eaf83719568d756b3383a0.zip"), + url = "https://github.com/jmillikin/rules_bison/archive/478079b28605a38000eaf83719568d756b3383a0.zip", sha256 = "d662d200f4e2a868f6873d666402fa4d413f07ba1a433591c5f60ac601157fb9", strip_prefix = "rules_bison-478079b28605a38000eaf83719568d756b3383a0", ) http_archive( name = "rules_m4", - urls = _github_public_urls("jmillikin/rules_m4/releases/download/v0.2.1/rules_m4-v0.2.1.tar.xz"), + url = "https://github.com/jmillikin/rules_m4/releases/download/v0.2.1/rules_m4-v0.2.1.tar.xz", sha256 = "f59f75ac8a315d7647a2d058d324a87ff9ebbc4bf5c7a61b08d58da119a7fb43", ) http_archive( name = "cpp_subprocess", - urls = _github_public_urls("arun11299/cpp-subprocess/archive/9c624ce4e3423cce9f148bafbae56abfd6437ea0.zip"), + url = "https://github.com/arun11299/cpp-subprocess/archive/9c624ce4e3423cce9f148bafbae56abfd6437ea0.zip", sha256 = "1810d1ec80f3c319dcbb530443b264b9a32a449b5a5d3630076e473648bba8cc", build_file = "@com_stripe_ruby_typer//third_party:cpp_subprocess.BUILD", strip_prefix = "cpp-subprocess-9c624ce4e3423cce9f148bafbae56abfd6437ea0", @@ -324,32 +302,28 @@ def register_sorbet_dependencies(): http_archive( name = "bazel_skylib", - sha256 = "9a737999532daca978a158f94e77e9af6a6a169709c0cee274f0a4c3359519bd", - strip_prefix = "bazel-skylib-1.0.0", - urls = _github_public_urls("bazelbuild/bazel-skylib/archive/1.0.0.tar.gz"), + sha256 = "cd55a062e763b9349921f0f5db8c3933288dc8ba4f76dd9416aac68acee3cb94", + url = "https://github.com/bazelbuild/bazel-skylib/releases/download/1.5.0/bazel-skylib-1.5.0.tar.gz", ) http_archive( - name = "llvm", - - # llvm 12.0.0 - urls = _github_public_urls("llvm/llvm-project/archive/0cbbf06b625605fff83d89b17c2187c7ccfcecd5.tar.gz"), - build_file = "@com_stripe_ruby_typer//third_party/llvm:llvm.autogenerated.BUILD", - sha256 = "cd4964439e7b4a2a22176ec2de70c3b67771c515eacaf88fb82a3a52fed7592a", - strip_prefix = "llvm-project-0cbbf06b625605fff83d89b17c2187c7ccfcecd5/llvm", + name = "aspect_bazel_lib", + sha256 = "357dad9d212327c35d9244190ef010aad315e73ffa1bed1a29e20c372f9ca346", + strip_prefix = "bazel-lib-2.7.0", + url = "https://github.com/aspect-build/bazel-lib/releases/download/v2.7.0/bazel-lib-v2.7.0.tar.gz", ) shellcheck_version = "0.8.0" http_archive( name = "shellcheck_linux", - urls = _github_public_urls("koalaman/shellcheck/releases/download/v{0}/shellcheck-v{0}.linux.x86_64.tar.xz".format(shellcheck_version)), + url = "https://github.com/koalaman/shellcheck/releases/download/v{0}/shellcheck-v{0}.linux.x86_64.tar.xz".format(shellcheck_version), build_file = "@com_stripe_ruby_typer//third_party:shellcheck.BUILD", sha256 = "ab6ee1b178f014d1b86d1e24da20d1139656c8b0ed34d2867fbb834dad02bf0a", strip_prefix = "shellcheck-v{}".format(shellcheck_version), ) http_archive( name = "shellcheck_darwin", - urls = _github_public_urls("koalaman/shellcheck/releases/download/v{0}/shellcheck-v{0}.darwin.x86_64.tar.xz".format(shellcheck_version)), + url = "https://github.com/koalaman/shellcheck/releases/download/v{0}/shellcheck-v{0}.darwin.x86_64.tar.xz".format(shellcheck_version), build_file = "@com_stripe_ruby_typer//third_party:shellcheck.BUILD", sha256 = "e065d4afb2620cc8c1d420a9b3e6243c84ff1a693c1ff0e38f279c8f31e86634", strip_prefix = "shellcheck-v{}".format(shellcheck_version), @@ -358,7 +332,7 @@ def register_sorbet_dependencies(): # Needed to build CMake projects http_archive( name = "rules_foreign_cc", - urls = _github_public_urls("bazelbuild/rules_foreign_cc/archive/d74623f0ad47f4e375de81baa454eb106715a416.zip"), + url = "https://github.com/bazelbuild/rules_foreign_cc/archive/d74623f0ad47f4e375de81baa454eb106715a416.zip", sha256 = "47b61d25dd52bdaa1d571dab6705d076f05ba3d7a1bbbfed36145f8281c0403f", strip_prefix = "rules_foreign_cc-d74623f0ad47f4e375de81baa454eb106715a416", ) @@ -372,21 +346,3 @@ def register_scip_ruby_dependencies(): urls = [data["archive_url"]], sha256 = data["archive_sha256"], ) - -def _github_public_urls(path): - """ - Produce a url list that works both with github, and stripe's internal artifact cache. - """ - return [ - "https://github.com/{}".format(path), - "https://artifactory-content.stripe.build/artifactory/github-archives/{}".format(path), - ] - -def _emscripten_urls(path): - """ - Produce a url list that works both with emscripten, and stripe's internal artifact cache. - """ - return [ - "https://storage.googleapis.com/webassembly/emscripten-releases-builds/old/{}".format(path), - "https://artifactory-content.stripe.build/artifactory/googleapis-storage-cache/webassembly/emscripten-releases-builds/old/{}".format(path), - ] diff --git a/third_party/licenses/README b/third_party/licenses/README index 80e2d0021e..be2c98bb0e 100644 --- a/third_party/licenses/README +++ b/third_party/licenses/README @@ -1,2 +1,2 @@ Every .txt file here just gets concatenated into output of sorbet --license -It's a low-tech hack, that might be superceeded by https://github.com/bazelbuild/bazel/issues/7444. +It's a low-tech hack, that might be superseded by https://github.com/bazelbuild/bazel/issues/7444. diff --git a/third_party/llvm/BUILD b/third_party/llvm/BUILD deleted file mode 100644 index 05c0c5685e..0000000000 --- a/third_party/llvm/BUILD +++ /dev/null @@ -1,6 +0,0 @@ -py_binary( - name = "expand_cmake_vars", - srcs = ["expand_cmake_vars.py"], - srcs_version = "PY3", - visibility = ["@llvm//:__subpackages__"], -) diff --git a/third_party/llvm/README.md b/third_party/llvm/README.md deleted file mode 100644 index 0d4b85fed0..0000000000 --- a/third_party/llvm/README.md +++ /dev/null @@ -1 +0,0 @@ -Very inspired by https://github.com/tensorflow/tensorflow/tree/master/third_party/llvm diff --git a/third_party/llvm/common.bzl b/third_party/llvm/common.bzl deleted file mode 100644 index 4f622cd7da..0000000000 --- a/third_party/llvm/common.bzl +++ /dev/null @@ -1,42 +0,0 @@ -# Rule for simple expansion of template files. This performs a simple -# search over the template file for the keys in substitutions, -# and replaces them with the corresponding values. -# -# Typical usage: -# load("/tools/build_rules/template_rule", "expand_header_template") -# template_rule( -# name = "ExpandMyTemplate", -# src = "my.template", -# out = "my.txt", -# substitutions = { -# "$VAR1": "foo", -# "$VAR2": "bar", -# } -# ) -# -# Args: -# name: The name of the rule. -# template: The template file to expand -# out: The destination of the expanded file -# substitutions: A dictionary mapping strings to their substitutions - -def template_rule_impl(ctx): - ctx.actions.expand_template( - template = ctx.file.src, - output = ctx.outputs.out, - substitutions = ctx.attr.substitutions, - ) - -template_rule = rule( - attrs = { - "src": attr.label( - mandatory = True, - allow_single_file = True, - ), - "substitutions": attr.string_dict(mandatory = True), - "out": attr.output(mandatory = True), - }, - # output_to_genfiles is required for header files. - output_to_genfiles = True, - implementation = template_rule_impl, -) diff --git a/third_party/llvm/expand_cmake_vars.py b/third_party/llvm/expand_cmake_vars.py deleted file mode 100644 index a8a4b9673e..0000000000 --- a/third_party/llvm/expand_cmake_vars.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright 2016 The TensorFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ============================================================================== - -"""Expands CMake variables in a text file.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import re -import sys - -_CMAKE_DEFINE_REGEX = re.compile(r"\s*#cmakedefine\s+([A-Za-z_0-9]*)(\s.*)?$") -_CMAKE_DEFINE01_REGEX = re.compile(r"\s*#cmakedefine01\s+([A-Za-z_0-9]*)") -_CMAKE_VAR_REGEX = re.compile(r"\${([A-Za-z_0-9]*)}") -_CMAKE_ATVAR_REGEX = re.compile(r"@([A-Za-z_0-9]*)@") - - -def _parse_args(argv): - """Parses arguments with the form KEY=VALUE into a dictionary.""" - result = {} - for arg in argv: - k, v = arg.split("=") - result[k] = v - return result - - -def _expand_variables(input_str, cmake_vars): - """Expands ${VARIABLE}s and @VARIABLE@s in 'input_str', using dictionary 'cmake_vars'. - - Args: - input_str: the string containing ${VARIABLE} or @VARIABLE@ expressions to expand. - cmake_vars: a dictionary mapping variable names to their values. - - Returns: - The expanded string. - """ - def replace(match): - if match.group(1) in cmake_vars: - return cmake_vars[match.group(1)] - return "" - return _CMAKE_ATVAR_REGEX.sub(replace,_CMAKE_VAR_REGEX.sub(replace, input_str)) - - -def _expand_cmakedefines(line, cmake_vars): - """Expands #cmakedefine declarations, using a dictionary 'cmake_vars'.""" - - # Handles #cmakedefine lines - match = _CMAKE_DEFINE_REGEX.match(line) - if match: - name = match.group(1) - suffix = match.group(2) or "" - if name in cmake_vars: - return "#define {}{}\n".format(name, - _expand_variables(suffix, cmake_vars)) - else: - return "/* #undef {} */\n".format(name) - - # Handles #cmakedefine01 lines - match = _CMAKE_DEFINE01_REGEX.match(line) - if match: - name = match.group(1) - value = cmake_vars.get(name, "0") - return "#define {} {}\n".format(name, value) - - # Otherwise return the line unchanged. - return _expand_variables(line, cmake_vars) - - -def main(): - cmake_vars = _parse_args(sys.argv[1:]) - for line in sys.stdin: - sys.stdout.write(_expand_cmakedefines(line, cmake_vars)) - - -if __name__ == "__main__": - main() diff --git a/third_party/llvm/llvm.autogenerated.BUILD b/third_party/llvm/llvm.autogenerated.BUILD deleted file mode 100644 index 3e0b0f72f1..0000000000 --- a/third_party/llvm/llvm.autogenerated.BUILD +++ /dev/null @@ -1,4903 +0,0 @@ -# Bazel BUILD file for LLVM. -# -# This BUILD file is auto-generated; do not edit! - -licenses(["notice"]) - -exports_files(["LICENSE.TXT"]) - -load( - "@com_stripe_ruby_typer//third_party/llvm:llvm.bzl", - "cmake_var_string", - "expand_cmake_vars", - "gentbl", - "llvm_all_cmake_vars", - "llvm_copts", - "llvm_defines", - "llvm_linkopts", - "llvm_support_platform_specific_srcs_glob", -) -load( - "@com_stripe_ruby_typer//third_party/llvm:common.bzl", - "template_rule", -) - -package(default_visibility = ["//visibility:public"]) - -llvm_host_triple = "x86_64-unknown-linux_gnu" - -llvm_targets = [ - "AArch64", - "AMDGPU", - "ARM", - "NVPTX", - "PowerPC", - "SystemZ", - "X86", -] - -llvm_target_asm_parsers = llvm_targets - -llvm_target_asm_printers = llvm_targets - -llvm_target_disassemblers = llvm_targets - -# Performs CMake variable substitutions on configuration header files. -expand_cmake_vars( - name = "config_gen", - src = "include/llvm/Config/config.h.cmake", - cmake_vars = llvm_all_cmake_vars, - dst = "include/llvm/Config/config.h", -) - -expand_cmake_vars( - name = "llvm_config_gen", - src = "include/llvm/Config/llvm-config.h.cmake", - cmake_vars = llvm_all_cmake_vars, - dst = "include/llvm/Config/llvm-config.h", -) - -expand_cmake_vars( - name = "abi_breaking_gen", - src = "include/llvm/Config/abi-breaking.h.cmake", - cmake_vars = llvm_all_cmake_vars, - dst = "include/llvm/Config/abi-breaking.h", -) - -# Performs macro expansions on .def.in files -template_rule( - name = "targets_def_gen", - src = "include/llvm/Config/Targets.def.in", - out = "include/llvm/Config/Targets.def", - substitutions = { - "@LLVM_ENUM_TARGETS@": "\n".join( - ["LLVM_TARGET({})".format(t) for t in llvm_targets], - ), - }, -) - -template_rule( - name = "asm_parsers_def_gen", - src = "include/llvm/Config/AsmParsers.def.in", - out = "include/llvm/Config/AsmParsers.def", - substitutions = { - "@LLVM_ENUM_ASM_PARSERS@": "\n".join( - ["LLVM_ASM_PARSER({})".format(t) for t in llvm_target_asm_parsers], - ), - }, -) - -template_rule( - name = "asm_printers_def_gen", - src = "include/llvm/Config/AsmPrinters.def.in", - out = "include/llvm/Config/AsmPrinters.def", - substitutions = { - "@LLVM_ENUM_ASM_PRINTERS@": "\n".join( - ["LLVM_ASM_PRINTER({})".format(t) for t in llvm_target_asm_printers], - ), - }, -) - -template_rule( - name = "disassemblers_def_gen", - src = "include/llvm/Config/Disassemblers.def.in", - out = "include/llvm/Config/Disassemblers.def", - substitutions = { - "@LLVM_ENUM_DISASSEMBLERS@": "\n".join( - ["LLVM_DISASSEMBLER({})".format(t) for t in llvm_target_disassemblers], - ), - }, -) - -# A common library that all LLVM targets depend on. -# TODO(b/113996071): We need to glob all potentially #included files and stage -# them here because LLVM's build files are not strict headers clean, and remote -# build execution requires all inputs to be depended upon. -cc_library( - name = "config", - hdrs = glob([ - "**/*.h", - "**/*.def", - "**/*.inc.cpp", - ]) + [ - "include/llvm/Config/AsmParsers.def", - "include/llvm/Config/AsmPrinters.def", - "include/llvm/Config/Disassemblers.def", - "include/llvm/Config/Targets.def", - "include/llvm/Config/config.h", - "include/llvm/Config/llvm-config.h", - "include/llvm/Config/abi-breaking.h", - ], - defines = llvm_defines, - includes = ["include"], -) - -# A creator of an empty file include/llvm/Support/VCSRevision.h. -# This is usually populated by the upstream build infrastructure, but in this -# case we leave it blank. See upstream revision r300160. -genrule( - name = "vcs_revision_gen", - srcs = [], - outs = ["include/llvm/Support/VCSRevision.h"], - cmd = "echo '' > \"$@\"", -) - -# Rules that apply the LLVM tblgen tool. -gentbl( - name = "attributes_gen", - tbl_outs = [("-gen-attrs", "include/llvm/IR/Attributes.inc")], - tblgen = ":llvm-tblgen", - td_file = "include/llvm/IR/Attributes.td", - td_srcs = ["include/llvm/IR/Attributes.td"], -) - -gentbl( - name = "InstCombineTableGen", - tbl_outs = [( - "-gen-searchable-tables", - "lib/Target/AMDGPU/InstCombineTables.inc", - )], - tblgen = ":llvm-tblgen", - td_file = "lib/Target/AMDGPU/InstCombineTables.td", - td_srcs = glob([ - "include/llvm/CodeGen/*.td", - "include/llvm/IR/Intrinsics*.td", - ]) + ["include/llvm/TableGen/SearchableTable.td"], -) - -gentbl( - name = "intrinsic_enums_gen", - tbl_outs = [("-gen-intrinsic-enums", "include/llvm/IR/IntrinsicEnums.inc")], - tblgen = ":llvm-tblgen", - td_file = "include/llvm/IR/Intrinsics.td", - td_srcs = glob([ - "include/llvm/CodeGen/*.td", - "include/llvm/IR/Intrinsics*.td", - ]), -) - -gentbl( - name = "aarch64_enums_gen", - tbl_outs = [( - "-gen-intrinsic-enums -intrinsic-prefix=aarch64", - "include/llvm/IR/IntrinsicsAArch64.h", - )], - tblgen = ":llvm-tblgen", - td_file = "include/llvm/IR/Intrinsics.td", - td_srcs = glob([ - "include/llvm/CodeGen/*.td", - "include/llvm/IR/Intrinsics*.td", - ]), -) - -gentbl( - name = "amdgcn_enums_gen", - tbl_outs = [( - "-gen-intrinsic-enums -intrinsic-prefix=amdgcn", - "include/llvm/IR/IntrinsicsAMDGPU.h", - )], - tblgen = ":llvm-tblgen", - td_file = "include/llvm/IR/Intrinsics.td", - td_srcs = glob([ - "include/llvm/CodeGen/*.td", - "include/llvm/IR/Intrinsics*.td", - ]), -) - -gentbl( - name = "arm_enums_gen", - tbl_outs = [( - "-gen-intrinsic-enums -intrinsic-prefix=arm", - "include/llvm/IR/IntrinsicsARM.h", - )], - tblgen = ":llvm-tblgen", - td_file = "include/llvm/IR/Intrinsics.td", - td_srcs = glob([ - "include/llvm/CodeGen/*.td", - "include/llvm/IR/Intrinsics*.td", - ]), -) - -gentbl( - name = "bpf_enums_gen", - tbl_outs = [( - "-gen-intrinsic-enums -intrinsic-prefix=bpf", - "include/llvm/IR/IntrinsicsBPF.h", - )], - tblgen = ":llvm-tblgen", - td_file = "include/llvm/IR/Intrinsics.td", - td_srcs = glob([ - "include/llvm/CodeGen/*.td", - "include/llvm/IR/Intrinsics*.td", - ]), -) - -gentbl( - name = "hexagon_enums_gen", - tbl_outs = [( - "-gen-intrinsic-enums -intrinsic-prefix=hexagon", - "include/llvm/IR/IntrinsicsHexagon.h", - )], - tblgen = ":llvm-tblgen", - td_file = "include/llvm/IR/Intrinsics.td", - td_srcs = glob([ - "include/llvm/CodeGen/*.td", - "include/llvm/IR/Intrinsics*.td", - ]), -) - -gentbl( - name = "mips_enums_gen", - tbl_outs = [( - "-gen-intrinsic-enums -intrinsic-prefix=mips", - "include/llvm/IR/IntrinsicsMips.h", - )], - tblgen = ":llvm-tblgen", - td_file = "include/llvm/IR/Intrinsics.td", - td_srcs = glob([ - "include/llvm/CodeGen/*.td", - "include/llvm/IR/Intrinsics*.td", - ]), -) - -gentbl( - name = "nvvm_enums_gen", - tbl_outs = [( - "-gen-intrinsic-enums -intrinsic-prefix=nvvm", - "include/llvm/IR/IntrinsicsNVPTX.h", - )], - tblgen = ":llvm-tblgen", - td_file = "include/llvm/IR/Intrinsics.td", - td_srcs = glob([ - "include/llvm/CodeGen/*.td", - "include/llvm/IR/Intrinsics*.td", - ]), -) - -gentbl( - name = "ppc_enums_gen", - tbl_outs = [( - "-gen-intrinsic-enums -intrinsic-prefix=ppc", - "include/llvm/IR/IntrinsicsPowerPC.h", - )], - tblgen = ":llvm-tblgen", - td_file = "include/llvm/IR/Intrinsics.td", - td_srcs = glob([ - "include/llvm/CodeGen/*.td", - "include/llvm/IR/Intrinsics*.td", - ]), -) - -gentbl( - name = "r600_enums_gen", - tbl_outs = [( - "-gen-intrinsic-enums -intrinsic-prefix=r600", - "include/llvm/IR/IntrinsicsR600.h", - )], - tblgen = ":llvm-tblgen", - td_file = "include/llvm/IR/Intrinsics.td", - td_srcs = glob([ - "include/llvm/CodeGen/*.td", - "include/llvm/IR/Intrinsics*.td", - ]), -) - -gentbl( - name = "riscv_enums_gen", - tbl_outs = [( - "-gen-intrinsic-enums -intrinsic-prefix=riscv", - "include/llvm/IR/IntrinsicsRISCV.h", - )], - tblgen = ":llvm-tblgen", - td_file = "include/llvm/IR/Intrinsics.td", - td_srcs = glob([ - "include/llvm/CodeGen/*.td", - "include/llvm/IR/Intrinsics*.td", - ]), -) - -gentbl( - name = "s390_enums_gen", - tbl_outs = [( - "-gen-intrinsic-enums -intrinsic-prefix=s390", - "include/llvm/IR/IntrinsicsS390.h", - )], - tblgen = ":llvm-tblgen", - td_file = "include/llvm/IR/Intrinsics.td", - td_srcs = glob([ - "include/llvm/CodeGen/*.td", - "include/llvm/IR/Intrinsics*.td", - ]), -) - -gentbl( - name = "ve_enums_gen", - tbl_outs = [( - "-gen-intrinsic-enums -intrinsic-prefix=ve", - "include/llvm/IR/IntrinsicsVE.h", - )], - tblgen = ":llvm-tblgen", - td_file = "include/llvm/IR/Intrinsics.td", - td_srcs = glob([ - "include/llvm/CodeGen/*.td", - "include/llvm/IR/Intrinsics*.td", - ]), -) - -gentbl( - name = "wasm_enums_gen", - tbl_outs = [( - "-gen-intrinsic-enums -intrinsic-prefix=wasm", - "include/llvm/IR/IntrinsicsWebAssembly.h", - )], - tblgen = ":llvm-tblgen", - td_file = "include/llvm/IR/Intrinsics.td", - td_srcs = glob([ - "include/llvm/CodeGen/*.td", - "include/llvm/IR/Intrinsics*.td", - ]), -) - -gentbl( - name = "x86_enums_gen", - tbl_outs = [( - "-gen-intrinsic-enums -intrinsic-prefix=x86", - "include/llvm/IR/IntrinsicsX86.h", - )], - tblgen = ":llvm-tblgen", - td_file = "include/llvm/IR/Intrinsics.td", - td_srcs = glob([ - "include/llvm/CodeGen/*.td", - "include/llvm/IR/Intrinsics*.td", - ]), -) - -gentbl( - name = "xcore_enums_gen", - tbl_outs = [( - "-gen-intrinsic-enums -intrinsic-prefix=xcore", - "include/llvm/IR/IntrinsicsXCore.h", - )], - tblgen = ":llvm-tblgen", - td_file = "include/llvm/IR/Intrinsics.td", - td_srcs = glob([ - "include/llvm/CodeGen/*.td", - "include/llvm/IR/Intrinsics*.td", - ]), -) - -gentbl( - name = "intrinsics_impl_gen", - tbl_outs = [("-gen-intrinsic-impl", "include/llvm/IR/IntrinsicImpl.inc")], - tblgen = ":llvm-tblgen", - td_file = "include/llvm/IR/Intrinsics.td", - td_srcs = glob([ - "include/llvm/CodeGen/*.td", - "include/llvm/IR/Intrinsics*.td", - ]), -) - -cc_library( - name = "tblgen", - srcs = glob([ - "utils/TableGen/*.cpp", - "utils/TableGen/*.h", - "utils/TableGen/GlobalISel/*.cpp", - ]), - hdrs = glob([ - "utils/TableGen/GlobalISel/*.h", - ]), - deps = [ - ":MC", - ":Support", - ":TableGen", - ":config", - ], -) - -# Binary targets used by Tensorflow. -cc_binary( - name = "llvm-tblgen", - srcs = glob([ - "utils/TableGen/*.cpp", - "utils/TableGen/*.h", - ]), - copts = llvm_copts, - linkopts = llvm_linkopts, - stamp = 0, - deps = [ - ":Support", - ":TableGen", - ":config", - ":tblgen", - ], -) - -cc_library( - name = "FileCheckLib", - srcs = glob([ - "lib/FileCheck/*.cpp", - "lib/FileCheck/*.h", - ]), - hdrs = glob([ - "include/llvm/FileCheck/*.h", - ]), - includes = ["include"], - deps = [":Support"], -) - -cc_binary( - name = "FileCheck", - testonly = 1, - srcs = glob([ - "utils/FileCheck/*.cpp", - "utils/FileCheck/*.h", - ]), - copts = llvm_copts, - linkopts = llvm_linkopts, - stamp = 0, - deps = [ - ":FileCheckLib", - ":Support", - ], -) - -llvm_target_list = [ - { - "name": "AArch64", - "lower_name": "aarch64", - "short_name": "AArch64", - "dir_name": "AArch64", - "tbl_outs": [ - ("-gen-register-bank", "lib/Target/AArch64/AArch64GenRegisterBank.inc"), - ("-gen-register-info", "lib/Target/AArch64/AArch64GenRegisterInfo.inc"), - ("-gen-instr-info", "lib/Target/AArch64/AArch64GenInstrInfo.inc"), - ("-gen-emitter", "lib/Target/AArch64/AArch64GenMCCodeEmitter.inc"), - ("-gen-pseudo-lowering", "lib/Target/AArch64/AArch64GenMCPseudoLowering.inc"), - ("-gen-asm-writer", "lib/Target/AArch64/AArch64GenAsmWriter.inc"), - ("-gen-asm-writer -asmwriternum=1", "lib/Target/AArch64/AArch64GenAsmWriter1.inc"), - ("-gen-asm-matcher", "lib/Target/AArch64/AArch64GenAsmMatcher.inc"), - ("-gen-dag-isel", "lib/Target/AArch64/AArch64GenDAGISel.inc"), - ("-gen-fast-isel", "lib/Target/AArch64/AArch64GenFastISel.inc"), - ("-gen-global-isel", "lib/Target/AArch64/AArch64GenGlobalISel.inc"), - ("-gen-global-isel-combiner -combiners=AArch64PreLegalizerCombinerHelper", "lib/Target/AArch64/AArch64GenPreLegalizeGICombiner.inc"), - ("-gen-global-isel-combiner -combiners=AArch64PostLegalizerCombinerHelper", "lib/Target/AArch64/AArch64GenPostLegalizeGICombiner.inc"), - ("-gen-global-isel-combiner -combiners=AArch64PostLegalizerLoweringHelper", "lib/Target/AArch64/AArch64GenPostLegalizeGILowering.inc"), - ("-gen-callingconv", "lib/Target/AArch64/AArch64GenCallingConv.inc"), - ("-gen-subtarget", "lib/Target/AArch64/AArch64GenSubtargetInfo.inc"), - ("-gen-disassembler", "lib/Target/AArch64/AArch64GenDisassemblerTables.inc"), - ("-gen-searchable-tables", "lib/Target/AArch64/AArch64GenSystemOperands.inc"), - ], - }, - { - "name": "AMDGPU", - "lower_name": "amdgpu", - "short_name": "AMDGPU", - "dir_name": "AMDGPU", - "tbl_outs": [ - ("-gen-register-bank", "lib/Target/AMDGPU/AMDGPUGenRegisterBank.inc"), - ("-gen-register-info", "lib/Target/AMDGPU/AMDGPUGenRegisterInfo.inc"), - ("-gen-instr-info", "lib/Target/AMDGPU/AMDGPUGenInstrInfo.inc"), - ("-gen-emitter", "lib/Target/AMDGPU/AMDGPUGenMCCodeEmitter.inc"), - ("-gen-pseudo-lowering", "lib/Target/AMDGPU/AMDGPUGenMCPseudoLowering.inc"), - ("-gen-asm-writer", "lib/Target/AMDGPU/AMDGPUGenAsmWriter.inc"), - ("-gen-asm-matcher", "lib/Target/AMDGPU/AMDGPUGenAsmMatcher.inc"), - ("-gen-dag-isel", "lib/Target/AMDGPU/AMDGPUGenDAGISel.inc"), - ("-gen-callingconv", "lib/Target/AMDGPU/AMDGPUGenCallingConv.inc"), - ("-gen-subtarget", "lib/Target/AMDGPU/AMDGPUGenSubtargetInfo.inc"), - ("-gen-disassembler", "lib/Target/AMDGPU/AMDGPUGenDisassemblerTables.inc"), - ("-gen-searchable-tables", "lib/Target/AMDGPU/AMDGPUGenSearchableTables.inc"), - ], - "tbl_deps": [ - ":amdgpu_isel_target_gen", - ], - }, - { - "name": "ARM", - "lower_name": "arm", - "short_name": "ARM", - "dir_name": "ARM", - "tbl_outs": [ - ("-gen-register-bank", "lib/Target/ARM/ARMGenRegisterBank.inc"), - ("-gen-register-info", "lib/Target/ARM/ARMGenRegisterInfo.inc"), - ("-gen-searchable-tables", "lib/Target/ARM/ARMGenSystemRegister.inc"), - ("-gen-instr-info", "lib/Target/ARM/ARMGenInstrInfo.inc"), - ("-gen-emitter", "lib/Target/ARM/ARMGenMCCodeEmitter.inc"), - ("-gen-pseudo-lowering", "lib/Target/ARM/ARMGenMCPseudoLowering.inc"), - ("-gen-asm-writer", "lib/Target/ARM/ARMGenAsmWriter.inc"), - ("-gen-asm-matcher", "lib/Target/ARM/ARMGenAsmMatcher.inc"), - ("-gen-dag-isel", "lib/Target/ARM/ARMGenDAGISel.inc"), - ("-gen-fast-isel", "lib/Target/ARM/ARMGenFastISel.inc"), - ("-gen-global-isel", "lib/Target/ARM/ARMGenGlobalISel.inc"), - ("-gen-callingconv", "lib/Target/ARM/ARMGenCallingConv.inc"), - ("-gen-subtarget", "lib/Target/ARM/ARMGenSubtargetInfo.inc"), - ("-gen-disassembler", "lib/Target/ARM/ARMGenDisassemblerTables.inc"), - ], - }, - { - "name": "NVPTX", - "lower_name": "nvptx", - "short_name": "NVPTX", - "dir_name": "NVPTX", - "tbl_outs": [ - ("-gen-register-info", "lib/Target/NVPTX/NVPTXGenRegisterInfo.inc"), - ("-gen-instr-info", "lib/Target/NVPTX/NVPTXGenInstrInfo.inc"), - ("-gen-asm-writer", "lib/Target/NVPTX/NVPTXGenAsmWriter.inc"), - ("-gen-dag-isel", "lib/Target/NVPTX/NVPTXGenDAGISel.inc"), - ("-gen-subtarget", "lib/Target/NVPTX/NVPTXGenSubtargetInfo.inc"), - ], - }, - { - "name": "PowerPC", - "lower_name": "powerpc", - "short_name": "PPC", - "dir_name": "PowerPC", - "tbl_outs": [ - ("-gen-asm-writer", "lib/Target/PowerPC/PPCGenAsmWriter.inc"), - ("-gen-asm-matcher", "lib/Target/PowerPC/PPCGenAsmMatcher.inc"), - ("-gen-emitter", "lib/Target/PowerPC/PPCGenMCCodeEmitter.inc"), - ("-gen-register-info", "lib/Target/PowerPC/PPCGenRegisterInfo.inc"), - ("-gen-instr-info", "lib/Target/PowerPC/PPCGenInstrInfo.inc"), - ("-gen-dag-isel", "lib/Target/PowerPC/PPCGenDAGISel.inc"), - ("-gen-fast-isel", "lib/Target/PowerPC/PPCGenFastISel.inc"), - ("-gen-callingconv", "lib/Target/PowerPC/PPCGenCallingConv.inc"), - ("-gen-subtarget", "lib/Target/PowerPC/PPCGenSubtargetInfo.inc"), - ("-gen-disassembler", "lib/Target/PowerPC/PPCGenDisassemblerTables.inc"), - ("-gen-register-bank", "lib/Target/PowerPC/PPCGenRegisterBank.inc"), - ("-gen-global-isel", "lib/Target/PowerPC/PPCGenGlobalISel.inc"), - ], - }, - { - "name": "SystemZ", - "lower_name": "system_z", - "short_name": "SystemZ", - "dir_name": "SystemZ", - "tbl_outs": [ - ("-gen-asm-writer", "lib/Target/SystemZ/SystemZGenAsmWriter.inc"), - ("-gen-asm-matcher", "lib/Target/SystemZ/SystemZGenAsmMatcher.inc"), - ("-gen-emitter", "lib/Target/SystemZ/SystemZGenMCCodeEmitter.inc"), - ("-gen-register-info", "lib/Target/SystemZ/SystemZGenRegisterInfo.inc"), - ("-gen-instr-info", "lib/Target/SystemZ/SystemZGenInstrInfo.inc"), - ("-gen-dag-isel", "lib/Target/SystemZ/SystemZGenDAGISel.inc"), - ("-gen-callingconv", "lib/Target/SystemZ/SystemZGenCallingConv.inc"), - ("-gen-subtarget", "lib/Target/SystemZ/SystemZGenSubtargetInfo.inc"), - ("-gen-disassembler", "lib/Target/SystemZ/SystemZGenDisassemblerTables.inc"), - ], - }, - { - "name": "X86", - "lower_name": "x86", - "short_name": "X86", - "dir_name": "X86", - "tbl_outs": [ - ("-gen-register-bank", "lib/Target/X86/X86GenRegisterBank.inc"), - ("-gen-register-info", "lib/Target/X86/X86GenRegisterInfo.inc"), - ("-gen-disassembler", "lib/Target/X86/X86GenDisassemblerTables.inc"), - ("-gen-instr-info", "lib/Target/X86/X86GenInstrInfo.inc"), - ("-gen-asm-writer", "lib/Target/X86/X86GenAsmWriter.inc"), - ("-gen-asm-writer -asmwriternum=1", "lib/Target/X86/X86GenAsmWriter1.inc"), - ("-gen-asm-matcher", "lib/Target/X86/X86GenAsmMatcher.inc"), - ("-gen-dag-isel", "lib/Target/X86/X86GenDAGISel.inc"), - ("-gen-fast-isel", "lib/Target/X86/X86GenFastISel.inc"), - ("-gen-global-isel", "lib/Target/X86/X86GenGlobalISel.inc"), - ("-gen-callingconv", "lib/Target/X86/X86GenCallingConv.inc"), - ("-gen-subtarget", "lib/Target/X86/X86GenSubtargetInfo.inc"), - ("-gen-x86-EVEX2VEX-tables", "lib/Target/X86/X86GenEVEX2VEXTables.inc"), - ("-gen-exegesis", "lib/Target/X86/X86GenExegesis.inc"), - ], - }, -] - -filegroup( - name = "common_target_td_sources", - srcs = glob([ - "include/llvm/CodeGen/*.td", - "include/llvm/Frontend/Directive/*.td", - "include/llvm/IR/Intrinsics*.td", - "include/llvm/TableGen/*.td", - "include/llvm/Target/*.td", - "include/llvm/Target/GlobalISel/*.td", - ]), -) - -gentbl( - name = "amdgpu_isel_target_gen", - tbl_outs = [ - ("-gen-global-isel", "lib/Target/AMDGPU/AMDGPUGenGlobalISel.inc"), - ("-gen-global-isel-combiner -combiners=AMDGPUPreLegalizerCombinerHelper", "lib/Target/AMDGPU/AMDGPUGenPreLegalizeGICombiner.inc"), - ("-gen-global-isel-combiner -combiners=AMDGPUPostLegalizerCombinerHelper", "lib/Target/AMDGPU/AMDGPUGenPostLegalizeGICombiner.inc"), - ("-gen-global-isel-combiner -combiners=AMDGPURegBankCombinerHelper", "lib/Target/AMDGPU/AMDGPUGenRegBankGICombiner.inc"), - ], - tblgen = ":llvm-tblgen", - td_file = "lib/Target/AMDGPU/AMDGPUGISel.td", - td_srcs = [ - ":common_target_td_sources", - ] + glob([ - "lib/Target/AMDGPU/*.td", - ]), -) - -gentbl( - name = "r600_target_gen", - tbl_outs = [ - ("-gen-asm-writer", "lib/Target/AMDGPU/R600GenAsmWriter.inc"), - ("-gen-callingconv", "lib/Target/AMDGPU/R600GenCallingConv.inc"), - ("-gen-dag-isel", "lib/Target/AMDGPU/R600GenDAGISel.inc"), - ("-gen-dfa-packetizer", "lib/Target/AMDGPU/R600GenDFAPacketizer.inc"), - ("-gen-instr-info", "lib/Target/AMDGPU/R600GenInstrInfo.inc"), - ("-gen-emitter", "lib/Target/AMDGPU/R600GenMCCodeEmitter.inc"), - ("-gen-register-info", "lib/Target/AMDGPU/R600GenRegisterInfo.inc"), - ("-gen-subtarget", "lib/Target/AMDGPU/R600GenSubtargetInfo.inc"), - ], - tblgen = ":llvm-tblgen", - td_file = "lib/Target/AMDGPU/R600.td", - td_srcs = [ - ":common_target_td_sources", - ] + glob([ - "lib/Target/AMDGPU/*.td", - ]), -) - -[gentbl( - name = target["name"] + "CommonTableGen", - tbl_outs = target["tbl_outs"], - tblgen = ":llvm-tblgen", - td_file = "lib/Target/" + target["dir_name"] + "/" + target["short_name"] + ".td", - td_srcs = [ - ":common_target_td_sources", - ] + glob([ - "lib/Target/" + target["dir_name"] + "/*.td", - "lib/Target/" + target["name"] + "/GISel/*.td", - ]), - deps = target.get("tbl_deps", []), -) for target in llvm_target_list] - -# This target is used to provide *.def files to x86_code_gen. -# Files with '.def' extension are not allowed in 'srcs' of 'cc_library' rule. -cc_library( - name = "x86_defs", - hdrs = glob([ - "lib/Target/X86/*.def", - ]), - visibility = ["//visibility:private"], -) - -# This filegroup provides the docker build script in LLVM repo -filegroup( - name = "docker", - srcs = glob([ - "utils/docker/build_docker_image.sh", - ]), - visibility = ["//visibility:public"], -) - -py_binary( - name = "lit", - srcs = ["utils/lit/lit.py"] + glob(["utils/lit/lit/**/*.py"]), -) - -cc_binary( - name = "count", - srcs = ["utils/count/count.c"], -) - -cc_binary( - name = "not", - srcs = ["utils/not/not.cpp"], - copts = llvm_copts, - linkopts = llvm_linkopts, - deps = [ - ":Support", - ], -) - -cc_library( - name = "AllTargetsCodeGens", - deps = [ - target["name"] + "CodeGen" - for target in llvm_target_list - ], -) - -exports_files([ - "include/llvm/Frontend/OpenMP/OMP.td", -]) - -filegroup( - name = "omp_td_files", - srcs = glob([ - "include/llvm/Frontend/OpenMP/*.td", - "include/llvm/Frontend/Directive/*.td", - ]), -) - -gentbl( - name = "omp_gen", - tbl_outs = [("--gen-directive-decl", "include/llvm/Frontend/OpenMP/OMP.h.inc")], - tblgen = ":llvm-tblgen", - td_file = "include/llvm/Frontend/OpenMP/OMP.td", - td_srcs = [ - ":omp_td_files", - ], -) - -gentbl( - name = "omp_gen_impl", - tbl_outs = [("--gen-directive-impl", "include/llvm/Frontend/OpenMP/OMP.inc")], - tblgen = ":llvm-tblgen", - td_file = "include/llvm/Frontend/OpenMP/OMP.td", - td_srcs = [ - ":omp_td_files", - ], -) - -# TODO(b/159809163): autogenerate this after enabling release-mode ML -# InlineAdvisor -cc_library( - name = "Analysis", - srcs = glob( - [ - "lib/Analysis/*.c", - "lib/Analysis/*.cpp", - "lib/Analysis/*.inc", - "include/llvm/Transforms/Utils/Local.h", - "include/llvm/Transforms/Scalar.h", - "lib/Analysis/*.h", - ], - exclude = [ - "lib/Analysis/DevelopmentModeInlineAdvisor.cpp", - "lib/Analysis/MLInlineAdvisor.cpp", - "lib/Analysis/ReleaseModeModelRunner.cpp", - "lib/Analysis/TFUtils.cpp", - ], - ), - hdrs = glob([ - "include/llvm/Analysis/*.h", - "include/llvm/Analysis/*.def", - "include/llvm/Analysis/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":BinaryFormat", - ":Core", - ":Object", - ":ProfileData", - ":Support", - ":config", - ], -) - -########################## Begin generated content ########################## -cc_library( - name = "AArch64AsmParser", - srcs = glob([ - "lib/Target/AArch64/AsmParser/*.c", - "lib/Target/AArch64/AsmParser/*.cpp", - "lib/Target/AArch64/AsmParser/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/AArch64/AsmParser/*.h", - "include/llvm/Target/AArch64/AsmParser/*.def", - "include/llvm/Target/AArch64/AsmParser/*.inc", - "lib/Target/AArch64/AsmParser/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/AArch64"], - deps = [ - ":AArch64Desc", - ":AArch64Info", - ":AArch64Utils", - ":MC", - ":MCParser", - ":Support", - ":config", - ], -) - -cc_library( - name = "AArch64CodeGen", - srcs = glob([ - "lib/Target/AArch64/*.c", - "lib/Target/AArch64/*.cpp", - "lib/Target/AArch64/*.inc", - "lib/Target/AArch64/GISel/*.cpp", - ]), - hdrs = glob([ - "include/llvm/Target/AArch64/*.h", - "include/llvm/Target/AArch64/*.def", - "include/llvm/Target/AArch64/*.inc", - "lib/Target/AArch64/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/AArch64"], - deps = [ - ":AArch64Desc", - ":AArch64Info", - ":AArch64Utils", - ":Analysis", - ":AsmPrinter", - ":CFGuard", - ":CodeGen", - ":Core", - ":GlobalISel", - ":MC", - ":Scalar", - ":SelectionDAG", - ":Support", - ":Target", - ":TransformUtils", - ":config", - ], -) - -cc_library( - name = "AArch64Desc", - srcs = glob([ - "lib/Target/AArch64/MCTargetDesc/*.c", - "lib/Target/AArch64/MCTargetDesc/*.cpp", - "lib/Target/AArch64/MCTargetDesc/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/AArch64/MCTargetDesc/*.h", - "include/llvm/Target/AArch64/MCTargetDesc/*.def", - "include/llvm/Target/AArch64/MCTargetDesc/*.inc", - "lib/Target/AArch64/MCTargetDesc/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/AArch64"], - deps = [ - ":AArch64CommonTableGen", - ":AArch64Info", - ":AArch64Utils", - ":BinaryFormat", - ":MC", - ":Support", - ":attributes_gen", - ":config", - ":intrinsic_enums_gen", - ":intrinsics_impl_gen", - ], -) - -cc_library( - name = "AArch64Disassembler", - srcs = glob([ - "lib/Target/AArch64/Disassembler/*.c", - "lib/Target/AArch64/Disassembler/*.cpp", - "lib/Target/AArch64/Disassembler/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/AArch64/Disassembler/*.h", - "include/llvm/Target/AArch64/Disassembler/*.def", - "include/llvm/Target/AArch64/Disassembler/*.inc", - "lib/Target/AArch64/Disassembler/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/AArch64"], - deps = [ - ":AArch64Desc", - ":AArch64Info", - ":AArch64Utils", - ":MC", - ":MCDisassembler", - ":Support", - ":config", - ], -) - -cc_library( - name = "AArch64Info", - srcs = glob([ - "lib/Target/AArch64/TargetInfo/*.c", - "lib/Target/AArch64/TargetInfo/*.cpp", - "lib/Target/AArch64/TargetInfo/*.inc", - "lib/Target/AArch64/MCTargetDesc/*.h", - ]), - hdrs = glob([ - "include/llvm/Target/AArch64/TargetInfo/*.h", - "include/llvm/Target/AArch64/TargetInfo/*.def", - "include/llvm/Target/AArch64/TargetInfo/*.inc", - "lib/Target/AArch64/*.def", - "lib/Target/AArch64/AArch64*.h", - "lib/Target/AArch64/TargetInfo/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/AArch64"], - deps = [ - ":CodeGen", - ":Support", - ":Target", - ":config", - ], -) - -cc_library( - name = "AArch64Utils", - srcs = glob([ - "lib/Target/AArch64/Utils/*.c", - "lib/Target/AArch64/Utils/*.cpp", - "lib/Target/AArch64/Utils/*.inc", - "lib/Target/AArch64/MCTargetDesc/*.h", - ]), - hdrs = glob([ - "include/llvm/Target/AArch64/Utils/*.h", - "include/llvm/Target/AArch64/Utils/*.def", - "include/llvm/Target/AArch64/Utils/*.inc", - "lib/Target/AArch64/Utils/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/AArch64"], - deps = [ - ":AArch64CommonTableGen", - ":MC", - ":Support", - ":config", - ], -) - -cc_library( - name = "AMDGPUAsmParser", - srcs = glob([ - "lib/Target/AMDGPU/AsmParser/*.c", - "lib/Target/AMDGPU/AsmParser/*.cpp", - "lib/Target/AMDGPU/AsmParser/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/AMDGPU/AsmParser/*.h", - "include/llvm/Target/AMDGPU/AsmParser/*.def", - "include/llvm/Target/AMDGPU/AsmParser/*.inc", - "lib/Target/AMDGPU/AsmParser/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/AMDGPU"], - deps = [ - ":AMDGPUDesc", - ":AMDGPUInfo", - ":AMDGPUUtils", - ":MC", - ":MCParser", - ":Support", - ":config", - ], -) - -cc_library( - name = "AMDGPUCodeGen", - srcs = glob([ - "lib/Target/AMDGPU/*.c", - "lib/Target/AMDGPU/*.cpp", - "lib/Target/AMDGPU/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/AMDGPU/*.h", - "include/llvm/Target/AMDGPU/*.def", - "include/llvm/Target/AMDGPU/*.inc", - "lib/Target/AMDGPU/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/AMDGPU"], - deps = [ - ":AMDGPUDesc", - ":AMDGPUInfo", - ":AMDGPUUtils", - ":Analysis", - ":AsmPrinter", - ":BinaryFormat", - ":CodeGen", - ":Core", - ":GlobalISel", - ":IPO", - ":MC", - ":MIRParser", - ":Passes", - ":Scalar", - ":SelectionDAG", - ":Support", - ":Target", - ":TransformUtils", - ":Vectorize", - ":config", - ], -) - -cc_library( - name = "AMDGPUDesc", - srcs = glob([ - "lib/Target/AMDGPU/MCTargetDesc/*.c", - "lib/Target/AMDGPU/MCTargetDesc/*.cpp", - "lib/Target/AMDGPU/MCTargetDesc/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/AMDGPU/MCTargetDesc/*.h", - "include/llvm/Target/AMDGPU/MCTargetDesc/*.def", - "include/llvm/Target/AMDGPU/MCTargetDesc/*.inc", - "lib/Target/AMDGPU/MCTargetDesc/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/AMDGPU"], - deps = [ - ":AMDGPUInfo", - ":AMDGPUUtils", - ":BinaryFormat", - ":Core", - ":MC", - ":Support", - ":config", - ], -) - -cc_library( - name = "AMDGPUDisassembler", - srcs = glob([ - "lib/Target/AMDGPU/Disassembler/*.c", - "lib/Target/AMDGPU/Disassembler/*.cpp", - "lib/Target/AMDGPU/Disassembler/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/AMDGPU/Disassembler/*.h", - "include/llvm/Target/AMDGPU/Disassembler/*.def", - "include/llvm/Target/AMDGPU/Disassembler/*.inc", - "lib/Target/AMDGPU/Disassembler/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/AMDGPU"], - deps = [ - ":AMDGPUDesc", - ":AMDGPUInfo", - ":AMDGPUUtils", - ":MC", - ":MCDisassembler", - ":Support", - ":config", - ], -) - -cc_library( - name = "AMDGPUInfo", - srcs = glob([ - "lib/Target/AMDGPU/TargetInfo/*.c", - "lib/Target/AMDGPU/TargetInfo/*.cpp", - "lib/Target/AMDGPU/TargetInfo/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/AMDGPU/TargetInfo/*.h", - "include/llvm/Target/AMDGPU/TargetInfo/*.def", - "include/llvm/Target/AMDGPU/TargetInfo/*.inc", - "lib/Target/AMDGPU/TargetInfo/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/AMDGPU"], - deps = [ - ":AMDGPUCommonTableGen", - ":Core", - ":Support", - ":config", - ":r600_target_gen", - ], -) - -cc_library( - name = "AMDGPUUtils", - srcs = glob([ - "lib/Target/AMDGPU/Utils/*.c", - "lib/Target/AMDGPU/Utils/*.cpp", - "lib/Target/AMDGPU/Utils/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/AMDGPU/Utils/*.h", - "include/llvm/Target/AMDGPU/Utils/*.def", - "include/llvm/Target/AMDGPU/Utils/*.inc", - "lib/Target/AMDGPU/Utils/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/AMDGPU"], - deps = [ - ":AMDGPUCommonTableGen", - ":BinaryFormat", - ":Core", - ":MC", - ":Support", - ":config", - ":r600_target_gen", - ], -) - -cc_library( - name = "ARCCodeGen", - srcs = glob([ - "lib/Target/ARC/*.c", - "lib/Target/ARC/*.cpp", - "lib/Target/ARC/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/ARC/*.h", - "include/llvm/Target/ARC/*.def", - "include/llvm/Target/ARC/*.inc", - "lib/Target/ARC/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/ARC"], - deps = [ - ":ARCDesc", - ":ARCInfo", - ":Analysis", - ":AsmPrinter", - ":CodeGen", - ":Core", - ":MC", - ":SelectionDAG", - ":Support", - ":Target", - ":TransformUtils", - ":config", - ], -) - -cc_library( - name = "ARCDesc", - srcs = glob([ - "lib/Target/ARC/MCTargetDesc/*.c", - "lib/Target/ARC/MCTargetDesc/*.cpp", - "lib/Target/ARC/MCTargetDesc/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/ARC/MCTargetDesc/*.h", - "include/llvm/Target/ARC/MCTargetDesc/*.def", - "include/llvm/Target/ARC/MCTargetDesc/*.inc", - "lib/Target/ARC/MCTargetDesc/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/ARC"], - deps = [ - ":ARCInfo", - ":MC", - ":Support", - ":config", - ], -) - -cc_library( - name = "ARCDisassembler", - srcs = glob([ - "lib/Target/ARC/Disassembler/*.c", - "lib/Target/ARC/Disassembler/*.cpp", - "lib/Target/ARC/Disassembler/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/ARC/Disassembler/*.h", - "include/llvm/Target/ARC/Disassembler/*.def", - "include/llvm/Target/ARC/Disassembler/*.inc", - "lib/Target/ARC/Disassembler/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/ARC"], - deps = [ - ":ARCInfo", - ":MCDisassembler", - ":Support", - ":config", - ], -) - -cc_library( - name = "ARCInfo", - srcs = glob([ - "lib/Target/ARC/TargetInfo/*.c", - "lib/Target/ARC/TargetInfo/*.cpp", - "lib/Target/ARC/TargetInfo/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/ARC/TargetInfo/*.h", - "include/llvm/Target/ARC/TargetInfo/*.def", - "include/llvm/Target/ARC/TargetInfo/*.inc", - "lib/Target/ARC/TargetInfo/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/ARC"], - deps = [ - ":Support", - ":config", - ], -) - -cc_library( - name = "ARMAsmParser", - srcs = glob([ - "lib/Target/ARM/AsmParser/*.c", - "lib/Target/ARM/AsmParser/*.cpp", - "lib/Target/ARM/AsmParser/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/ARM/AsmParser/*.h", - "include/llvm/Target/ARM/AsmParser/*.def", - "include/llvm/Target/ARM/AsmParser/*.inc", - "lib/Target/ARM/AsmParser/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/ARM"], - deps = [ - ":ARMDesc", - ":ARMInfo", - ":ARMUtils", - ":MC", - ":MCParser", - ":Support", - ":config", - ], -) - -cc_library( - name = "ARMCodeGen", - srcs = glob([ - "lib/Target/ARM/*.c", - "lib/Target/ARM/*.cpp", - "lib/Target/ARM/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/ARM/*.h", - "include/llvm/Target/ARM/*.def", - "include/llvm/Target/ARM/*.inc", - "lib/Target/ARM/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/ARM"], - deps = [ - ":ARMDesc", - ":ARMInfo", - ":ARMUtils", - ":Analysis", - ":AsmPrinter", - ":CFGuard", - ":CodeGen", - ":Core", - ":GlobalISel", - ":MC", - ":Scalar", - ":SelectionDAG", - ":Support", - ":Target", - ":TransformUtils", - ":config", - ], -) - -cc_library( - name = "ARMDesc", - srcs = glob([ - "lib/Target/ARM/MCTargetDesc/*.c", - "lib/Target/ARM/MCTargetDesc/*.cpp", - "lib/Target/ARM/MCTargetDesc/*.inc", - "lib/Target/ARM/*.h", - "include/llvm/CodeGen/GlobalISel/*.h", - ]), - hdrs = glob([ - "include/llvm/Target/ARM/MCTargetDesc/*.h", - "include/llvm/Target/ARM/MCTargetDesc/*.def", - "include/llvm/Target/ARM/MCTargetDesc/*.inc", - "lib/Target/ARM/MCTargetDesc/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/ARM"], - deps = [ - ":ARMCommonTableGen", - ":ARMInfo", - ":ARMUtils", - ":BinaryFormat", - ":MC", - ":MCDisassembler", - ":Support", - ":attributes_gen", - ":config", - ":intrinsic_enums_gen", - ":intrinsics_impl_gen", - ], -) - -cc_library( - name = "ARMDisassembler", - srcs = glob([ - "lib/Target/ARM/Disassembler/*.c", - "lib/Target/ARM/Disassembler/*.cpp", - "lib/Target/ARM/Disassembler/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/ARM/Disassembler/*.h", - "include/llvm/Target/ARM/Disassembler/*.def", - "include/llvm/Target/ARM/Disassembler/*.inc", - "lib/Target/ARM/Disassembler/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/ARM"], - deps = [ - ":ARMDesc", - ":ARMInfo", - ":ARMUtils", - ":MCDisassembler", - ":Support", - ":config", - ], -) - -cc_library( - name = "ARMInfo", - srcs = glob([ - "lib/Target/ARM/TargetInfo/*.c", - "lib/Target/ARM/TargetInfo/*.cpp", - "lib/Target/ARM/TargetInfo/*.inc", - "lib/Target/ARM/MCTargetDesc/*.h", - ]), - hdrs = glob([ - "include/llvm/Target/ARM/TargetInfo/*.h", - "include/llvm/Target/ARM/TargetInfo/*.def", - "include/llvm/Target/ARM/TargetInfo/*.inc", - "lib/Target/ARM/TargetInfo/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/ARM"], - deps = [ - ":ARMCommonTableGen", - ":Support", - ":Target", - ":config", - ], -) - -cc_library( - name = "ARMUtils", - srcs = glob([ - "lib/Target/ARM/Utils/*.c", - "lib/Target/ARM/Utils/*.cpp", - "lib/Target/ARM/Utils/*.inc", - "lib/Target/ARM/MCTargetDesc/*.h", - ]), - hdrs = glob([ - "include/llvm/Target/ARM/Utils/*.h", - "include/llvm/Target/ARM/Utils/*.def", - "include/llvm/Target/ARM/Utils/*.inc", - "lib/Target/ARM/Utils/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/ARM"], - deps = [ - ":ARMCommonTableGen", - ":MC", - ":Support", - ":config", - ], -) - -cc_library( - name = "AVRAsmParser", - srcs = glob([ - "lib/Target/AVR/AsmParser/*.c", - "lib/Target/AVR/AsmParser/*.cpp", - "lib/Target/AVR/AsmParser/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/AVR/AsmParser/*.h", - "include/llvm/Target/AVR/AsmParser/*.def", - "include/llvm/Target/AVR/AsmParser/*.inc", - "lib/Target/AVR/AsmParser/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/AVR"], - deps = [ - ":AVRDesc", - ":AVRInfo", - ":MC", - ":MCParser", - ":Support", - ":config", - ], -) - -cc_library( - name = "AVRCodeGen", - srcs = glob([ - "lib/Target/AVR/*.c", - "lib/Target/AVR/*.cpp", - "lib/Target/AVR/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/AVR/*.h", - "include/llvm/Target/AVR/*.def", - "include/llvm/Target/AVR/*.inc", - "lib/Target/AVR/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/AVR"], - deps = [ - ":AVRDesc", - ":AVRInfo", - ":AsmPrinter", - ":CodeGen", - ":Core", - ":MC", - ":SelectionDAG", - ":Support", - ":Target", - ":config", - ], -) - -cc_library( - name = "AVRDesc", - srcs = glob([ - "lib/Target/AVR/MCTargetDesc/*.c", - "lib/Target/AVR/MCTargetDesc/*.cpp", - "lib/Target/AVR/MCTargetDesc/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/AVR/MCTargetDesc/*.h", - "include/llvm/Target/AVR/MCTargetDesc/*.def", - "include/llvm/Target/AVR/MCTargetDesc/*.inc", - "lib/Target/AVR/MCTargetDesc/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/AVR"], - deps = [ - ":AVRInfo", - ":MC", - ":Support", - ":config", - ], -) - -cc_library( - name = "AVRDisassembler", - srcs = glob([ - "lib/Target/AVR/Disassembler/*.c", - "lib/Target/AVR/Disassembler/*.cpp", - "lib/Target/AVR/Disassembler/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/AVR/Disassembler/*.h", - "include/llvm/Target/AVR/Disassembler/*.def", - "include/llvm/Target/AVR/Disassembler/*.inc", - "lib/Target/AVR/Disassembler/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/AVR"], - deps = [ - ":AVRInfo", - ":MCDisassembler", - ":Support", - ":config", - ], -) - -cc_library( - name = "AVRInfo", - srcs = glob([ - "lib/Target/AVR/TargetInfo/*.c", - "lib/Target/AVR/TargetInfo/*.cpp", - "lib/Target/AVR/TargetInfo/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/AVR/TargetInfo/*.h", - "include/llvm/Target/AVR/TargetInfo/*.def", - "include/llvm/Target/AVR/TargetInfo/*.inc", - "lib/Target/AVR/TargetInfo/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/AVR"], - deps = [ - ":Support", - ":config", - ], -) - -cc_library( - name = "AggressiveInstCombine", - srcs = glob([ - "lib/Transforms/AggressiveInstCombine/*.c", - "lib/Transforms/AggressiveInstCombine/*.cpp", - "lib/Transforms/AggressiveInstCombine/*.inc", - "lib/Transforms/AggressiveInstCombine/*.h", - ]), - hdrs = glob([ - "include/llvm/Transforms/AggressiveInstCombine/*.h", - "include/llvm/Transforms/AggressiveInstCombine/*.def", - "include/llvm/Transforms/AggressiveInstCombine/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":Analysis", - ":Core", - ":Support", - ":TransformUtils", - ":config", - ], -) - -cc_library( - name = "AsmParser", - srcs = glob([ - "lib/AsmParser/*.c", - "lib/AsmParser/*.cpp", - "lib/AsmParser/*.inc", - "lib/AsmParser/*.h", - ]), - hdrs = glob([ - "include/llvm/AsmParser/*.h", - "include/llvm/AsmParser/*.def", - "include/llvm/AsmParser/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":BinaryFormat", - ":Core", - ":Support", - ":config", - ], -) - -cc_library( - name = "AsmPrinter", - srcs = glob([ - "lib/CodeGen/AsmPrinter/*.c", - "lib/CodeGen/AsmPrinter/*.cpp", - "lib/CodeGen/AsmPrinter/*.inc", - "lib/CodeGen/AsmPrinter/*.h", - ]), - hdrs = glob([ - "include/llvm/CodeGen/AsmPrinter/*.h", - "include/llvm/CodeGen/AsmPrinter/*.def", - "include/llvm/CodeGen/AsmPrinter/*.inc", - "lib/CodeGen/AsmPrinter/*.def", - ]), - copts = llvm_copts, - deps = [ - ":Analysis", - ":BinaryFormat", - ":CodeGen", - ":Core", - ":DebugInfoCodeView", - ":DebugInfoDWARF", - ":DebugInfoMSF", - ":MC", - ":MCParser", - ":Remarks", - ":Support", - ":Target", - ":config", - ], -) - -cc_library( - name = "BPFAsmParser", - srcs = glob([ - "lib/Target/BPF/AsmParser/*.c", - "lib/Target/BPF/AsmParser/*.cpp", - "lib/Target/BPF/AsmParser/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/BPF/AsmParser/*.h", - "include/llvm/Target/BPF/AsmParser/*.def", - "include/llvm/Target/BPF/AsmParser/*.inc", - "lib/Target/BPF/AsmParser/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/BPF"], - deps = [ - ":BPFDesc", - ":BPFInfo", - ":MC", - ":MCParser", - ":Support", - ":config", - ], -) - -cc_library( - name = "BPFCodeGen", - srcs = glob([ - "lib/Target/BPF/*.c", - "lib/Target/BPF/*.cpp", - "lib/Target/BPF/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/BPF/*.h", - "include/llvm/Target/BPF/*.def", - "include/llvm/Target/BPF/*.inc", - "lib/Target/BPF/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/BPF"], - deps = [ - ":AsmPrinter", - ":BPFDesc", - ":BPFInfo", - ":CodeGen", - ":Core", - ":IPO", - ":MC", - ":Scalar", - ":SelectionDAG", - ":Support", - ":Target", - ":config", - ], -) - -cc_library( - name = "BPFDesc", - srcs = glob([ - "lib/Target/BPF/MCTargetDesc/*.c", - "lib/Target/BPF/MCTargetDesc/*.cpp", - "lib/Target/BPF/MCTargetDesc/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/BPF/MCTargetDesc/*.h", - "include/llvm/Target/BPF/MCTargetDesc/*.def", - "include/llvm/Target/BPF/MCTargetDesc/*.inc", - "lib/Target/BPF/MCTargetDesc/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/BPF"], - deps = [ - ":BPFInfo", - ":MC", - ":Support", - ":config", - ], -) - -cc_library( - name = "BPFDisassembler", - srcs = glob([ - "lib/Target/BPF/Disassembler/*.c", - "lib/Target/BPF/Disassembler/*.cpp", - "lib/Target/BPF/Disassembler/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/BPF/Disassembler/*.h", - "include/llvm/Target/BPF/Disassembler/*.def", - "include/llvm/Target/BPF/Disassembler/*.inc", - "lib/Target/BPF/Disassembler/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/BPF"], - deps = [ - ":BPFInfo", - ":MCDisassembler", - ":Support", - ":config", - ], -) - -cc_library( - name = "BPFInfo", - srcs = glob([ - "lib/Target/BPF/TargetInfo/*.c", - "lib/Target/BPF/TargetInfo/*.cpp", - "lib/Target/BPF/TargetInfo/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/BPF/TargetInfo/*.h", - "include/llvm/Target/BPF/TargetInfo/*.def", - "include/llvm/Target/BPF/TargetInfo/*.inc", - "lib/Target/BPF/TargetInfo/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/BPF"], - deps = [ - ":Support", - ":config", - ], -) - -cc_library( - name = "BinaryFormat", - srcs = glob([ - "lib/BinaryFormat/*.c", - "lib/BinaryFormat/*.cpp", - "lib/BinaryFormat/*.inc", - "lib/BinaryFormat/*.h", - ]), - hdrs = glob([ - "include/llvm/BinaryFormat/*.h", - "include/llvm/BinaryFormat/*.def", - "include/llvm/BinaryFormat/*.inc", - "include/llvm/BinaryFormat/ELFRelocs/*.def", - "include/llvm/BinaryFormat/WasmRelocs/*.def", - ]), - copts = llvm_copts, - deps = [ - ":Support", - ":config", - ], -) - -cc_library( - name = "BitReader", - srcs = glob([ - "lib/Bitcode/Reader/*.c", - "lib/Bitcode/Reader/*.cpp", - "lib/Bitcode/Reader/*.inc", - "lib/Bitcode/Reader/*.h", - ]), - hdrs = glob([ - "include/llvm/Bitcode/Reader/*.h", - "include/llvm/Bitcode/Reader/*.def", - "include/llvm/Bitcode/Reader/*.inc", - "include/llvm/Bitcode/BitstreamReader.h", - ]), - copts = llvm_copts, - deps = [ - ":BitstreamReader", - ":Core", - ":Support", - ":config", - ], -) - -cc_library( - name = "BitWriter", - srcs = glob([ - "lib/Bitcode/Writer/*.c", - "lib/Bitcode/Writer/*.cpp", - "lib/Bitcode/Writer/*.inc", - "lib/Bitcode/Writer/*.h", - ]), - hdrs = glob([ - "include/llvm/Bitcode/Writer/*.h", - "include/llvm/Bitcode/Writer/*.def", - "include/llvm/Bitcode/Writer/*.inc", - "include/llvm/Bitcode/BitcodeWriter.h", - "include/llvm/Bitcode/BitcodeWriterPass.h", - "include/llvm/Bitcode/BitstreamWriter.h", - ]), - copts = llvm_copts, - deps = [ - ":Analysis", - ":Core", - ":MC", - ":Object", - ":Support", - ":config", - ], -) - -cc_library( - name = "BitstreamReader", - srcs = glob([ - "lib/Bitstream/Reader/*.c", - "lib/Bitstream/Reader/*.cpp", - "lib/Bitstream/Reader/*.inc", - "lib/Bitstream/Reader/*.h", - ]), - hdrs = glob([ - "include/llvm/Bitstream/Reader/*.h", - "include/llvm/Bitstream/Reader/*.def", - "include/llvm/Bitstream/Reader/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":Support", - ":config", - ], -) - -cc_library( - name = "CFGuard", - srcs = glob([ - "lib/Transforms/CFGuard/*.c", - "lib/Transforms/CFGuard/*.cpp", - "lib/Transforms/CFGuard/*.inc", - "lib/Transforms/CFGuard/*.h", - ]), - hdrs = glob([ - "include/llvm/Transforms/CFGuard/*.h", - "include/llvm/Transforms/CFGuard/*.def", - "include/llvm/Transforms/CFGuard/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":Core", - ":Support", - ":config", - ], -) - -cc_library( - name = "CSKYCodeGen", - srcs = glob([ - "lib/Target/CSKY/*.c", - "lib/Target/CSKY/*.cpp", - "lib/Target/CSKY/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/CSKY/*.h", - "include/llvm/Target/CSKY/*.def", - "include/llvm/Target/CSKY/*.inc", - "lib/Target/CSKY/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/CSKY"], - deps = [ - ":CSKYInfo", - ":CodeGen", - ":Core", - ":Support", - ":Target", - ":config", - ], -) - -cc_library( - name = "CSKYInfo", - srcs = glob([ - "lib/Target/CSKY/TargetInfo/*.c", - "lib/Target/CSKY/TargetInfo/*.cpp", - "lib/Target/CSKY/TargetInfo/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/CSKY/TargetInfo/*.h", - "include/llvm/Target/CSKY/TargetInfo/*.def", - "include/llvm/Target/CSKY/TargetInfo/*.inc", - "lib/Target/CSKY/TargetInfo/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/CSKY"], - deps = [ - ":Support", - ":config", - ], -) - -cc_library( - name = "CodeGen", - srcs = glob([ - "lib/CodeGen/*.c", - "lib/CodeGen/*.cpp", - "lib/CodeGen/*.inc", - "lib/CodeGen/LiveDebugValues/*.cpp", - "lib/CodeGen/*.h", - ]), - hdrs = glob([ - "include/llvm/CodeGen/*.h", - "include/llvm/CodeGen/*.def", - "include/llvm/CodeGen/*.inc", - "include/llvm/CodeGen/**/*.h", - ]), - copts = llvm_copts, - deps = [ - ":Analysis", - ":BitReader", - ":BitWriter", - ":Core", - ":Instrumentation", - ":MC", - ":ProfileData", - ":Scalar", - ":Support", - ":Target", - ":TransformUtils", - ":config", - ], -) - -cc_library( - name = "Core", - srcs = glob([ - "lib/IR/*.c", - "lib/IR/*.cpp", - "lib/IR/*.inc", - "include/llvm/Analysis/*.h", - "include/llvm/Bitcode/BitcodeReader.h", - "include/llvm/Bitcode/BitCodes.h", - "include/llvm/Bitcode/LLVMBitCodes.h", - "include/llvm/CodeGen/MachineValueType.h", - "include/llvm/CodeGen/ValueTypes.h", - "lib/IR/*.h", - ]), - hdrs = glob([ - "include/llvm/IR/*.h", - "include/llvm/IR/*.def", - "include/llvm/IR/*.inc", - "include/llvm/*.h", - "include/llvm/Analysis/*.def", - ]), - copts = llvm_copts, - deps = [ - ":BinaryFormat", - ":Remarks", - ":Support", - ":aarch64_enums_gen", - ":amdgcn_enums_gen", - ":arm_enums_gen", - ":attributes_gen", - ":bpf_enums_gen", - ":config", - ":hexagon_enums_gen", - ":intrinsic_enums_gen", - ":intrinsics_impl_gen", - ":mips_enums_gen", - ":nvvm_enums_gen", - ":ppc_enums_gen", - ":r600_enums_gen", - ":riscv_enums_gen", - ":s390_enums_gen", - ":ve_enums_gen", - ":wasm_enums_gen", - ":x86_enums_gen", - ":xcore_enums_gen", - ], -) - -cc_library( - name = "Coroutines", - srcs = glob([ - "lib/Transforms/Coroutines/*.c", - "lib/Transforms/Coroutines/*.cpp", - "lib/Transforms/Coroutines/*.inc", - "lib/Transforms/Coroutines/*.h", - ]), - hdrs = glob([ - "include/llvm/Transforms/Coroutines/*.h", - "include/llvm/Transforms/Coroutines/*.def", - "include/llvm/Transforms/Coroutines/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":Analysis", - ":Core", - ":IPO", - ":Scalar", - ":Support", - ":TransformUtils", - ":config", - ], -) - -cc_library( - name = "Coverage", - srcs = glob([ - "lib/ProfileData/Coverage/*.c", - "lib/ProfileData/Coverage/*.cpp", - "lib/ProfileData/Coverage/*.inc", - "lib/ProfileData/Coverage/*.h", - ]), - hdrs = glob([ - "include/llvm/ProfileData/Coverage/*.h", - "include/llvm/ProfileData/Coverage/*.def", - "include/llvm/ProfileData/Coverage/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":Core", - ":Object", - ":ProfileData", - ":Support", - ":config", - ], -) - -cc_library( - name = "DWARFLinker", - srcs = glob([ - "lib/DWARFLinker/*.c", - "lib/DWARFLinker/*.cpp", - "lib/DWARFLinker/*.inc", - "lib/DWARFLinker/*.h", - ]), - hdrs = glob([ - "include/llvm/DWARFLinker/*.h", - "include/llvm/DWARFLinker/*.def", - "include/llvm/DWARFLinker/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":AsmPrinter", - ":CodeGen", - ":DebugInfoDWARF", - ":MC", - ":Object", - ":Support", - ":config", - ], -) - -cc_library( - name = "DebugInfoCodeView", - srcs = glob([ - "lib/DebugInfo/CodeView/*.c", - "lib/DebugInfo/CodeView/*.cpp", - "lib/DebugInfo/CodeView/*.inc", - "lib/DebugInfo/CodeView/*.h", - ]), - hdrs = glob([ - "include/llvm/DebugInfo/CodeView/*.h", - "include/llvm/DebugInfo/CodeView/*.def", - "include/llvm/DebugInfo/CodeView/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":BinaryFormat", - ":DebugInfoMSF", - ":Support", - ":config", - ], -) - -cc_library( - name = "DebugInfoDWARF", - srcs = glob([ - "lib/DebugInfo/DWARF/*.c", - "lib/DebugInfo/DWARF/*.cpp", - "lib/DebugInfo/DWARF/*.inc", - "lib/DebugInfo/DWARF/*.h", - ]), - hdrs = glob([ - "include/llvm/DebugInfo/DWARF/*.h", - "include/llvm/DebugInfo/DWARF/*.def", - "include/llvm/DebugInfo/DWARF/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":BinaryFormat", - ":MC", - ":Object", - ":Support", - ":config", - ], -) - -cc_library( - name = "DebugInfoGSYM", - srcs = glob([ - "lib/DebugInfo/GSYM/*.c", - "lib/DebugInfo/GSYM/*.cpp", - "lib/DebugInfo/GSYM/*.inc", - "lib/DebugInfo/GSYM/*.h", - ]), - hdrs = glob([ - "include/llvm/DebugInfo/GSYM/*.h", - "include/llvm/DebugInfo/GSYM/*.def", - "include/llvm/DebugInfo/GSYM/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":DebugInfoDWARF", - ":MC", - ":Object", - ":Support", - ":config", - ], -) - -cc_library( - name = "DebugInfoMSF", - srcs = glob([ - "lib/DebugInfo/MSF/*.c", - "lib/DebugInfo/MSF/*.cpp", - "lib/DebugInfo/MSF/*.inc", - "lib/DebugInfo/MSF/*.h", - ]), - hdrs = glob([ - "include/llvm/DebugInfo/MSF/*.h", - "include/llvm/DebugInfo/MSF/*.def", - "include/llvm/DebugInfo/MSF/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":Support", - ":config", - ], -) - -cc_library( - name = "DebugInfoPDB", - srcs = glob([ - "lib/DebugInfo/PDB/*.c", - "lib/DebugInfo/PDB/*.cpp", - "lib/DebugInfo/PDB/*.inc", - "lib/DebugInfo/PDB/*.h", - ]), - hdrs = glob([ - "include/llvm/DebugInfo/PDB/*.h", - "include/llvm/DebugInfo/PDB/*.def", - "include/llvm/DebugInfo/PDB/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":BinaryFormat", - ":DebugInfoCodeView", - ":DebugInfoMSF", - ":Object", - ":Support", - ":config", - ], -) - -cc_library( - name = "Demangle", - srcs = glob([ - "lib/Demangle/*.c", - "lib/Demangle/*.cpp", - "lib/Demangle/*.inc", - "lib/Demangle/*.h", - ]), - hdrs = glob([ - "include/llvm/Demangle/*.h", - "include/llvm/Demangle/*.def", - "include/llvm/Demangle/*.inc", - ]), - copts = llvm_copts, - deps = [":config"], -) - -cc_library( - name = "DlltoolDriver", - srcs = glob([ - "lib/ToolDrivers/llvm-dlltool/*.c", - "lib/ToolDrivers/llvm-dlltool/*.cpp", - "lib/ToolDrivers/llvm-dlltool/*.inc", - "lib/ToolDrivers/llvm-dlltool/*.h", - ]), - hdrs = glob([ - "include/llvm/ToolDrivers/llvm-dlltool/*.h", - "include/llvm/ToolDrivers/llvm-dlltool/*.def", - "include/llvm/ToolDrivers/llvm-dlltool/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":Object", - ":Option", - ":Support", - ":config", - ], -) - -cc_library( - name = "ExecutionEngine", - srcs = glob([ - "lib/ExecutionEngine/*.c", - "lib/ExecutionEngine/*.cpp", - "lib/ExecutionEngine/*.inc", - "lib/ExecutionEngine/*.h", - ]), - hdrs = glob([ - "include/llvm/ExecutionEngine/*.h", - "include/llvm/ExecutionEngine/*.def", - "include/llvm/ExecutionEngine/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":Core", - ":MC", - ":Object", - ":RuntimeDyld", - ":Support", - ":Target", - ":config", - ], -) - -cc_library( - name = "Extensions", - srcs = glob([ - "lib/Extensions/*.c", - "lib/Extensions/*.cpp", - "lib/Extensions/*.inc", - "lib/Extensions/*.h", - ]), - hdrs = glob([ - "include/llvm/Extensions/*.h", - "include/llvm/Extensions/*.def", - "include/llvm/Extensions/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":Support", - ":config", - ], -) - -cc_library( - name = "FrontendOpenMP", - srcs = glob([ - "lib/Frontend/OpenMP/*.c", - "lib/Frontend/OpenMP/*.cpp", - "lib/Frontend/OpenMP/*.inc", - "lib/Frontend/OpenMP/*.h", - ]), - hdrs = glob([ - "include/llvm/Frontend/OpenMP/*.h", - "include/llvm/Frontend/OpenMP/*.def", - "include/llvm/Frontend/OpenMP/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":Core", - ":Support", - ":TransformUtils", - ":config", - ":omp_gen", - ":omp_gen_impl", - ], -) - -filegroup( - name = "acc_td_files", - srcs = glob([ - "include/llvm/Frontend/OpenACC/*.td", - "include/llvm/Frontend/Directive/*.td", - ]), -) - -gentbl( - name = "acc_gen", - library = False, - tbl_outs = [ - ("--gen-directive-decl", "include/llvm/Frontend/OpenACC/ACC.h.inc"), - ], - tblgen = ":llvm-tblgen", - td_file = "include/llvm/Frontend/OpenACC/ACC.td", - td_srcs = [":acc_td_files"], -) - -gentbl( - name = "acc_gen_impl", - library = False, - tbl_outs = [ - ("--gen-directive-impl", "include/llvm/Frontend/OpenACC/ACC.inc"), - ], - tblgen = ":llvm-tblgen", - td_file = "include/llvm/Frontend/OpenACC/ACC.td", - td_srcs = [":acc_td_files"], -) - -cc_library( - name = "FrontendOpenACC", - srcs = glob([ - "lib/Frontend/OpenACC/*.cpp", - ]) + [ - "include/llvm/Frontend/OpenACC/ACC.inc", - ], - hdrs = glob([ - "include/llvm/Frontend/OpenACC/*.h", - ]) + [ - "include/llvm/Frontend/OpenACC/ACC.h.inc", - ], - copts = llvm_copts, - deps = [ - ":Analysis", - ":Core", - ":Support", - ":TransformUtils", - ], -) - -cc_library( - name = "FuzzMutate", - srcs = glob([ - "lib/FuzzMutate/*.c", - "lib/FuzzMutate/*.cpp", - "lib/FuzzMutate/*.inc", - "lib/FuzzMutate/*.h", - ]), - hdrs = glob([ - "include/llvm/FuzzMutate/*.h", - "include/llvm/FuzzMutate/*.def", - "include/llvm/FuzzMutate/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":Analysis", - ":BitReader", - ":BitWriter", - ":Core", - ":Scalar", - ":Support", - ":Target", - ":config", - ], -) - -cc_library( - name = "GlobalISel", - srcs = glob([ - "lib/CodeGen/GlobalISel/*.c", - "lib/CodeGen/GlobalISel/*.cpp", - "lib/CodeGen/GlobalISel/*.inc", - "lib/CodeGen/GlobalISel/*.h", - ]), - hdrs = glob([ - "include/llvm/CodeGen/GlobalISel/*.h", - "include/llvm/CodeGen/GlobalISel/*.def", - "include/llvm/CodeGen/GlobalISel/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":Analysis", - ":CodeGen", - ":Core", - ":MC", - ":SelectionDAG", - ":Support", - ":Target", - ":TransformUtils", - ":config", - ], -) - -cc_library( - name = "HelloNew", - srcs = glob([ - "lib/Transforms/HelloNew/*.c", - "lib/Transforms/HelloNew/*.cpp", - "lib/Transforms/HelloNew/*.inc", - "lib/Transforms/HelloNew/*.h", - ]), - hdrs = glob([ - "include/llvm/Transforms/HelloNew/*.h", - "include/llvm/Transforms/HelloNew/*.def", - "include/llvm/Transforms/HelloNew/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":Core", - ":Support", - ":config", - ], -) - -cc_library( - name = "HexagonAsmParser", - srcs = glob([ - "lib/Target/Hexagon/AsmParser/*.c", - "lib/Target/Hexagon/AsmParser/*.cpp", - "lib/Target/Hexagon/AsmParser/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/Hexagon/AsmParser/*.h", - "include/llvm/Target/Hexagon/AsmParser/*.def", - "include/llvm/Target/Hexagon/AsmParser/*.inc", - "lib/Target/Hexagon/AsmParser/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/Hexagon"], - deps = [ - ":HexagonDesc", - ":HexagonInfo", - ":MC", - ":MCParser", - ":Support", - ":config", - ], -) - -cc_library( - name = "HexagonCodeGen", - srcs = glob([ - "lib/Target/Hexagon/*.c", - "lib/Target/Hexagon/*.cpp", - "lib/Target/Hexagon/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/Hexagon/*.h", - "include/llvm/Target/Hexagon/*.def", - "include/llvm/Target/Hexagon/*.inc", - "lib/Target/Hexagon/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/Hexagon"], - deps = [ - ":Analysis", - ":AsmPrinter", - ":CodeGen", - ":Core", - ":HexagonAsmParser", - ":HexagonDesc", - ":HexagonInfo", - ":IPO", - ":MC", - ":Scalar", - ":SelectionDAG", - ":Support", - ":Target", - ":TransformUtils", - ":config", - ], -) - -cc_library( - name = "HexagonDesc", - srcs = glob([ - "lib/Target/Hexagon/MCTargetDesc/*.c", - "lib/Target/Hexagon/MCTargetDesc/*.cpp", - "lib/Target/Hexagon/MCTargetDesc/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/Hexagon/MCTargetDesc/*.h", - "include/llvm/Target/Hexagon/MCTargetDesc/*.def", - "include/llvm/Target/Hexagon/MCTargetDesc/*.inc", - "lib/Target/Hexagon/MCTargetDesc/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/Hexagon"], - deps = [ - ":HexagonInfo", - ":MC", - ":Support", - ":config", - ], -) - -cc_library( - name = "HexagonDisassembler", - srcs = glob([ - "lib/Target/Hexagon/Disassembler/*.c", - "lib/Target/Hexagon/Disassembler/*.cpp", - "lib/Target/Hexagon/Disassembler/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/Hexagon/Disassembler/*.h", - "include/llvm/Target/Hexagon/Disassembler/*.def", - "include/llvm/Target/Hexagon/Disassembler/*.inc", - "lib/Target/Hexagon/Disassembler/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/Hexagon"], - deps = [ - ":HexagonDesc", - ":HexagonInfo", - ":MC", - ":MCDisassembler", - ":Support", - ":config", - ], -) - -cc_library( - name = "HexagonInfo", - srcs = glob([ - "lib/Target/Hexagon/TargetInfo/*.c", - "lib/Target/Hexagon/TargetInfo/*.cpp", - "lib/Target/Hexagon/TargetInfo/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/Hexagon/TargetInfo/*.h", - "include/llvm/Target/Hexagon/TargetInfo/*.def", - "include/llvm/Target/Hexagon/TargetInfo/*.inc", - "lib/Target/Hexagon/TargetInfo/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/Hexagon"], - deps = [ - ":Support", - ":config", - ], -) - -cc_library( - name = "IPO", - srcs = glob([ - "lib/Transforms/IPO/*.c", - "lib/Transforms/IPO/*.cpp", - "lib/Transforms/IPO/*.inc", - "include/llvm/Transforms/SampleProfile.h", - "include/llvm-c/Transforms/IPO.h", - "include/llvm-c/Transforms/PassManagerBuilder.h", - "lib/Transforms/IPO/*.h", - ]), - hdrs = glob([ - "include/llvm/Transforms/IPO/*.h", - "include/llvm/Transforms/IPO/*.def", - "include/llvm/Transforms/IPO/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":AggressiveInstCombine", - ":Analysis", - ":BitReader", - ":BitWriter", - ":Core", - ":FrontendOpenMP", - ":IRReader", - ":InstCombine", - ":Instrumentation", - ":Linker", - ":Object", - ":ProfileData", - ":Scalar", - ":Support", - ":TransformUtils", - ":Vectorize", - ":config", - ], -) - -cc_library( - name = "IRReader", - srcs = glob([ - "lib/IRReader/*.c", - "lib/IRReader/*.cpp", - "lib/IRReader/*.inc", - "lib/IRReader/*.h", - ]), - hdrs = glob([ - "include/llvm/IRReader/*.h", - "include/llvm/IRReader/*.def", - "include/llvm/IRReader/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":AsmParser", - ":BitReader", - ":Core", - ":Support", - ":config", - ], -) - -cc_library( - name = "InstCombine", - srcs = glob([ - "lib/Transforms/InstCombine/*.c", - "lib/Transforms/InstCombine/*.cpp", - "lib/Transforms/InstCombine/*.inc", - "lib/Transforms/InstCombine/*.h", - ]), - hdrs = glob([ - "include/llvm/Transforms/InstCombine/*.h", - "include/llvm/Transforms/InstCombine/*.def", - "include/llvm/Transforms/InstCombine/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":Analysis", - ":Core", - ":InstCombineTableGen", - ":Support", - ":TransformUtils", - ":config", - ], -) - -cc_library( - name = "Instrumentation", - srcs = glob([ - "lib/Transforms/Instrumentation/*.c", - "lib/Transforms/Instrumentation/*.cpp", - "lib/Transforms/Instrumentation/*.inc", - "lib/Transforms/Instrumentation/*.h", - ]), - hdrs = glob([ - "include/llvm/Transforms/Instrumentation/*.h", - "include/llvm/Transforms/Instrumentation/*.def", - "include/llvm/Transforms/Instrumentation/*.inc", - "include/llvm/Transforms/GCOVProfiler.h", - "include/llvm/Transforms/Instrumentation.h", - "include/llvm/Transforms/InstrProfiling.h", - "include/llvm/Transforms/PGOInstrumentation.h", - ]), - copts = llvm_copts, - deps = [ - ":Analysis", - ":Core", - ":MC", - ":ProfileData", - ":Support", - ":TransformUtils", - ":config", - ], -) - -cc_library( - name = "InterfaceStub", - srcs = glob([ - "lib/InterfaceStub/*.c", - "lib/InterfaceStub/*.cpp", - "lib/InterfaceStub/*.inc", - "lib/InterfaceStub/*.h", - ]), - hdrs = glob([ - "include/llvm/InterfaceStub/*.h", - "include/llvm/InterfaceStub/*.def", - "include/llvm/InterfaceStub/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":Object", - ":Support", - ":config", - ], -) - -cc_library( - name = "Interpreter", - srcs = glob([ - "lib/ExecutionEngine/Interpreter/*.c", - "lib/ExecutionEngine/Interpreter/*.cpp", - "lib/ExecutionEngine/Interpreter/*.inc", - "lib/ExecutionEngine/Interpreter/*.h", - ]), - hdrs = glob([ - "include/llvm/ExecutionEngine/Interpreter/*.h", - "include/llvm/ExecutionEngine/Interpreter/*.def", - "include/llvm/ExecutionEngine/Interpreter/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":CodeGen", - ":Core", - ":ExecutionEngine", - ":Support", - ":config", - ], -) - -cc_library( - name = "JITLink", - srcs = glob([ - "lib/ExecutionEngine/JITLink/*.c", - "lib/ExecutionEngine/JITLink/*.cpp", - "lib/ExecutionEngine/JITLink/*.inc", - "lib/ExecutionEngine/JITLink/*.h", - ]), - hdrs = glob([ - "include/llvm/ExecutionEngine/JITLink/*.h", - "include/llvm/ExecutionEngine/JITLink/*.def", - "include/llvm/ExecutionEngine/JITLink/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":BinaryFormat", - ":Object", - ":OrcTargetProcess", - ":Support", - ":config", - ], -) - -cc_library( - name = "LTO", - srcs = glob([ - "lib/LTO/*.c", - "lib/LTO/*.cpp", - "lib/LTO/*.inc", - "lib/LTO/*.h", - ]), - hdrs = glob([ - "include/llvm/LTO/*.h", - "include/llvm/LTO/*.def", - "include/llvm/LTO/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":AggressiveInstCombine", - ":Analysis", - ":BinaryFormat", - ":BitReader", - ":BitWriter", - ":CodeGen", - ":Core", - ":Extensions", - ":IPO", - ":InstCombine", - ":Linker", - ":MC", - ":ObjCARC", - ":Object", - ":Passes", - ":Remarks", - ":Scalar", - ":Support", - ":Target", - ":TransformUtils", - ":config", - ], -) - -cc_library( - name = "LanaiAsmParser", - srcs = glob([ - "lib/Target/Lanai/AsmParser/*.c", - "lib/Target/Lanai/AsmParser/*.cpp", - "lib/Target/Lanai/AsmParser/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/Lanai/AsmParser/*.h", - "include/llvm/Target/Lanai/AsmParser/*.def", - "include/llvm/Target/Lanai/AsmParser/*.inc", - "lib/Target/Lanai/AsmParser/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/Lanai"], - deps = [ - ":LanaiDesc", - ":LanaiInfo", - ":MC", - ":MCParser", - ":Support", - ":config", - ], -) - -cc_library( - name = "LanaiCodeGen", - srcs = glob([ - "lib/Target/Lanai/*.c", - "lib/Target/Lanai/*.cpp", - "lib/Target/Lanai/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/Lanai/*.h", - "include/llvm/Target/Lanai/*.def", - "include/llvm/Target/Lanai/*.inc", - "lib/Target/Lanai/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/Lanai"], - deps = [ - ":Analysis", - ":AsmPrinter", - ":CodeGen", - ":Core", - ":LanaiAsmParser", - ":LanaiDesc", - ":LanaiInfo", - ":MC", - ":SelectionDAG", - ":Support", - ":Target", - ":TransformUtils", - ":config", - ], -) - -cc_library( - name = "LanaiDesc", - srcs = glob([ - "lib/Target/Lanai/MCTargetDesc/*.c", - "lib/Target/Lanai/MCTargetDesc/*.cpp", - "lib/Target/Lanai/MCTargetDesc/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/Lanai/MCTargetDesc/*.h", - "include/llvm/Target/Lanai/MCTargetDesc/*.def", - "include/llvm/Target/Lanai/MCTargetDesc/*.inc", - "lib/Target/Lanai/MCTargetDesc/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/Lanai"], - deps = [ - ":LanaiInfo", - ":MC", - ":MCDisassembler", - ":Support", - ":config", - ], -) - -cc_library( - name = "LanaiDisassembler", - srcs = glob([ - "lib/Target/Lanai/Disassembler/*.c", - "lib/Target/Lanai/Disassembler/*.cpp", - "lib/Target/Lanai/Disassembler/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/Lanai/Disassembler/*.h", - "include/llvm/Target/Lanai/Disassembler/*.def", - "include/llvm/Target/Lanai/Disassembler/*.inc", - "lib/Target/Lanai/Disassembler/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/Lanai"], - deps = [ - ":LanaiDesc", - ":LanaiInfo", - ":MC", - ":MCDisassembler", - ":Support", - ":config", - ], -) - -cc_library( - name = "LanaiInfo", - srcs = glob([ - "lib/Target/Lanai/TargetInfo/*.c", - "lib/Target/Lanai/TargetInfo/*.cpp", - "lib/Target/Lanai/TargetInfo/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/Lanai/TargetInfo/*.h", - "include/llvm/Target/Lanai/TargetInfo/*.def", - "include/llvm/Target/Lanai/TargetInfo/*.inc", - "lib/Target/Lanai/TargetInfo/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/Lanai"], - deps = [ - ":Support", - ":config", - ], -) - -cc_library( - name = "LibDriver", - srcs = glob([ - "lib/ToolDrivers/llvm-lib/*.c", - "lib/ToolDrivers/llvm-lib/*.cpp", - "lib/ToolDrivers/llvm-lib/*.inc", - "lib/ToolDrivers/llvm-lib/*.h", - ]), - hdrs = glob([ - "include/llvm/ToolDrivers/llvm-lib/*.h", - "include/llvm/ToolDrivers/llvm-lib/*.def", - "include/llvm/ToolDrivers/llvm-lib/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":BinaryFormat", - ":BitReader", - ":Object", - ":Option", - ":Support", - ":config", - ], -) - -cc_library( - name = "LineEditor", - srcs = glob([ - "lib/LineEditor/*.c", - "lib/LineEditor/*.cpp", - "lib/LineEditor/*.inc", - "lib/LineEditor/*.h", - ]), - hdrs = glob([ - "include/llvm/LineEditor/*.h", - "include/llvm/LineEditor/*.def", - "include/llvm/LineEditor/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":Support", - ":config", - ], -) - -cc_library( - name = "Linker", - srcs = glob([ - "lib/Linker/*.c", - "lib/Linker/*.cpp", - "lib/Linker/*.inc", - "lib/Linker/*.h", - ]), - hdrs = glob([ - "include/llvm/Linker/*.h", - "include/llvm/Linker/*.def", - "include/llvm/Linker/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":Core", - ":Support", - ":TransformUtils", - ":config", - ], -) - -cc_library( - name = "MC", - srcs = glob([ - "lib/MC/*.c", - "lib/MC/*.cpp", - "lib/MC/*.inc", - "lib/MC/*.h", - ]), - hdrs = glob([ - "include/llvm/MC/*.h", - "include/llvm/MC/*.def", - "include/llvm/MC/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":BinaryFormat", - ":DebugInfoCodeView", - ":Support", - ":config", - ], -) - -cc_library( - name = "MCA", - srcs = glob([ - "lib/MCA/*.c", - "lib/MCA/*.cpp", - "lib/MCA/*.inc", - "lib/MCA/*.h", - ]), - hdrs = glob([ - "include/llvm/MCA/*.h", - "include/llvm/MCA/*.def", - "include/llvm/MCA/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":MC", - ":Support", - ":config", - ], -) - -cc_library( - name = "MCDisassembler", - srcs = glob([ - "lib/MC/MCDisassembler/*.c", - "lib/MC/MCDisassembler/*.cpp", - "lib/MC/MCDisassembler/*.inc", - "lib/MC/MCDisassembler/*.h", - ]), - hdrs = glob([ - "include/llvm/MC/MCDisassembler/*.h", - "include/llvm/MC/MCDisassembler/*.def", - "include/llvm/MC/MCDisassembler/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":MC", - ":Support", - ":config", - ], -) - -cc_library( - name = "MCJIT", - srcs = glob([ - "lib/ExecutionEngine/MCJIT/*.c", - "lib/ExecutionEngine/MCJIT/*.cpp", - "lib/ExecutionEngine/MCJIT/*.inc", - "lib/ExecutionEngine/MCJIT/*.h", - ]), - hdrs = glob([ - "include/llvm/ExecutionEngine/MCJIT/*.h", - "include/llvm/ExecutionEngine/MCJIT/*.def", - "include/llvm/ExecutionEngine/MCJIT/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":Core", - ":ExecutionEngine", - ":Object", - ":RuntimeDyld", - ":Support", - ":Target", - ":config", - ], -) - -cc_library( - name = "MCParser", - srcs = glob([ - "lib/MC/MCParser/*.c", - "lib/MC/MCParser/*.cpp", - "lib/MC/MCParser/*.inc", - "lib/MC/MCParser/*.h", - ]), - hdrs = glob([ - "include/llvm/MC/MCParser/*.h", - "include/llvm/MC/MCParser/*.def", - "include/llvm/MC/MCParser/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":MC", - ":Support", - ":config", - ], -) - -cc_library( - name = "MIRParser", - srcs = glob([ - "lib/CodeGen/MIRParser/*.c", - "lib/CodeGen/MIRParser/*.cpp", - "lib/CodeGen/MIRParser/*.inc", - "lib/CodeGen/MIRParser/*.h", - ]), - hdrs = glob([ - "include/llvm/CodeGen/MIRParser/*.h", - "include/llvm/CodeGen/MIRParser/*.def", - "include/llvm/CodeGen/MIRParser/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":AsmParser", - ":BinaryFormat", - ":CodeGen", - ":Core", - ":MC", - ":Support", - ":Target", - ":config", - ], -) - -cc_library( - name = "MSP430AsmParser", - srcs = glob([ - "lib/Target/MSP430/AsmParser/*.c", - "lib/Target/MSP430/AsmParser/*.cpp", - "lib/Target/MSP430/AsmParser/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/MSP430/AsmParser/*.h", - "include/llvm/Target/MSP430/AsmParser/*.def", - "include/llvm/Target/MSP430/AsmParser/*.inc", - "lib/Target/MSP430/AsmParser/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/MSP430"], - deps = [ - ":MC", - ":MCParser", - ":MSP430Desc", - ":MSP430Info", - ":Support", - ":config", - ], -) - -cc_library( - name = "MSP430CodeGen", - srcs = glob([ - "lib/Target/MSP430/*.c", - "lib/Target/MSP430/*.cpp", - "lib/Target/MSP430/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/MSP430/*.h", - "include/llvm/Target/MSP430/*.def", - "include/llvm/Target/MSP430/*.inc", - "lib/Target/MSP430/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/MSP430"], - deps = [ - ":AsmPrinter", - ":CodeGen", - ":Core", - ":MC", - ":MSP430Desc", - ":MSP430Info", - ":SelectionDAG", - ":Support", - ":Target", - ":config", - ], -) - -cc_library( - name = "MSP430Desc", - srcs = glob([ - "lib/Target/MSP430/MCTargetDesc/*.c", - "lib/Target/MSP430/MCTargetDesc/*.cpp", - "lib/Target/MSP430/MCTargetDesc/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/MSP430/MCTargetDesc/*.h", - "include/llvm/Target/MSP430/MCTargetDesc/*.def", - "include/llvm/Target/MSP430/MCTargetDesc/*.inc", - "lib/Target/MSP430/MCTargetDesc/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/MSP430"], - deps = [ - ":MC", - ":MSP430Info", - ":Support", - ":config", - ], -) - -cc_library( - name = "MSP430Disassembler", - srcs = glob([ - "lib/Target/MSP430/Disassembler/*.c", - "lib/Target/MSP430/Disassembler/*.cpp", - "lib/Target/MSP430/Disassembler/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/MSP430/Disassembler/*.h", - "include/llvm/Target/MSP430/Disassembler/*.def", - "include/llvm/Target/MSP430/Disassembler/*.inc", - "lib/Target/MSP430/Disassembler/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/MSP430"], - deps = [ - ":MCDisassembler", - ":MSP430Info", - ":Support", - ":config", - ], -) - -cc_library( - name = "MSP430Info", - srcs = glob([ - "lib/Target/MSP430/TargetInfo/*.c", - "lib/Target/MSP430/TargetInfo/*.cpp", - "lib/Target/MSP430/TargetInfo/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/MSP430/TargetInfo/*.h", - "include/llvm/Target/MSP430/TargetInfo/*.def", - "include/llvm/Target/MSP430/TargetInfo/*.inc", - "lib/Target/MSP430/TargetInfo/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/MSP430"], - deps = [ - ":Support", - ":config", - ], -) - -cc_library( - name = "MipsAsmParser", - srcs = glob([ - "lib/Target/Mips/AsmParser/*.c", - "lib/Target/Mips/AsmParser/*.cpp", - "lib/Target/Mips/AsmParser/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/Mips/AsmParser/*.h", - "include/llvm/Target/Mips/AsmParser/*.def", - "include/llvm/Target/Mips/AsmParser/*.inc", - "lib/Target/Mips/AsmParser/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/Mips"], - deps = [ - ":MC", - ":MCParser", - ":MipsDesc", - ":MipsInfo", - ":Support", - ":config", - ], -) - -cc_library( - name = "MipsCodeGen", - srcs = glob([ - "lib/Target/Mips/*.c", - "lib/Target/Mips/*.cpp", - "lib/Target/Mips/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/Mips/*.h", - "include/llvm/Target/Mips/*.def", - "include/llvm/Target/Mips/*.inc", - "lib/Target/Mips/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/Mips"], - deps = [ - ":Analysis", - ":AsmPrinter", - ":CodeGen", - ":Core", - ":GlobalISel", - ":MC", - ":MipsDesc", - ":MipsInfo", - ":SelectionDAG", - ":Support", - ":Target", - ":config", - ], -) - -cc_library( - name = "MipsDesc", - srcs = glob([ - "lib/Target/Mips/MCTargetDesc/*.c", - "lib/Target/Mips/MCTargetDesc/*.cpp", - "lib/Target/Mips/MCTargetDesc/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/Mips/MCTargetDesc/*.h", - "include/llvm/Target/Mips/MCTargetDesc/*.def", - "include/llvm/Target/Mips/MCTargetDesc/*.inc", - "lib/Target/Mips/MCTargetDesc/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/Mips"], - deps = [ - ":MC", - ":MipsInfo", - ":Support", - ":config", - ], -) - -cc_library( - name = "MipsDisassembler", - srcs = glob([ - "lib/Target/Mips/Disassembler/*.c", - "lib/Target/Mips/Disassembler/*.cpp", - "lib/Target/Mips/Disassembler/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/Mips/Disassembler/*.h", - "include/llvm/Target/Mips/Disassembler/*.def", - "include/llvm/Target/Mips/Disassembler/*.inc", - "lib/Target/Mips/Disassembler/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/Mips"], - deps = [ - ":MCDisassembler", - ":MipsInfo", - ":Support", - ":config", - ], -) - -cc_library( - name = "MipsInfo", - srcs = glob([ - "lib/Target/Mips/TargetInfo/*.c", - "lib/Target/Mips/TargetInfo/*.cpp", - "lib/Target/Mips/TargetInfo/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/Mips/TargetInfo/*.h", - "include/llvm/Target/Mips/TargetInfo/*.def", - "include/llvm/Target/Mips/TargetInfo/*.inc", - "lib/Target/Mips/TargetInfo/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/Mips"], - deps = [ - ":Support", - ":config", - ], -) - -cc_library( - name = "NVPTXCodeGen", - srcs = glob([ - "lib/Target/NVPTX/*.c", - "lib/Target/NVPTX/*.cpp", - "lib/Target/NVPTX/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/NVPTX/*.h", - "include/llvm/Target/NVPTX/*.def", - "include/llvm/Target/NVPTX/*.inc", - "lib/Target/NVPTX/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/NVPTX"], - deps = [ - ":Analysis", - ":AsmPrinter", - ":CodeGen", - ":Core", - ":IPO", - ":MC", - ":NVPTXDesc", - ":NVPTXInfo", - ":Scalar", - ":SelectionDAG", - ":Support", - ":Target", - ":TransformUtils", - ":Vectorize", - ":config", - ], -) - -cc_library( - name = "NVPTXDesc", - srcs = glob([ - "lib/Target/NVPTX/MCTargetDesc/*.c", - "lib/Target/NVPTX/MCTargetDesc/*.cpp", - "lib/Target/NVPTX/MCTargetDesc/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/NVPTX/MCTargetDesc/*.h", - "include/llvm/Target/NVPTX/MCTargetDesc/*.def", - "include/llvm/Target/NVPTX/MCTargetDesc/*.inc", - "lib/Target/NVPTX/MCTargetDesc/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/NVPTX"], - deps = [ - ":MC", - ":NVPTXCommonTableGen", - ":NVPTXInfo", - ":Support", - ":config", - ], -) - -cc_library( - name = "NVPTXInfo", - srcs = glob([ - "lib/Target/NVPTX/TargetInfo/*.c", - "lib/Target/NVPTX/TargetInfo/*.cpp", - "lib/Target/NVPTX/TargetInfo/*.inc", - "lib/Target/NVPTX/MCTargetDesc/*.h", - ]), - hdrs = glob([ - "include/llvm/Target/NVPTX/TargetInfo/*.h", - "include/llvm/Target/NVPTX/TargetInfo/*.def", - "include/llvm/Target/NVPTX/TargetInfo/*.inc", - "lib/Target/NVPTX/NVPTX.h", - "lib/Target/NVPTX/TargetInfo/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/NVPTX"], - deps = [ - ":Core", - ":NVPTXCommonTableGen", - ":Support", - ":Target", - ":attributes_gen", - ":config", - ], -) - -cc_library( - name = "ObjCARC", - srcs = glob([ - "lib/Transforms/ObjCARC/*.c", - "lib/Transforms/ObjCARC/*.cpp", - "lib/Transforms/ObjCARC/*.inc", - "include/llvm/Transforms/ObjCARC.h", - "lib/Transforms/ObjCARC/*.h", - ]), - hdrs = glob([ - "include/llvm/Transforms/ObjCARC/*.h", - "include/llvm/Transforms/ObjCARC/*.def", - "include/llvm/Transforms/ObjCARC/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":Analysis", - ":Core", - ":Support", - ":TransformUtils", - ":config", - ], -) - -cc_library( - name = "Object", - srcs = glob([ - "lib/Object/*.c", - "lib/Object/*.cpp", - "lib/Object/*.inc", - "lib/Object/*.h", - ]), - hdrs = glob([ - "include/llvm/Object/*.h", - "include/llvm/Object/*.def", - "include/llvm/Object/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":BinaryFormat", - ":BitReader", - ":Core", - ":MC", - ":MCParser", - ":Support", - ":TextAPI", - ":config", - ], -) - -cc_library( - name = "ObjectYAML", - srcs = glob([ - "lib/ObjectYAML/*.c", - "lib/ObjectYAML/*.cpp", - "lib/ObjectYAML/*.inc", - "lib/ObjectYAML/*.h", - ]), - hdrs = glob([ - "include/llvm/ObjectYAML/*.h", - "include/llvm/ObjectYAML/*.def", - "include/llvm/ObjectYAML/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":BinaryFormat", - ":DebugInfoCodeView", - ":MC", - ":Object", - ":Support", - ":config", - ], -) - -cc_library( - name = "Option", - srcs = glob([ - "lib/Option/*.c", - "lib/Option/*.cpp", - "lib/Option/*.inc", - "lib/Option/*.h", - ]), - hdrs = glob([ - "include/llvm/Option/*.h", - "include/llvm/Option/*.def", - "include/llvm/Option/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":Support", - ":config", - ], -) - -cc_library( - name = "OrcJIT", - srcs = glob([ - "lib/ExecutionEngine/Orc/*.c", - "lib/ExecutionEngine/Orc/*.cpp", - "lib/ExecutionEngine/Orc/*.inc", - "lib/ExecutionEngine/Orc/*.h", - ]), - hdrs = glob([ - "include/llvm/ExecutionEngine/Orc/*.h", - "include/llvm/ExecutionEngine/Orc/*.def", - "include/llvm/ExecutionEngine/Orc/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":Core", - ":ExecutionEngine", - ":JITLink", - ":MC", - ":Object", - ":OrcShared", - ":OrcTargetProcess", - ":Passes", - ":RuntimeDyld", - ":Support", - ":Target", - ":TransformUtils", - ":config", - ], -) - -cc_library( - name = "OrcShared", - srcs = glob([ - "lib/ExecutionEngine/Orc/Shared/*.c", - "lib/ExecutionEngine/Orc/Shared/*.cpp", - "lib/ExecutionEngine/Orc/Shared/*.inc", - "lib/ExecutionEngine/Orc/Shared/*.h", - ]), - hdrs = glob([ - "include/llvm/ExecutionEngine/Orc/Shared/*.h", - "include/llvm/ExecutionEngine/Orc/Shared/*.def", - "include/llvm/ExecutionEngine/Orc/Shared/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":Support", - ":config", - ], -) - -cc_library( - name = "OrcTargetProcess", - srcs = glob([ - "lib/ExecutionEngine/Orc/TargetProcess/*.c", - "lib/ExecutionEngine/Orc/TargetProcess/*.cpp", - "lib/ExecutionEngine/Orc/TargetProcess/*.inc", - "lib/ExecutionEngine/Orc/TargetProcess/*.h", - ]), - hdrs = glob([ - "include/llvm/ExecutionEngine/Orc/TargetProcess/*.h", - "include/llvm/ExecutionEngine/Orc/TargetProcess/*.def", - "include/llvm/ExecutionEngine/Orc/TargetProcess/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":OrcShared", - ":Support", - ":config", - ], -) - -cc_library( - name = "Passes", - srcs = glob([ - "lib/Passes/*.c", - "lib/Passes/*.cpp", - "lib/Passes/*.inc", - "lib/Passes/*.h", - ]), - hdrs = glob([ - "include/llvm/Passes/*.h", - "include/llvm/Passes/*.def", - "include/llvm/Passes/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":AggressiveInstCombine", - ":Analysis", - ":CodeGen", - ":Core", - ":Coroutines", - ":HelloNew", - ":IPO", - ":InstCombine", - ":Instrumentation", - ":ObjCARC", - ":Scalar", - ":Support", - ":Target", - ":TransformUtils", - ":Vectorize", - ":config", - ], -) - -cc_library( - name = "PowerPCAsmParser", - srcs = glob([ - "lib/Target/PowerPC/AsmParser/*.c", - "lib/Target/PowerPC/AsmParser/*.cpp", - "lib/Target/PowerPC/AsmParser/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/PowerPC/AsmParser/*.h", - "include/llvm/Target/PowerPC/AsmParser/*.def", - "include/llvm/Target/PowerPC/AsmParser/*.inc", - "lib/Target/PowerPC/AsmParser/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/PowerPC"], - deps = [ - ":MC", - ":MCParser", - ":PowerPCDesc", - ":PowerPCInfo", - ":Support", - ":config", - ], -) - -cc_library( - name = "PowerPCCodeGen", - srcs = glob([ - "lib/Target/PowerPC/*.c", - "lib/Target/PowerPC/*.cpp", - "lib/Target/PowerPC/*.inc", - "lib/Target/PowerPC/GISel/*.cpp", - ]), - hdrs = glob([ - "include/llvm/Target/PowerPC/*.h", - "include/llvm/Target/PowerPC/*.def", - "include/llvm/Target/PowerPC/*.inc", - "lib/Target/PowerPC/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/PowerPC"], - deps = [ - ":Analysis", - ":AsmPrinter", - ":CodeGen", - ":Core", - ":GlobalISel", - ":MC", - ":PowerPCDesc", - ":PowerPCInfo", - ":Scalar", - ":SelectionDAG", - ":Support", - ":Target", - ":TransformUtils", - ":config", - ], -) - -cc_library( - name = "PowerPCDesc", - srcs = glob([ - "lib/Target/PowerPC/MCTargetDesc/*.c", - "lib/Target/PowerPC/MCTargetDesc/*.cpp", - "lib/Target/PowerPC/MCTargetDesc/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/PowerPC/MCTargetDesc/*.h", - "include/llvm/Target/PowerPC/MCTargetDesc/*.def", - "include/llvm/Target/PowerPC/MCTargetDesc/*.inc", - "lib/Target/PowerPC/MCTargetDesc/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/PowerPC"], - deps = [ - ":BinaryFormat", - ":MC", - ":PowerPCCommonTableGen", - ":PowerPCInfo", - ":Support", - ":attributes_gen", - ":config", - ":intrinsic_enums_gen", - ":intrinsics_impl_gen", - ], -) - -cc_library( - name = "PowerPCDisassembler", - srcs = glob([ - "lib/Target/PowerPC/Disassembler/*.c", - "lib/Target/PowerPC/Disassembler/*.cpp", - "lib/Target/PowerPC/Disassembler/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/PowerPC/Disassembler/*.h", - "include/llvm/Target/PowerPC/Disassembler/*.def", - "include/llvm/Target/PowerPC/Disassembler/*.inc", - "lib/Target/PowerPC/Disassembler/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/PowerPC"], - deps = [ - ":MCDisassembler", - ":PowerPCInfo", - ":Support", - ":config", - ], -) - -cc_library( - name = "PowerPCInfo", - srcs = glob([ - "lib/Target/PowerPC/TargetInfo/*.c", - "lib/Target/PowerPC/TargetInfo/*.cpp", - "lib/Target/PowerPC/TargetInfo/*.inc", - "lib/Target/PowerPC/MCTargetDesc/*.h", - ]), - hdrs = glob([ - "include/llvm/Target/PowerPC/TargetInfo/*.h", - "include/llvm/Target/PowerPC/TargetInfo/*.def", - "include/llvm/Target/PowerPC/TargetInfo/*.inc", - "lib/Target/PowerPC/PPC*.h", - "lib/Target/PowerPC/TargetInfo/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/PowerPC"], - deps = [ - ":Core", - ":PowerPCCommonTableGen", - ":Support", - ":Target", - ":attributes_gen", - ":config", - ], -) - -cc_library( - name = "ProfileData", - srcs = glob([ - "lib/ProfileData/*.c", - "lib/ProfileData/*.cpp", - "lib/ProfileData/*.inc", - "lib/ProfileData/*.h", - ]), - hdrs = glob([ - "include/llvm/ProfileData/*.h", - "include/llvm/ProfileData/*.def", - "include/llvm/ProfileData/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":Core", - ":Demangle", - ":Support", - ":config", - ], -) - -cc_library( - name = "RISCVAsmParser", - srcs = glob([ - "lib/Target/RISCV/AsmParser/*.c", - "lib/Target/RISCV/AsmParser/*.cpp", - "lib/Target/RISCV/AsmParser/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/RISCV/AsmParser/*.h", - "include/llvm/Target/RISCV/AsmParser/*.def", - "include/llvm/Target/RISCV/AsmParser/*.inc", - "lib/Target/RISCV/AsmParser/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/RISCV"], - deps = [ - ":MC", - ":MCParser", - ":RISCVDesc", - ":RISCVInfo", - ":RISCVUtils", - ":Support", - ":config", - ], -) - -cc_library( - name = "RISCVCodeGen", - srcs = glob([ - "lib/Target/RISCV/*.c", - "lib/Target/RISCV/*.cpp", - "lib/Target/RISCV/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/RISCV/*.h", - "include/llvm/Target/RISCV/*.def", - "include/llvm/Target/RISCV/*.inc", - "lib/Target/RISCV/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/RISCV"], - deps = [ - ":Analysis", - ":AsmPrinter", - ":CodeGen", - ":Core", - ":GlobalISel", - ":MC", - ":RISCVDesc", - ":RISCVInfo", - ":RISCVUtils", - ":SelectionDAG", - ":Support", - ":Target", - ":config", - ], -) - -cc_library( - name = "RISCVDesc", - srcs = glob([ - "lib/Target/RISCV/MCTargetDesc/*.c", - "lib/Target/RISCV/MCTargetDesc/*.cpp", - "lib/Target/RISCV/MCTargetDesc/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/RISCV/MCTargetDesc/*.h", - "include/llvm/Target/RISCV/MCTargetDesc/*.def", - "include/llvm/Target/RISCV/MCTargetDesc/*.inc", - "lib/Target/RISCV/MCTargetDesc/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/RISCV"], - deps = [ - ":MC", - ":RISCVInfo", - ":RISCVUtils", - ":Support", - ":config", - ], -) - -cc_library( - name = "RISCVDisassembler", - srcs = glob([ - "lib/Target/RISCV/Disassembler/*.c", - "lib/Target/RISCV/Disassembler/*.cpp", - "lib/Target/RISCV/Disassembler/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/RISCV/Disassembler/*.h", - "include/llvm/Target/RISCV/Disassembler/*.def", - "include/llvm/Target/RISCV/Disassembler/*.inc", - "lib/Target/RISCV/Disassembler/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/RISCV"], - deps = [ - ":MCDisassembler", - ":RISCVInfo", - ":Support", - ":config", - ], -) - -cc_library( - name = "RISCVInfo", - srcs = glob([ - "lib/Target/RISCV/TargetInfo/*.c", - "lib/Target/RISCV/TargetInfo/*.cpp", - "lib/Target/RISCV/TargetInfo/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/RISCV/TargetInfo/*.h", - "include/llvm/Target/RISCV/TargetInfo/*.def", - "include/llvm/Target/RISCV/TargetInfo/*.inc", - "lib/Target/RISCV/TargetInfo/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/RISCV"], - deps = [ - ":Support", - ":config", - ], -) - -cc_library( - name = "RISCVUtils", - srcs = glob([ - "lib/Target/RISCV/Utils/*.c", - "lib/Target/RISCV/Utils/*.cpp", - "lib/Target/RISCV/Utils/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/RISCV/Utils/*.h", - "include/llvm/Target/RISCV/Utils/*.def", - "include/llvm/Target/RISCV/Utils/*.inc", - "lib/Target/RISCV/Utils/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/RISCV"], - deps = [ - ":Support", - ":config", - ], -) - -cc_library( - name = "Remarks", - srcs = glob([ - "lib/Remarks/*.c", - "lib/Remarks/*.cpp", - "lib/Remarks/*.inc", - "lib/Remarks/*.h", - ]), - hdrs = glob([ - "include/llvm/Remarks/*.h", - "include/llvm/Remarks/*.def", - "include/llvm/Remarks/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":BitstreamReader", - ":Support", - ":config", - ], -) - -cc_library( - name = "RuntimeDyld", - srcs = glob([ - "lib/ExecutionEngine/RuntimeDyld/*.c", - "lib/ExecutionEngine/RuntimeDyld/*.cpp", - "lib/ExecutionEngine/RuntimeDyld/*.inc", - "include/llvm/ExecutionEngine/JITSymbol.h", - "include/llvm/ExecutionEngine/RTDyldMemoryManager.h", - "lib/ExecutionEngine/RuntimeDyld/*.h", - "lib/ExecutionEngine/RuntimeDyld/Targets/*.h", - "lib/ExecutionEngine/RuntimeDyld/Targets/*.cpp", - "lib/ExecutionEngine/RuntimeDyld/*.h", - ]), - hdrs = glob([ - "include/llvm/ExecutionEngine/RuntimeDyld/*.h", - "include/llvm/ExecutionEngine/RuntimeDyld/*.def", - "include/llvm/ExecutionEngine/RuntimeDyld/*.inc", - "include/llvm/DebugInfo/DIContext.h", - "include/llvm/ExecutionEngine/RTDyldMemoryManager.h", - "include/llvm/ExecutionEngine/RuntimeDyld*.h", - ]), - copts = llvm_copts, - deps = [ - ":Core", - ":MC", - ":MCDisassembler", - ":Object", - ":Support", - ":config", - ], -) - -cc_library( - name = "Scalar", - srcs = glob([ - "lib/Transforms/Scalar/*.c", - "lib/Transforms/Scalar/*.cpp", - "lib/Transforms/Scalar/*.inc", - "include/llvm-c/Transforms/Scalar.h", - "include/llvm/Transforms/Scalar.h", - "include/llvm/Target/TargetMachine.h", - "lib/Transforms/Scalar/*.h", - ]), - hdrs = glob([ - "include/llvm/Transforms/Scalar/*.h", - "include/llvm/Transforms/Scalar/*.def", - "include/llvm/Transforms/Scalar/*.inc", - "include/llvm/Transforms/IPO.h", - "include/llvm/Transforms/IPO/SCCP.h", - ]), - copts = llvm_copts, - deps = [ - ":AggressiveInstCombine", - ":Analysis", - ":Core", - ":InstCombine", - ":Support", - ":Target", - ":TransformUtils", - ":config", - ], -) - -cc_library( - name = "SelectionDAG", - srcs = glob([ - "lib/CodeGen/SelectionDAG/*.c", - "lib/CodeGen/SelectionDAG/*.cpp", - "lib/CodeGen/SelectionDAG/*.inc", - "lib/CodeGen/SelectionDAG/*.h", - ]), - hdrs = glob([ - "include/llvm/CodeGen/SelectionDAG/*.h", - "include/llvm/CodeGen/SelectionDAG/*.def", - "include/llvm/CodeGen/SelectionDAG/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":Analysis", - ":CodeGen", - ":Core", - ":MC", - ":Support", - ":Target", - ":TransformUtils", - ":config", - ], -) - -cc_library( - name = "SparcAsmParser", - srcs = glob([ - "lib/Target/Sparc/AsmParser/*.c", - "lib/Target/Sparc/AsmParser/*.cpp", - "lib/Target/Sparc/AsmParser/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/Sparc/AsmParser/*.h", - "include/llvm/Target/Sparc/AsmParser/*.def", - "include/llvm/Target/Sparc/AsmParser/*.inc", - "lib/Target/Sparc/AsmParser/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/Sparc"], - deps = [ - ":MC", - ":MCParser", - ":SparcDesc", - ":SparcInfo", - ":Support", - ":config", - ], -) - -cc_library( - name = "SparcCodeGen", - srcs = glob([ - "lib/Target/Sparc/*.c", - "lib/Target/Sparc/*.cpp", - "lib/Target/Sparc/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/Sparc/*.h", - "include/llvm/Target/Sparc/*.def", - "include/llvm/Target/Sparc/*.inc", - "lib/Target/Sparc/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/Sparc"], - deps = [ - ":AsmPrinter", - ":CodeGen", - ":Core", - ":MC", - ":SelectionDAG", - ":SparcDesc", - ":SparcInfo", - ":Support", - ":Target", - ":config", - ], -) - -cc_library( - name = "SparcDesc", - srcs = glob([ - "lib/Target/Sparc/MCTargetDesc/*.c", - "lib/Target/Sparc/MCTargetDesc/*.cpp", - "lib/Target/Sparc/MCTargetDesc/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/Sparc/MCTargetDesc/*.h", - "include/llvm/Target/Sparc/MCTargetDesc/*.def", - "include/llvm/Target/Sparc/MCTargetDesc/*.inc", - "lib/Target/Sparc/MCTargetDesc/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/Sparc"], - deps = [ - ":MC", - ":SparcInfo", - ":Support", - ":config", - ], -) - -cc_library( - name = "SparcDisassembler", - srcs = glob([ - "lib/Target/Sparc/Disassembler/*.c", - "lib/Target/Sparc/Disassembler/*.cpp", - "lib/Target/Sparc/Disassembler/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/Sparc/Disassembler/*.h", - "include/llvm/Target/Sparc/Disassembler/*.def", - "include/llvm/Target/Sparc/Disassembler/*.inc", - "lib/Target/Sparc/Disassembler/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/Sparc"], - deps = [ - ":MCDisassembler", - ":SparcInfo", - ":Support", - ":config", - ], -) - -cc_library( - name = "SparcInfo", - srcs = glob([ - "lib/Target/Sparc/TargetInfo/*.c", - "lib/Target/Sparc/TargetInfo/*.cpp", - "lib/Target/Sparc/TargetInfo/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/Sparc/TargetInfo/*.h", - "include/llvm/Target/Sparc/TargetInfo/*.def", - "include/llvm/Target/Sparc/TargetInfo/*.inc", - "lib/Target/Sparc/TargetInfo/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/Sparc"], - deps = [ - ":Support", - ":config", - ], -) - -cc_library( - name = "Support", - srcs = glob([ - "lib/Support/*.c", - "lib/Support/*.cpp", - "lib/Support/*.inc", - "include/llvm-c/*.h", - "include/llvm/CodeGen/MachineValueType.h", - "include/llvm/BinaryFormat/COFF.h", - "include/llvm/BinaryFormat/MachO.h", - "lib/Support/*.h", - ]) + llvm_support_platform_specific_srcs_glob(), - hdrs = glob([ - "include/llvm/Support/*.h", - "include/llvm/Support/*.def", - "include/llvm/Support/*.inc", - "include/llvm/ADT/*.h", - "include/llvm/Support/ELFRelocs/*.def", - "include/llvm/Support/WasmRelocs/*.def", - ]) + [ - "include/llvm/BinaryFormat/MachO.def", - "include/llvm/Support/VCSRevision.h", - ], - copts = llvm_copts, - deps = [ - ":Demangle", - ":config", - "@zlib", - ], -) - -cc_library( - name = "Symbolize", - srcs = glob([ - "lib/DebugInfo/Symbolize/*.c", - "lib/DebugInfo/Symbolize/*.cpp", - "lib/DebugInfo/Symbolize/*.inc", - "lib/DebugInfo/Symbolize/*.h", - ]), - hdrs = glob([ - "include/llvm/DebugInfo/Symbolize/*.h", - "include/llvm/DebugInfo/Symbolize/*.def", - "include/llvm/DebugInfo/Symbolize/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":DebugInfoDWARF", - ":DebugInfoPDB", - ":Demangle", - ":Object", - ":Support", - ":config", - ], -) - -cc_library( - name = "SystemZAsmParser", - srcs = glob([ - "lib/Target/SystemZ/AsmParser/*.c", - "lib/Target/SystemZ/AsmParser/*.cpp", - "lib/Target/SystemZ/AsmParser/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/SystemZ/AsmParser/*.h", - "include/llvm/Target/SystemZ/AsmParser/*.def", - "include/llvm/Target/SystemZ/AsmParser/*.inc", - "lib/Target/SystemZ/AsmParser/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/SystemZ"], - deps = [ - ":MC", - ":MCParser", - ":Support", - ":SystemZDesc", - ":SystemZInfo", - ":config", - ], -) - -cc_library( - name = "SystemZCodeGen", - srcs = glob([ - "lib/Target/SystemZ/*.c", - "lib/Target/SystemZ/*.cpp", - "lib/Target/SystemZ/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/SystemZ/*.h", - "include/llvm/Target/SystemZ/*.def", - "include/llvm/Target/SystemZ/*.inc", - "lib/Target/SystemZ/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/SystemZ"], - deps = [ - ":Analysis", - ":AsmPrinter", - ":CodeGen", - ":Core", - ":MC", - ":Scalar", - ":SelectionDAG", - ":Support", - ":SystemZDesc", - ":SystemZInfo", - ":Target", - ":config", - ], -) - -cc_library( - name = "SystemZDesc", - srcs = glob([ - "lib/Target/SystemZ/MCTargetDesc/*.c", - "lib/Target/SystemZ/MCTargetDesc/*.cpp", - "lib/Target/SystemZ/MCTargetDesc/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/SystemZ/MCTargetDesc/*.h", - "include/llvm/Target/SystemZ/MCTargetDesc/*.def", - "include/llvm/Target/SystemZ/MCTargetDesc/*.inc", - "lib/Target/SystemZ/MCTargetDesc/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/SystemZ"], - deps = [ - ":MC", - ":Support", - ":SystemZCommonTableGen", - ":SystemZInfo", - ":config", - ], -) - -cc_library( - name = "SystemZDisassembler", - srcs = glob([ - "lib/Target/SystemZ/Disassembler/*.c", - "lib/Target/SystemZ/Disassembler/*.cpp", - "lib/Target/SystemZ/Disassembler/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/SystemZ/Disassembler/*.h", - "include/llvm/Target/SystemZ/Disassembler/*.def", - "include/llvm/Target/SystemZ/Disassembler/*.inc", - "lib/Target/SystemZ/Disassembler/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/SystemZ"], - deps = [ - ":MC", - ":MCDisassembler", - ":Support", - ":SystemZDesc", - ":SystemZInfo", - ":config", - ], -) - -cc_library( - name = "SystemZInfo", - srcs = glob([ - "lib/Target/SystemZ/TargetInfo/*.c", - "lib/Target/SystemZ/TargetInfo/*.cpp", - "lib/Target/SystemZ/TargetInfo/*.inc", - "lib/Target/SystemZ/MCTargetDesc/*.h", - ]), - hdrs = glob([ - "include/llvm/Target/SystemZ/TargetInfo/*.h", - "include/llvm/Target/SystemZ/TargetInfo/*.def", - "include/llvm/Target/SystemZ/TargetInfo/*.inc", - "lib/Target/SystemZ/TargetInfo/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/SystemZ"], - deps = [ - ":Support", - ":SystemZCommonTableGen", - ":config", - ], -) - -cc_library( - name = "TableGen", - srcs = glob([ - "lib/TableGen/*.c", - "lib/TableGen/*.cpp", - "lib/TableGen/*.inc", - "include/llvm/CodeGen/*.h", - "lib/TableGen/*.h", - ]), - hdrs = glob([ - "include/llvm/TableGen/*.h", - "include/llvm/TableGen/*.def", - "include/llvm/TableGen/*.inc", - "include/llvm/Target/*.def", - ]), - copts = llvm_copts, - deps = [ - ":MC", - ":Support", - ":config", - ], -) - -cc_library( - name = "Target", - srcs = glob([ - "lib/Target/*.c", - "lib/Target/*.cpp", - "lib/Target/*.inc", - "include/llvm/CodeGen/*.h", - "include/llvm-c/Initialization.h", - "include/llvm-c/Target.h", - "lib/Target/*.h", - ]), - hdrs = glob([ - "include/llvm/Target/*.h", - "include/llvm/Target/*.def", - "include/llvm/Target/*.inc", - "include/llvm/CodeGen/*.def", - "include/llvm/CodeGen/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":Analysis", - ":Core", - ":MC", - ":Support", - ":config", - ], -) - -cc_library( - name = "TestingSupport", - srcs = glob([ - "lib/Testing/Support/*.c", - "lib/Testing/Support/*.cpp", - "lib/Testing/Support/*.inc", - "lib/Testing/Support/*.h", - ]), - hdrs = glob([ - "include/llvm/Testing/Support/*.h", - "include/llvm/Testing/Support/*.def", - "include/llvm/Testing/Support/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":Support", - ":config", - ], -) - -# The original CMakeLists.txt in lib/TextAPI mentions the MachO subdirectory -# explicitly, and also an Elf subdirectory that doesn't exist in the includes -cc_library( - name = "TextAPI", - srcs = glob([ - "lib/TextAPI/*.c", - "lib/TextAPI/*.cpp", - "lib/TextAPI/*.inc", - "lib/TextAPI/*.h", - "lib/TextAPI/MachO/*.c", - "lib/TextAPI/MachO/*.cpp", - "lib/TextAPI/MachO/*.inc", - "lib/TextAPI/MachO/*.h", - ]), - hdrs = glob([ - "include/llvm/TextAPI/*.h", - "include/llvm/TextAPI/*.def", - "include/llvm/TextAPI/*.inc", - "include/llvm/TextAPI/MachO/*.h", - "include/llvm/TextAPI/MachO/*.def", - "include/llvm/TextAPI/MachO/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":BinaryFormat", - ":Support", - ":config", - ], -) - -cc_library( - name = "TransformUtils", - srcs = glob([ - "lib/Transforms/Utils/*.c", - "lib/Transforms/Utils/*.cpp", - "lib/Transforms/Utils/*.inc", - "include/llvm/Transforms/IPO.h", - "include/llvm/Transforms/Scalar.h", - "lib/Transforms/Utils/*.h", - ]), - hdrs = glob([ - "include/llvm/Transforms/Utils/*.h", - "include/llvm/Transforms/Utils/*.def", - "include/llvm/Transforms/Utils/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":Analysis", - ":Core", - ":Support", - ":config", - ], -) - -cc_library( - name = "VEAsmParser", - srcs = glob([ - "lib/Target/VE/AsmParser/*.c", - "lib/Target/VE/AsmParser/*.cpp", - "lib/Target/VE/AsmParser/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/VE/AsmParser/*.h", - "include/llvm/Target/VE/AsmParser/*.def", - "include/llvm/Target/VE/AsmParser/*.inc", - "lib/Target/VE/AsmParser/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/VE"], - deps = [ - ":MC", - ":MCParser", - ":Support", - ":VEDesc", - ":VEInfo", - ":config", - ], -) - -cc_library( - name = "VECodeGen", - srcs = glob([ - "lib/Target/VE/*.c", - "lib/Target/VE/*.cpp", - "lib/Target/VE/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/VE/*.h", - "include/llvm/Target/VE/*.def", - "include/llvm/Target/VE/*.inc", - "lib/Target/VE/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/VE"], - deps = [ - ":Analysis", - ":AsmPrinter", - ":CodeGen", - ":Core", - ":MC", - ":SelectionDAG", - ":Support", - ":Target", - ":VEDesc", - ":VEInfo", - ":config", - ], -) - -cc_library( - name = "VEDesc", - srcs = glob([ - "lib/Target/VE/MCTargetDesc/*.c", - "lib/Target/VE/MCTargetDesc/*.cpp", - "lib/Target/VE/MCTargetDesc/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/VE/MCTargetDesc/*.h", - "include/llvm/Target/VE/MCTargetDesc/*.def", - "include/llvm/Target/VE/MCTargetDesc/*.inc", - "lib/Target/VE/MCTargetDesc/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/VE"], - deps = [ - ":MC", - ":Support", - ":VEInfo", - ":config", - ], -) - -cc_library( - name = "VEDisassembler", - srcs = glob([ - "lib/Target/VE/Disassembler/*.c", - "lib/Target/VE/Disassembler/*.cpp", - "lib/Target/VE/Disassembler/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/VE/Disassembler/*.h", - "include/llvm/Target/VE/Disassembler/*.def", - "include/llvm/Target/VE/Disassembler/*.inc", - "lib/Target/VE/Disassembler/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/VE"], - deps = [ - ":MCDisassembler", - ":Support", - ":VEInfo", - ":config", - ], -) - -cc_library( - name = "VEInfo", - srcs = glob([ - "lib/Target/VE/TargetInfo/*.c", - "lib/Target/VE/TargetInfo/*.cpp", - "lib/Target/VE/TargetInfo/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/VE/TargetInfo/*.h", - "include/llvm/Target/VE/TargetInfo/*.def", - "include/llvm/Target/VE/TargetInfo/*.inc", - "lib/Target/VE/TargetInfo/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/VE"], - deps = [ - ":Support", - ":config", - ], -) - -cc_library( - name = "Vectorize", - srcs = glob([ - "lib/Transforms/Vectorize/*.c", - "lib/Transforms/Vectorize/*.cpp", - "lib/Transforms/Vectorize/*.inc", - "include/llvm-c/Transforms/Vectorize.h", - "lib/Transforms/Vectorize/*.h", - ]), - hdrs = glob([ - "include/llvm/Transforms/Vectorize/*.h", - "include/llvm/Transforms/Vectorize/*.def", - "include/llvm/Transforms/Vectorize/*.inc", - "include/llvm/Transforms/Vectorize.h", - ]), - copts = llvm_copts, - deps = [ - ":Analysis", - ":Core", - ":Scalar", - ":Support", - ":TransformUtils", - ":config", - ], -) - -cc_library( - name = "WebAssemblyAsmParser", - srcs = glob([ - "lib/Target/WebAssembly/AsmParser/*.c", - "lib/Target/WebAssembly/AsmParser/*.cpp", - "lib/Target/WebAssembly/AsmParser/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/WebAssembly/AsmParser/*.h", - "include/llvm/Target/WebAssembly/AsmParser/*.def", - "include/llvm/Target/WebAssembly/AsmParser/*.inc", - "lib/Target/WebAssembly/AsmParser/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/WebAssembly"], - deps = [ - ":MC", - ":MCParser", - ":Support", - ":WebAssemblyInfo", - ":config", - ], -) - -cc_library( - name = "WebAssemblyCodeGen", - srcs = glob([ - "lib/Target/WebAssembly/*.c", - "lib/Target/WebAssembly/*.cpp", - "lib/Target/WebAssembly/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/WebAssembly/*.h", - "include/llvm/Target/WebAssembly/*.def", - "include/llvm/Target/WebAssembly/*.inc", - "lib/Target/WebAssembly/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/WebAssembly"], - deps = [ - ":Analysis", - ":AsmPrinter", - ":BinaryFormat", - ":CodeGen", - ":Core", - ":MC", - ":Scalar", - ":SelectionDAG", - ":Support", - ":Target", - ":TransformUtils", - ":WebAssemblyDesc", - ":WebAssemblyInfo", - ":config", - ], -) - -cc_library( - name = "WebAssemblyDesc", - srcs = glob([ - "lib/Target/WebAssembly/MCTargetDesc/*.c", - "lib/Target/WebAssembly/MCTargetDesc/*.cpp", - "lib/Target/WebAssembly/MCTargetDesc/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/WebAssembly/MCTargetDesc/*.h", - "include/llvm/Target/WebAssembly/MCTargetDesc/*.def", - "include/llvm/Target/WebAssembly/MCTargetDesc/*.inc", - "lib/Target/WebAssembly/MCTargetDesc/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/WebAssembly"], - deps = [ - ":MC", - ":Support", - ":WebAssemblyInfo", - ":config", - ], -) - -cc_library( - name = "WebAssemblyDisassembler", - srcs = glob([ - "lib/Target/WebAssembly/Disassembler/*.c", - "lib/Target/WebAssembly/Disassembler/*.cpp", - "lib/Target/WebAssembly/Disassembler/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/WebAssembly/Disassembler/*.h", - "include/llvm/Target/WebAssembly/Disassembler/*.def", - "include/llvm/Target/WebAssembly/Disassembler/*.inc", - "lib/Target/WebAssembly/Disassembler/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/WebAssembly"], - deps = [ - ":MC", - ":MCDisassembler", - ":Support", - ":WebAssemblyDesc", - ":WebAssemblyInfo", - ":config", - ], -) - -cc_library( - name = "WebAssemblyInfo", - srcs = glob([ - "lib/Target/WebAssembly/TargetInfo/*.c", - "lib/Target/WebAssembly/TargetInfo/*.cpp", - "lib/Target/WebAssembly/TargetInfo/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/WebAssembly/TargetInfo/*.h", - "include/llvm/Target/WebAssembly/TargetInfo/*.def", - "include/llvm/Target/WebAssembly/TargetInfo/*.inc", - "lib/Target/WebAssembly/TargetInfo/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/WebAssembly"], - deps = [ - ":Support", - ":config", - ], -) - -cc_library( - name = "WindowsManifest", - srcs = glob([ - "lib/WindowsManifest/*.c", - "lib/WindowsManifest/*.cpp", - "lib/WindowsManifest/*.inc", - "lib/WindowsManifest/*.h", - ]), - hdrs = glob([ - "include/llvm/WindowsManifest/*.h", - "include/llvm/WindowsManifest/*.def", - "include/llvm/WindowsManifest/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":Support", - ":config", - ], -) - -cc_library( - name = "X86AsmParser", - srcs = glob([ - "lib/Target/X86/AsmParser/*.c", - "lib/Target/X86/AsmParser/*.cpp", - "lib/Target/X86/AsmParser/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/X86/AsmParser/*.h", - "include/llvm/Target/X86/AsmParser/*.def", - "include/llvm/Target/X86/AsmParser/*.inc", - "lib/Target/X86/AsmParser/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/X86"], - deps = [ - ":MC", - ":MCParser", - ":Support", - ":X86Desc", - ":X86Info", - ":config", - ], -) - -cc_library( - name = "X86CodeGen", - srcs = glob([ - "lib/Target/X86/*.c", - "lib/Target/X86/*.cpp", - "lib/Target/X86/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/X86/*.h", - "include/llvm/Target/X86/*.def", - "include/llvm/Target/X86/*.inc", - "lib/Target/X86/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/X86"], - deps = [ - ":Analysis", - ":AsmPrinter", - ":CFGuard", - ":CodeGen", - ":Core", - ":GlobalISel", - ":MC", - ":ProfileData", - ":SelectionDAG", - ":Support", - ":Target", - ":X86Desc", - ":X86Info", - ":config", - ":x86_defs", - ], -) - -cc_library( - name = "X86Desc", - srcs = glob([ - "lib/Target/X86/MCTargetDesc/*.c", - "lib/Target/X86/MCTargetDesc/*.cpp", - "lib/Target/X86/MCTargetDesc/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/X86/MCTargetDesc/*.h", - "include/llvm/Target/X86/MCTargetDesc/*.def", - "include/llvm/Target/X86/MCTargetDesc/*.inc", - "lib/Target/X86/MCTargetDesc/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/X86"], - deps = [ - ":BinaryFormat", - ":MC", - ":MCDisassembler", - ":Support", - ":X86Info", - ":config", - ], -) - -cc_library( - name = "X86Disassembler", - srcs = glob([ - "lib/Target/X86/Disassembler/*.c", - "lib/Target/X86/Disassembler/*.cpp", - "lib/Target/X86/Disassembler/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/X86/Disassembler/*.h", - "include/llvm/Target/X86/Disassembler/*.def", - "include/llvm/Target/X86/Disassembler/*.inc", - "lib/Target/X86/Disassembler/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/X86"], - deps = [ - ":MCDisassembler", - ":Support", - ":X86Info", - ":config", - ], -) - -cc_library( - name = "X86Info", - srcs = glob([ - "lib/Target/X86/TargetInfo/*.c", - "lib/Target/X86/TargetInfo/*.cpp", - "lib/Target/X86/TargetInfo/*.inc", - "lib/Target/X86/MCTargetDesc/*.h", - ]), - hdrs = glob([ - "include/llvm/Target/X86/TargetInfo/*.h", - "include/llvm/Target/X86/TargetInfo/*.def", - "include/llvm/Target/X86/TargetInfo/*.inc", - "lib/Target/X86/TargetInfo/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/X86"], - deps = [ - ":MC", - ":Support", - ":X86CommonTableGen", - ":config", - ], -) - -cc_library( - name = "XCoreCodeGen", - srcs = glob([ - "lib/Target/XCore/*.c", - "lib/Target/XCore/*.cpp", - "lib/Target/XCore/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/XCore/*.h", - "include/llvm/Target/XCore/*.def", - "include/llvm/Target/XCore/*.inc", - "lib/Target/XCore/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/XCore"], - deps = [ - ":Analysis", - ":AsmPrinter", - ":CodeGen", - ":Core", - ":MC", - ":SelectionDAG", - ":Support", - ":Target", - ":TransformUtils", - ":XCoreDesc", - ":XCoreInfo", - ":config", - ], -) - -cc_library( - name = "XCoreDesc", - srcs = glob([ - "lib/Target/XCore/MCTargetDesc/*.c", - "lib/Target/XCore/MCTargetDesc/*.cpp", - "lib/Target/XCore/MCTargetDesc/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/XCore/MCTargetDesc/*.h", - "include/llvm/Target/XCore/MCTargetDesc/*.def", - "include/llvm/Target/XCore/MCTargetDesc/*.inc", - "lib/Target/XCore/MCTargetDesc/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/XCore"], - deps = [ - ":MC", - ":Support", - ":XCoreInfo", - ":config", - ], -) - -cc_library( - name = "XCoreDisassembler", - srcs = glob([ - "lib/Target/XCore/Disassembler/*.c", - "lib/Target/XCore/Disassembler/*.cpp", - "lib/Target/XCore/Disassembler/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/XCore/Disassembler/*.h", - "include/llvm/Target/XCore/Disassembler/*.def", - "include/llvm/Target/XCore/Disassembler/*.inc", - "lib/Target/XCore/Disassembler/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/XCore"], - deps = [ - ":MCDisassembler", - ":Support", - ":XCoreInfo", - ":config", - ], -) - -cc_library( - name = "XCoreInfo", - srcs = glob([ - "lib/Target/XCore/TargetInfo/*.c", - "lib/Target/XCore/TargetInfo/*.cpp", - "lib/Target/XCore/TargetInfo/*.inc", - ]), - hdrs = glob([ - "include/llvm/Target/XCore/TargetInfo/*.h", - "include/llvm/Target/XCore/TargetInfo/*.def", - "include/llvm/Target/XCore/TargetInfo/*.inc", - "lib/Target/XCore/TargetInfo/*.h", - ]), - copts = llvm_copts + ["-Iexternal/llvm/lib/Target/XCore"], - deps = [ - ":Support", - ":config", - ], -) - -cc_library( - name = "XRay", - srcs = glob([ - "lib/XRay/*.c", - "lib/XRay/*.cpp", - "lib/XRay/*.inc", - "lib/XRay/*.h", - ]), - hdrs = glob([ - "include/llvm/XRay/*.h", - "include/llvm/XRay/*.def", - "include/llvm/XRay/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":Object", - ":Support", - ":config", - ], -) - -cc_library( - name = "gtest", - srcs = glob([ - "utils/unittest/*.c", - "utils/unittest/*.cpp", - "utils/unittest/*.inc", - "utils/unittest/*.h", - ]), - hdrs = glob([ - "utils/unittest/*.h", - "utils/unittest/*.def", - "utils/unittest/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":Support", - ":config", - ], -) - -cc_library( - name = "gtest_main", - srcs = glob([ - "utils/unittest/*.c", - "utils/unittest/*.cpp", - "utils/unittest/*.inc", - "utils/unittest/*.h", - ]), - hdrs = glob([ - "utils/unittest/*.h", - "utils/unittest/*.def", - "utils/unittest/*.inc", - ]), - copts = llvm_copts, - deps = [ - ":config", - ":gtest", - ], -) diff --git a/third_party/llvm/llvm.bzl b/third_party/llvm/llvm.bzl deleted file mode 100644 index bb8f966862..0000000000 --- a/third_party/llvm/llvm.bzl +++ /dev/null @@ -1,348 +0,0 @@ -"""This file contains BUILD extensions for generating source code from LLVM's table definition files using the TableGen tool. - -See http://llvm.org/cmds/tblgen.html for more information on the TableGen -tool. -TODO(chandlerc): Currently this expresses include-based dependencies as -"sources", and has no transitive understanding due to these files not being -correctly understood by the build system. -""" - -def _dict_add(*dictionaries): - """Returns a new `dict` that has all the entries of the given dictionaries. - - If the same key is present in more than one of the input dictionaries, the - last of them in the argument list overrides any earlier ones. - - This function is designed to take zero or one arguments as well as multiple - dictionaries, so that it follows arithmetic identities and callers can avoid - special cases for their inputs: the sum of zero dictionaries is the empty - dictionary, and the sum of a single dictionary is a copy of itself. - - Re-implemented here to avoid adding a dependency on skylib. - - Args: - *dictionaries: Zero or more dictionaries to be added. - - Returns: - A new `dict` that has all the entries of the given dictionaries. - """ - result = {} - for d in dictionaries: - result.update(d) - return result - -def gentbl(name, tblgen, td_file, td_srcs, tbl_outs, library = True, **kwargs): - """gentbl() generates tabular code from a table definition file. - - Args: - name: The name of the build rule for use in dependencies. - tblgen: The binary used to produce the output. - td_file: The primary table definitions file. - td_srcs: A list of table definition files included transitively. - tbl_outs: A list of tuples (opts, out), where each opts is a string of - options passed to tblgen, and the out is the corresponding output file - produced. - library: Whether to bundle the generated files into a library. - **kwargs: Keyword arguments to pass to subsidiary cc_library() rule. - """ - if td_file not in td_srcs: - td_srcs += [td_file] - includes = [] - for (opts, out) in tbl_outs: - outdir = out[:out.rindex("/")] - if outdir not in includes: - includes.append(outdir) - rule_suffix = "_".join(opts.replace("-", "_").replace("=", "_").split(" ")) - native.genrule( - name = "%s_%s_genrule" % (name, rule_suffix), - srcs = td_srcs, - outs = [out], - tools = [tblgen], - message = "Generating code from table: %s" % td_file, - cmd = (("$(location %s) " + "-I external/llvm/include " + - "-I $$(dirname $(location %s)) " + ("%s $(location %s) --long-string-literals=0 " + - "-o $@")) % ( - tblgen, - td_file, - opts, - td_file, - )), - ) - - # For now, all generated files can be assumed to comprise public interfaces. - # If this is not true, you should specify library = False - # and list the generated '.inc' files in "srcs". - if library: - native.cc_library( - name = name, - textual_hdrs = [f for (_, f) in tbl_outs], - includes = includes, - **kwargs - ) - -def llvm_target_cmake_vars(native_arch, target_triple): - return { - "LLVM_HOST_TRIPLE": target_triple, - "LLVM_DEFAULT_TARGET_TRIPLE": target_triple, - "LLVM_NATIVE_ARCH": native_arch, - } - -def _quote(s): - """Quotes the given string for use in a shell command. - - This function double-quotes the given string (in case it contains spaces or - other special characters) and escapes any special characters (dollar signs, - double-quotes, and backslashes) that may be present. - - Args: - s: The string to quote. - - Returns: - An escaped and quoted version of the string that can be passed to a shell - command. - """ - return ('"' + - s.replace("\\", "\\\\").replace("$", "\\$").replace('"', "\\\"") + - '"') - -def cmake_var_string(cmake_vars): - """Converts a dictionary to an input suitable for expand_cmake_vars. - - Ideally we would jist stringify in the expand_cmake_vars() rule, but select() - interacts badly with genrules. - - TODO(phawkins): replace the genrule() with native rule and delete this rule. - - Args: - cmake_vars: a dictionary with string keys and values that are convertable to - strings. - - Returns: - cmake_vars in a form suitable for passing to expand_cmake_vars. - """ - return " ".join([ - _quote("{}={}".format(k, str(v))) - for (k, v) in cmake_vars.items() - ]) - -def expand_cmake_vars(name, src, dst, cmake_vars): - """Expands #cmakedefine, #cmakedefine01, and CMake variables in a text file. - - Args: - name: the name of the rule - src: the input of the rule - dst: the output of the rule - cmake_vars: a string containing the CMake variables, as generated by - cmake_var_string. - """ - expand_cmake_vars_tool = "@com_stripe_ruby_typer//third_party/llvm:expand_cmake_vars" - native.genrule( - name = name, - srcs = [src], - tools = [expand_cmake_vars_tool], - outs = [dst], - cmd = ("$(location {}) ".format(expand_cmake_vars_tool) + cmake_vars + - "< $< > $@"), - ) - -# TODO(phawkins): the set of CMake variables was hardcoded for expediency. -# However, we should really detect many of these via configure-time tests. - -# The set of CMake variables common to all targets. -cmake_vars = { - # LLVM features - "ENABLE_BACKTRACES": 1, - "LLVM_BINDIR": "/dev/null", - "LLVM_DISABLE_ABI_BREAKING_CHECKS_ENFORCING": 0, - "LLVM_ENABLE_ABI_BREAKING_CHECKS": 0, - "LLVM_ENABLE_THREADS": 1, - "LLVM_ENABLE_ZLIB": 1, - "LLVM_ENABLE_DUMP": 1, - "LLVM_HAS_ATOMICS": 1, - "LLVM_INCLUDEDIR": "/dev/null", - "LLVM_INFODIR": "/dev/null", - "LLVM_MANDIR": "/dev/null", - "LLVM_NATIVE_TARGET": 1, - "LLVM_NATIVE_TARGETINFO": 1, - "LLVM_NATIVE_TARGETMC": 1, - "LLVM_NATIVE_ASMPRINTER": 1, - "LLVM_NATIVE_ASMPARSER": 1, - "LLVM_NATIVE_DISASSEMBLER": 1, - "LLVM_PREFIX": "/dev/null", - "LLVM_VERSION_MAJOR": 0, - "LLVM_VERSION_MINOR": 0, - "LLVM_VERSION_PATCH": 0, - "PACKAGE_NAME": "llvm", - "PACKAGE_STRING": "llvm tensorflow-trunk", - "PACKAGE_VERSION": "tensorflow-trunk", - "RETSIGTYPE": "void", -} - -# The set of CMake variables common to POSIX targets. -posix_cmake_vars = { - # Headers - "HAVE_DIRENT_H": 1, - "HAVE_DLFCN_H": 1, - "HAVE_ERRNO_H": 1, - "HAVE_EXECINFO_H": 1, - "HAVE_FCNTL_H": 1, - "HAVE_INTTYPES_H": 1, - "HAVE_PTHREAD_H": 1, - "HAVE_SIGNAL_H": 1, - "HAVE_STDINT_H": 1, - "HAVE_SYSEXITS_H": 1, - "HAVE_SYS_IOCTL_H": 1, - "HAVE_SYS_MMAN_H": 1, - "HAVE_SYS_PARAM_H": 1, - "HAVE_SYS_RESOURCE_H": 1, - "HAVE_SYS_STAT_H": 1, - "HAVE_SYS_TIME_H": 1, - "HAVE_SYS_TYPES_H": 1, - "HAVE_TERMIOS_H": 1, - "HAVE_UNISTD_H": 1, - "HAVE_ZLIB_H": 1, - - # Features - "HAVE_BACKTRACE": 1, - "BACKTRACE_HEADER": "execinfo.h", - "HAVE_DLOPEN": 1, - "HAVE_FUTIMES": 1, - "HAVE_GETCWD": 1, - "HAVE_GETPAGESIZE": 1, - "HAVE_GETRLIMIT": 1, - "HAVE_GETRUSAGE": 1, - "HAVE_GETTIMEOFDAY": 1, - "HAVE_INT64_T": 1, - "HAVE_ISATTY": 1, - "HAVE_LIBEDIT": 1, - "HAVE_LIBPTHREAD": 1, - "HAVE_LIBZ": 1, - "HAVE_MKDTEMP": 1, - "HAVE_MKSTEMP": 1, - "HAVE_MKTEMP": 1, - "HAVE_PREAD": 1, - "HAVE_PTHREAD_GETSPECIFIC": 1, - "HAVE_PTHREAD_MUTEX_LOCK": 1, - "HAVE_PTHREAD_RWLOCK_INIT": 1, - "HAVE_REALPATH": 1, - "HAVE_SBRK": 1, - "HAVE_SETENV": 1, - "HAVE_SETRLIMIT": 1, - "HAVE_SIGALTSTACK": 1, - "HAVE_STRERROR": 1, - "HAVE_STRERROR_R": 1, - "HAVE_STRTOLL": 1, - "HAVE_SYSCONF": 1, - "HAVE_UINT64_T": 1, - "HAVE__UNWIND_BACKTRACE": 1, - - # LLVM features - "LLVM_ON_UNIX": 1, - "LTDL_SHLIB_EXT": ".so", -} - -# CMake variables specific to the Linux platform -linux_cmake_vars = { - "HAVE_MALLOC_H": 1, - "HAVE_LINK_H": 1, - "HAVE_MALLINFO": 1, - "HAVE_FUTIMENS": 1, -} - -# CMake variables specific to the FreeBSD platform -freebsd_cmake_vars = { - "HAVE_MALLOC_H": 1, - "HAVE_LINK_H": 1, -} - -# CMake variables specific to the Darwin (Mac OS X) platform. -darwin_cmake_vars = { - "HAVE_MALLOC_MALLOC_H": 1, - "HAVE_MALLOC_ZONE_STATISTICS": 1, -} - -# CMake variables specific to the Windows platform. -win32_cmake_vars = { - # Headers - "HAVE_ERRNO_H": 1, - "HAVE_EXECINFO_H": 1, - "HAVE_FCNTL_H": 1, - "HAVE_FENV_H": 1, - "HAVE_INTTYPES_H": 1, - "HAVE_MALLOC_H": 1, - "HAVE_SIGNAL_H": 1, - "HAVE_STDINT_H": 1, - "HAVE_SYS_STAT_H": 1, - "HAVE_SYS_TYPES_H": 1, - "HAVE_ZLIB_H": 1, - - # Features - "BACKTRACE_HEADER": "execinfo.h", - "HAVE_GETCWD": 1, - "HAVE_INT64_T": 1, - "HAVE_STRERROR": 1, - "HAVE_STRTOLL": 1, - "HAVE_SYSCONF": 1, - "HAVE_UINT64_T": 1, - "HAVE__CHSIZE_S": 1, - "HAVE___CHKSTK": 1, - - # MSVC specific - "stricmp": "_stricmp", - "strdup": "_strdup", - - # LLVM features - "LTDL_SHLIB_EXT": ".dll", -} - -# Select a set of CMake variables based on the platform. -# TODO(phawkins): use a better method to select the right host triple, rather -# than hardcoding x86_64. -llvm_all_cmake_vars = select({ - "@com_stripe_ruby_typer//tools/config:darwin_x86_64": cmake_var_string( - _dict_add( - cmake_vars, - llvm_target_cmake_vars("X86", "x86_64-apple-darwin"), - posix_cmake_vars, - darwin_cmake_vars, - ), - ), - "@com_stripe_ruby_typer//tools/config:linux_x86_64": cmake_var_string( - _dict_add( - cmake_vars, - llvm_target_cmake_vars("X86", "x86_64-unknown-linux_gnu"), - posix_cmake_vars, - linux_cmake_vars, - ), - ), -}) - -llvm_linkopts = select({ - "//conditions:default": ["-ldl", "-lm", "-lpthread"], -}) - -llvm_defines = [ - "LLVM_ENABLE_STATS", - "__STDC_LIMIT_MACROS", - "__STDC_CONSTANT_MACROS", - "__STDC_FORMAT_MACROS", - "LLVM_BUILD_GLOBAL_ISEL", -] - -llvm_copts = [ - "-Wno-error=implicit-fallthrough", - "-Wno-error=range-loop-construct", - - # don't compile llvm with lto. It's too slow to compile and not worth it - "-fno-lto", -] - -# Platform specific sources for libSupport. - -def llvm_support_platform_specific_srcs_glob(): - return select({ - "//conditions:default": native.glob([ - "lib/Support/Unix/*.inc", - "lib/Support/Unix/*.h", - ]), - }) diff --git a/third_party/lmdb.BUILD b/third_party/lmdb.BUILD index 62ebfb227c..9588393735 100644 --- a/third_party/lmdb.BUILD +++ b/third_party/lmdb.BUILD @@ -10,7 +10,10 @@ cc_library( "libraries/liblmdb/lmdb.h", "libraries/liblmdb/midl.h", ], - copts = ["-Wno-implicit-fallthrough"], + copts = [ + "-Wno-implicit-fallthrough", + "-Wno-unused-but-set-variable", + ], includes = [ "libraries/liblmdb/", ], diff --git a/third_party/mimalloc.BUILD b/third_party/mimalloc.BUILD index 3a2ce0d946..b056e965e2 100644 --- a/third_party/mimalloc.BUILD +++ b/third_party/mimalloc.BUILD @@ -6,7 +6,7 @@ filegroup( visibility = ["//visibility:public"], ) -# The MI_INSTALL_TOPLEVEL CMake flag is depracated +# The MI_INSTALL_TOPLEVEL CMake flag is deprecated # https://github.com/microsoft/mimalloc/blob/4e50d6714d471b72b2285e25a3df6c92db944593/CMakeLists.txt#L33 # this could break at any point. The current default # behavior is to append the mimalloc version. Eg: diff --git a/third_party/progressbar/progressbar/progressbar.h b/third_party/progressbar/progressbar/progressbar.h index 8231107168..6e9d12655f 100644 --- a/third_party/progressbar/progressbar/progressbar.h +++ b/third_party/progressbar/progressbar/progressbar.h @@ -62,7 +62,7 @@ progressbar *progressbar_new(const char *label, unsigned long max); /// @param max The number of times the progressbar must be incremented before it is considered complete, /// or, in other words, the number of tasks that this progressbar is tracking. /// @param format The format of the progressbar. The string provided must be three characters, and it will -/// be interpretted with the first character as the left border of the bar, the second +/// be interpreted with the first character as the left border of the bar, the second /// character of the bar and the third character as the right border of the bar. For example, /// "<->" would result in a bar formatted like "<------ >". /// diff --git a/third_party/ruby/10151_no_hash_allocate_static_kwargs_3_3_only.patch b/third_party/ruby/10151_no_hash_allocate_static_kwargs_3_3_only.patch new file mode 100644 index 0000000000..88612cf493 --- /dev/null +++ b/third_party/ruby/10151_no_hash_allocate_static_kwargs_3_3_only.patch @@ -0,0 +1,136 @@ +diff --git a/vm_args.c b/vm_args.c +index 14ae550d2f..79e4b2e0a1 100644 +--- a/vm_args.c ++++ b/vm_args.c +@@ -396,6 +396,109 @@ args_setup_kw_parameters(rb_execution_context_t *const ec, const rb_iseq_t *cons + locals[key_num] = unspecified_bits_value; + } + ++static void ++args_setup_kw_parameters_from_kwsplat(rb_execution_context_t *const ec, const rb_iseq_t *const iseq, ++ VALUE keyword_hash, VALUE *const locals, bool remove_hash_value) ++{ ++ const ID *acceptable_keywords = ISEQ_BODY(iseq)->param.keyword->table; ++ const int req_key_num = ISEQ_BODY(iseq)->param.keyword->required_num; ++ const int key_num = ISEQ_BODY(iseq)->param.keyword->num; ++ const VALUE * const default_values = ISEQ_BODY(iseq)->param.keyword->default_values; ++ VALUE missing = 0; ++ int i, di; ++ int unspecified_bits = 0; ++ size_t keyword_size = RHASH_SIZE(keyword_hash); ++ VALUE unspecified_bits_value = Qnil; ++ ++ for (i=0; i hash */ ++ int j; ++ unspecified_bits_value = rb_hash_new(); ++ ++ for (j=0; jparam.flags.has_kwrest) { ++ const int rest_hash_index = key_num + 1; ++ locals[rest_hash_index] = keyword_hash; ++ } ++ else { ++ if (!remove_hash_value) { ++ if (keyword_size != 0) { ++ /* Recurse with duplicated keyword hash in remove mode. ++ * This is simpler than writing code to check which entries in the hash do not match. ++ * This will raise an exception, so the additional performance impact shouldn't be material. ++ */ ++ args_setup_kw_parameters_from_kwsplat(ec, iseq, rb_hash_dup(keyword_hash), locals, true); ++ } ++ } ++ else if (!RHASH_EMPTY_P(keyword_hash)) { ++ argument_kw_error(ec, iseq, "unknown", rb_hash_keys(keyword_hash)); ++ } ++ } ++ ++ if (NIL_P(unspecified_bits_value)) { ++ unspecified_bits_value = INT2FIX(unspecified_bits); ++ } ++ locals[key_num] = unspecified_bits_value; ++} ++ + static inline void + args_setup_kw_rest_parameter(VALUE keyword_hash, VALUE *locals, int kw_flag) + { +@@ -720,15 +823,12 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co + args_setup_kw_parameters(ec, iseq, args->kw_argv, kw_arg->keyword_len, kw_arg->keywords, klocals); + } + else if (!NIL_P(keyword_hash)) { +- int kw_len = rb_long2int(RHASH_SIZE(keyword_hash)); +- struct fill_values_arg arg; +- /* copy kw_argv */ +- arg.keys = args->kw_argv = ALLOCA_N(VALUE, kw_len * 2); +- arg.vals = arg.keys + kw_len; +- arg.argc = 0; +- rb_hash_foreach(keyword_hash, fill_keys_values, (VALUE)&arg); +- VM_ASSERT(arg.argc == kw_len); +- args_setup_kw_parameters(ec, iseq, arg.vals, kw_len, arg.keys, klocals); ++ bool remove_hash_value = false; ++ if (ISEQ_BODY(iseq)->param.flags.has_kwrest) { ++ keyword_hash = check_kwrestarg(keyword_hash, &kw_flag); ++ remove_hash_value = true; ++ } ++ args_setup_kw_parameters_from_kwsplat(ec, iseq, keyword_hash, klocals, remove_hash_value); + } + else { + VM_ASSERT(args_argc(args) == 0); diff --git a/third_party/ruby/10306_no_hash_allocate_static_kwargs_3_3_only.patch b/third_party/ruby/10306_no_hash_allocate_static_kwargs_3_3_only.patch new file mode 100644 index 0000000000..48706154e5 --- /dev/null +++ b/third_party/ruby/10306_no_hash_allocate_static_kwargs_3_3_only.patch @@ -0,0 +1,15 @@ +diff --git a/vm_args.c b/vm_args.c +index 79e4b2e0a1..3e95fe50c0 100644 +--- a/vm_args.c ++++ b/vm_args.c +@@ -714,8 +714,9 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co + kw_flag &= ~(VM_CALL_KW_SPLAT | VM_CALL_KW_SPLAT_MUT); + } + else { +- if (!(kw_flag & VM_CALL_KW_SPLAT_MUT)) { ++ if (!(kw_flag & VM_CALL_KW_SPLAT_MUT) && !ISEQ_BODY(iseq)->param.flags.has_kw) { + converted_keyword_hash = rb_hash_dup(converted_keyword_hash); ++ kw_flag |= VM_CALL_KW_SPLAT_MUT; + } + + if (last_arg != converted_keyword_hash) { diff --git a/third_party/ruby/10899-avoid-unnecessary-writes-in-gc-marking.patch b/third_party/ruby/10899-avoid-unnecessary-writes-in-gc-marking.patch new file mode 100644 index 0000000000..15a39d0533 --- /dev/null +++ b/third_party/ruby/10899-avoid-unnecessary-writes-in-gc-marking.patch @@ -0,0 +1,52 @@ +commit 9d6b8806a4e0801c260f628e48a21ae94cb3fc91 +Author: John Hawthorn +Date: Sun Jun 2 23:39:36 2024 -0700 + + Avoid unnecessary writes to ISEQ during GC + + On mark we check whether a callcache has been invalidated and if it has + we replace it with the empty callcache, rb_vm_empty_cc(). However we + also consider the empty callcache to not be active, and so previously + would overwrite it with itself. + + These additional writes are problematic because they may force + Copy-on-Write to occur on the memory page, increasing system memory use. + +commit 520ab22725124ef3d588c9ec664b1adfc98da905 +Author: John Hawthorn +Date: Sun Jun 2 23:40:24 2024 -0700 + + Avoid unnecessary writes to imemo_env during GC + + Similar to the previous commit, to avoid unnecessary Copy-on-Write + memory use we should only set this flag when it has not previously been + set. + +diff --git a/gc.c b/gc.c +index 378e4a31cb..2023d9a2d1 100644 +--- a/gc.c ++++ b/gc.c +@@ -7228,7 +7228,9 @@ gc_mark_imemo(rb_objspace_t *objspace, VALUE obj) + GC_ASSERT(env->ep[VM_ENV_DATA_INDEX_ENV] == obj); + GC_ASSERT(VM_ENV_ESCAPED_P(env->ep)); + rb_gc_mark_values((long)env->env_size, env->env); +- VM_ENV_FLAGS_SET(env->ep, VM_ENV_FLAG_WB_REQUIRED); ++ if (!VM_ENV_FLAGS(env->ep, VM_ENV_FLAG_WB_REQUIRED)) { ++ VM_ENV_FLAGS_SET(env->ep, VM_ENV_FLAG_WB_REQUIRED); ++ } + gc_mark(objspace, (VALUE)rb_vm_env_prev_env(env)); + gc_mark(objspace, (VALUE)env->iseq); + } +diff --git a/iseq.c b/iseq.c +index 27c5bb5d82..e8cafd6caa 100644 +--- a/iseq.c ++++ b/iseq.c +@@ -344,7 +344,7 @@ rb_iseq_mark_and_move(rb_iseq_t *iseq, bool reference_updating) + if (cc_is_active(cds[i].cc, reference_updating)) { + rb_gc_mark_and_move_ptr(&cds[i].cc); + } +- else { ++ else if (cds[i].cc != rb_vm_empty_cc()) { + cds[i].cc = rb_vm_empty_cc(); + } + } diff --git a/third_party/ruby/build-ruby.bzl b/third_party/ruby/build-ruby.bzl index d5051be0c0..6032b07205 100644 --- a/third_party/ruby/build-ruby.bzl +++ b/third_party/ruby/build-ruby.bzl @@ -64,14 +64,24 @@ export PATH="$(dirname "{cc}"):$(dirname $(realpath {rustc})):$PATH" cp -aL "{src_dir}"/* "$build_dir" # Manually copy over .bundle as bundled gems are no longer installed # https://github.com/ruby/ruby/pull/6234 + + +if [ "$(uname)" == "Darwin" ]; then + # according to the man page -r is highly discouraged on macOS + CPFLAGS=-RaL +elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then + CPFLAGS=-raL +fi + if [[ -d "{src_dir}/.bundle" ]]; then - cp -raL "{src_dir}/.bundle" "$build_dir" + cp $CPFLAGS "{src_dir}/.bundle" "$build_dir" fi {install_extra_srcs} {install_append_srcs} libyaml_loc="$(realpath {libyaml})" +libffi_loc="$(realpath {libffi})" pushd "$build_dir" > /dev/null @@ -109,6 +119,7 @@ export LDFLAGS="{sysroot_flag} ${{lib_path[*]:-}} {linkopts}" run_cmd ./configure \ {configure_flags} \ --with-libyaml-source-dir=$libyaml_loc \ + --with-libffi-source-dir=$libffi_loc \ --enable-load-relative \ --with-destdir="$out_dir" \ --with-rubyhdrdir='${{includedir}}' \ @@ -277,7 +288,7 @@ def _build_ruby_impl(ctx): # Build ctx.actions.run_shell( mnemonic = "BuildRuby", - inputs = deps + ctx.files.src + ctx.files.rubygems + ctx.files.libyaml + ctx.files.gems + ctx.files.extra_srcs + ctx.files.append_srcs, + inputs = deps + ctx.files.src + ctx.files.rubygems + ctx.files.libyaml + ctx.files.libffi + ctx.files.gems + ctx.files.extra_srcs + ctx.files.append_srcs, outputs = outputs, command = ctx.expand_location(_BUILD_RUBY.format( cc = cc, @@ -291,6 +302,7 @@ def _build_ruby_impl(ctx): libs = " ".join(libs), rubygems = ctx.files.rubygems[0].path, libyaml = ctx.files.libyaml[0].dirname, + libffi = ctx.files.libffi[0].dirname, rustc = ctx.toolchains["@rules_rust//rust:toolchain_type"].rustc.path, configure_flags = " ".join(ctx.attr.configure_flags), sysroot_flag = ctx.attr.sysroot_flag, @@ -332,6 +344,10 @@ _build_ruby = rule( default = Label("@libyaml//:libyaml"), doc = "A filegroup containing the libyaml source, `configure` should be at the top level", ), + "libffi": attr.label( + default = Label("@libffi//:libffi"), + doc = "A filegroup containing the libffi source, `configure` should be at the top level", + ), "extra_srcs": attr.label_list( doc = "A list of *.c and *.h files to treat as extra source files to libruby", ), diff --git a/third_party/ruby/gc-more-t-none-context.patch b/third_party/ruby/gc-more-t-none-context.patch new file mode 100644 index 0000000000..77af41ca3d --- /dev/null +++ b/third_party/ruby/gc-more-t-none-context.patch @@ -0,0 +1,39 @@ +diff --git gc.c gc.c +index 346a77ec63..6e025cb200 100644 +--- gc.c ++++ gc.c +@@ -2935,7 +2935,33 @@ cc_table_mark_i(ID id, VALUE ccs_ptr, void *data_ptr) + return ID_TABLE_DELETE; + } + else { +- gc_mark(data->objspace, (VALUE)ccs->cme); ++ VALUE cme_obj = (VALUE)ccs->cme; ++ ++ if (UNLIKELY(RB_TYPE_P(cme_obj, T_NONE))) { ++ VALUE class_path = rb_class_path_cached(data->klass); ++ if (!NIL_P(class_path)) { ++ char *objname = rb_id2name(id); ++ if (objname) { ++ fprintf( ++ stdout, ++ "ERROR (cc_table_mark_i): will fail shortly with try to mark T_NONE object -- obj:%s, P:%s\n", ++ objname, ++ RSTRING_PTR(class_path) ++ ); ++ } ++ } else { ++ char *objname = rb_id2name(id); ++ if (objname) { ++ fprintf( ++ stdout, ++ "ERROR (cc_table_mark_i): will fail shortly with try to mark T_NONE object -- obj:%s\n", ++ objname ++ ); ++ } ++ } ++ } ++ ++ gc_mark(data->objspace, cme_obj); + + for (int i=0; ilen; i++) { + VM_ASSERT(data->klass == ccs->entries[i].cc->klass); diff --git a/third_party/ruby/gc-t-none-context.patch b/third_party/ruby/gc-t-none-context.patch new file mode 100644 index 0000000000..f795727860 --- /dev/null +++ b/third_party/ruby/gc-t-none-context.patch @@ -0,0 +1,78 @@ +diff --git gc.c gc.c +index 6f4ae3a397..033a89f8f3 100644 +--- gc.c ++++ gc.c +@@ -6709,6 +6709,49 @@ gc_aging(rb_objspace_t *objspace, VALUE obj) + NOINLINE(static void gc_mark_ptr(rb_objspace_t *objspace, VALUE obj)); + static void reachable_objects_from_callback(VALUE obj); + ++/** ++ * BEGIN Stripe debugging code. ++ * ++ * Context: We are seeing instances of "try to mark T_NONE" crashes at Stripe, at times tied to T_HASH parent object. ++ * In these cases, we want to print contextual information about the parent object at the time of the crash. ++ * ++*/ ++ ++const char * ++gc_debug_parent_object_STRIPE(rb_objspace_t *objspace, VALUE parent, char *buff, const int buff_size) { ++ int pos = 0; ++ ++ #define BUFF_ARGS buff + pos, buff_size - pos ++ #define APPENDF(f) if ((pos += snprintf f) >= buff_size) goto end ++ ++ // "P" is for parent object ++ APPENDF((BUFF_ARGS, "P:%s", obj_type_name(parent))); ++ ++ switch (BUILTIN_TYPE(parent)) { ++ case T_CLASS: ++ case T_MODULE: { ++ // If the parent object is a class or module, print its fully qualified name. ++ VALUE class_path = rb_class_path_cached(parent); ++ if (!NIL_P(class_path)) { ++ APPENDF((BUFF_ARGS, "(%s)", RSTRING_PTR(class_path))); ++ } ++ break; ++ } ++ // No special handling for other types ++ } ++ ++ APPENDF((BUFF_ARGS, "\n")); ++ ++ end: ++ ++ return buff; ++ ++ #undef BUFF_ARGS ++ #undef APPENDF ++} ++ ++/***** END Stripe debugging code ***/ ++ + static void + gc_mark_ptr(rb_objspace_t *objspace, VALUE obj) + { +@@ -6729,8 +6772,22 @@ gc_mark_ptr(rb_objspace_t *objspace, VALUE obj) + + if (UNLIKELY(RB_TYPE_P(obj, T_NONE))) { + rp(obj); +- rb_bug("try to mark T_NONE object"); /* check here will help debugging */ ++ ++/***** BEGIN Stripe debugging code ***/ ++ // Note: rgengc.parent_object will be set if we followed a reference from an old-gen object to ++ // get here. ++ if (objspace->rgengc.parent_object) { ++ char buff[0x200]; ++ rb_bug( ++ "try to mark T_NONE object -- %s", ++ gc_debug_parent_object_STRIPE(objspace, objspace->rgengc.parent_object, buff, 0x200) ++ ); /* check here will help debugging */ ++ } else { ++/***** END Stripe debugging code ***/ ++ rb_bug("try to mark T_NONE object"); /* check here will help debugging */ ++ } + } ++ + gc_aging(objspace, obj); + gc_grey(objspace, obj); + } diff --git a/third_party/ruby/gc-write-barrier-cme.patch b/third_party/ruby/gc-write-barrier-cme.patch new file mode 100644 index 0000000000..ae02d9fafc --- /dev/null +++ b/third_party/ruby/gc-write-barrier-cme.patch @@ -0,0 +1,65 @@ +diff --git vm_eval.c vm_eval.c +index 0abb4644f9..98db7f166c 100644 +--- vm_eval.c ++++ vm_eval.c +@@ -395,8 +395,7 @@ cc_new(VALUE klass, ID mid, int argc, const rb_callable_method_entry_t *cme) + ccs = (struct rb_class_cc_entries *)ccs_data; + } + else { +- ccs = vm_ccs_create(klass, cme); +- rb_id_table_insert(cc_tbl, mid, (VALUE)ccs); ++ ccs = vm_ccs_create(klass, cc_tbl, mid, cme); + } + + for (int i=0; ilen; i++) { +diff --git vm_insnhelper.c vm_insnhelper.c +index e01d39de77..aff6baa340 100644 +--- vm_insnhelper.c ++++ vm_insnhelper.c +@@ -1689,7 +1689,7 @@ static VALUE vm_call_general(rb_execution_context_t *ec, rb_control_frame_t *reg + static VALUE vm_mtbl_dump(VALUE klass, ID target_mid); + + static struct rb_class_cc_entries * +-vm_ccs_create(VALUE klass, const rb_callable_method_entry_t *cme) ++vm_ccs_create(VALUE klass, struct rb_id_table *cc_tbl, ID mid, const rb_callable_method_entry_t *cme) + { + struct rb_class_cc_entries *ccs = ALLOC(struct rb_class_cc_entries); + #if VM_CHECK_MODE > 0 +@@ -1697,9 +1697,12 @@ vm_ccs_create(VALUE klass, const rb_callable_method_entry_t *cme) + #endif + ccs->capa = 0; + ccs->len = 0; +- RB_OBJ_WRITE(klass, &ccs->cme, cme); ++ ccs->cme = cme; + METHOD_ENTRY_CACHED_SET((rb_callable_method_entry_t *)cme); + ccs->entries = NULL; ++ ++ rb_id_table_insert(cc_tbl, mid, (VALUE)ccs); ++ RB_OBJ_WRITTEN(klass, Qundef, cme); + return ccs; + } + +@@ -1850,8 +1853,7 @@ vm_search_cc(const VALUE klass, const struct rb_callinfo * const ci) + } + else { + // TODO: required? +- ccs = vm_ccs_create(klass, cme); +- rb_id_table_insert(cc_tbl, mid, (VALUE)ccs); ++ ccs = vm_ccs_create(klass, cc_tbl, mid, cme); + } + } + +diff --git vm_method.c vm_method.c +index 94c3f978dc..7cebd2e3bc 100644 +--- vm_method.c ++++ vm_method.c +@@ -1288,8 +1288,7 @@ cache_callable_method_entry(VALUE klass, ID mid, const rb_callable_method_entry_ + VM_ASSERT(ccs->cme == cme); + } + else { +- ccs = vm_ccs_create(klass, cme); +- rb_id_table_insert(cc_tbl, mid, (VALUE)ccs); ++ ccs = vm_ccs_create(klass, cc_tbl, mid, cme); + } + } + diff --git a/third_party/ruby/ldflags.patch b/third_party/ruby/ldflags.patch new file mode 100644 index 0000000000..28361cf13b --- /dev/null +++ b/third_party/ruby/ldflags.patch @@ -0,0 +1,13 @@ +diff --git ext/fiddle/extconf.rb ext/fiddle/extconf.rb +index 2d85b3eea5..eb03d3bfa6 100644 +--- a/ext/fiddle/extconf.rb ++++ b/ext/fiddle/extconf.rb +@@ -96,7 +96,7 @@ def enable_debug_build_flag(flags) + + FileUtils.mkdir_p(libffi.dir) + libffi.opt = CONFIG['configure_args'][/'(-C)'/, 1] +- libffi.ldflags = RbConfig.expand("$(LDFLAGS) #{libpathflag([relative_from($topdir, "..")])} #{$LIBRUBYARG}".dup) ++ libffi.ldflags = RbConfig.expand("$(LDFLAGS) #{libpathflag([relative_from($topdir, "..")])}".dup) + libffi.arch = RbConfig::CONFIG['host'] + if $mswin + unless find_executable(as = /x64/ =~ libffi.arch ? "ml64" : "ml") diff --git a/third_party/ruby/libffi.BUILD b/third_party/ruby/libffi.BUILD new file mode 100644 index 0000000000..569771055f --- /dev/null +++ b/third_party/ruby/libffi.BUILD @@ -0,0 +1,7 @@ +exports_files(glob(["**/*"])) + +filegroup( + name = "libffi", + srcs = glob(["**/*"]), + visibility = ["//visibility:public"], +) diff --git a/third_party/ruby/reinit_native_sched_lock.patch b/third_party/ruby/reinit_native_sched_lock.patch new file mode 100644 index 0000000000..aa0bd1e050 --- /dev/null +++ b/third_party/ruby/reinit_native_sched_lock.patch @@ -0,0 +1,12 @@ +diff --git a/thread_pthread.c b/thread_pthread.c +index 7918d0d3d9..a5568885e0 100644 +--- a/thread_pthread.c ++++ b/thread_pthread.c +@@ -1530,6 +1530,7 @@ thread_sched_atfork(struct rb_thread_sched *sched) + } + vm->ractor.sched.running_cnt = 0; + ++ rb_native_mutex_initialize(&vm->ractor.sched.lock); + // rb_native_cond_destroy(&vm->ractor.sched.cond); + rb_native_cond_initialize(&vm->ractor.sched.cond); + rb_native_cond_initialize(&vm->ractor.sched.barrier_complete_cond); diff --git a/third_party/ruby/ruby-versions.txt b/third_party/ruby/ruby-versions.txt index ad39e37133..aea2736f83 100644 --- a/third_party/ruby/ruby-versions.txt +++ b/third_party/ruby/ruby-versions.txt @@ -1,3 +1,2 @@ -sorbet_ruby_2_7 sorbet_ruby_3_1 -sorbet_ruby_3_2 +sorbet_ruby_3_3 diff --git a/third_party/ruby/ruby.BUILD b/third_party/ruby/ruby.BUILD index 0d79f745b2..d80e4b5d3a 100644 --- a/third_party/ruby/ruby.BUILD +++ b/third_party/ruby/ruby.BUILD @@ -19,7 +19,7 @@ ruby( "//conditions:default": [], }) + select({ # Do not enable the JIT unless opted in. - "@com_stripe_ruby_typer//tools/config:jit_enabled": [], + "@com_stripe_ruby_typer//tools/config:jit_enabled": ["--enable-yjit=stats"], "//conditions:default": ["--disable-jit-support"], }), copts = [ @@ -60,12 +60,12 @@ ruby( rubygems = "@rubygems_update_stripe//file", deps = select({ "@platforms//os:osx": [ - "@system_ssl_darwin//:ssl", "@system_ssl_darwin//:crypto", + "@system_ssl_darwin//:ssl", ], "@platforms//os:linux": [ - "@system_ssl_linux//:ssl", "@system_ssl_linux//:crypto", + "@system_ssl_linux//:ssl", ], }), ) diff --git a/third_party/ruby/ruby_3_3.BUILD b/third_party/ruby/ruby_3_3.BUILD new file mode 100644 index 0000000000..414c966d94 --- /dev/null +++ b/third_party/ruby/ruby_3_3.BUILD @@ -0,0 +1,68 @@ +# vim: ft=bzl sw=4 ts=4 et + +load("@com_stripe_ruby_typer//third_party/ruby:build-ruby.bzl", "ruby") + +ruby( + append_srcs = [], + configure_flags = [ + "--enable-shared", + "--sysconfdir=/etc", + "--localstatedir=/var", + "--disable-maintainer-mode", + "--disable-dependency-tracking", + "--disable-install-doc", + "--without-git", + ] + select({ + # Enforce that we don't need Ruby to build in release builds. + # (In non-release builds, we allow for an available system Ruby to + # speed up the build.) + "@com_stripe_ruby_typer//tools/config:release": ["--with-baseruby=no"], + "//conditions:default": [], + }) + select({ + # Do not enable the JIT unless opted in. + "@com_stripe_ruby_typer//tools/config:jit_enabled": ["--enable-yjit"], + "//conditions:default": ["--disable-jit-support"], + }), + copts = [ + "-g", + "-fstack-protector-strong", + "-Wformat", + "-Werror=format-security", + # The Ruby build is very noisy without this flag. + "-Wno-compound-token-split-by-macro", + ] + select({ + "@com_stripe_ruby_typer//tools/config:dbg": [], + "//conditions:default": ["-O2"], + }), + cppopts = [ + "-Wdate-time", + "-D_FORTIFY_SOURCE=2", + "-fPIC", + ], + extra_srcs = [], + gems = [ + "@bundler_stripe//file", + ], + linkopts = select({ + "@platforms//os:linux": [ + "-Wl,-Bsymbolic-functions", + "-Wl,-z,relro", + "-Wl,-z,noexecstack", + ], + "@platforms//os:osx": [ + "-mlinker-version=400", + ], + "//conditions:default": [], + }), + rubygems = "@rubygems_update_stripe//file", + deps = select({ + "@platforms//os:osx": [ + "@system_ssl_darwin//:ssl", + "@system_ssl_darwin//:crypto", + ], + "@platforms//os:linux": [ + "@system_ssl_linux//:ssl", + "@system_ssl_linux//:crypto", + ], + }), +) diff --git a/third_party/ruby/ruby_for_compiler.BUILD b/third_party/ruby/ruby_for_compiler.BUILD deleted file mode 100644 index b3aa84ac15..0000000000 --- a/third_party/ruby/ruby_for_compiler.BUILD +++ /dev/null @@ -1,69 +0,0 @@ -# vim: ft=bzl sw=4 ts=4 et - -load("@com_stripe_ruby_typer//third_party/ruby:build-ruby.bzl", "ruby") - -ruby( - append_srcs = [ - "@com_stripe_ruby_typer//compiler/ruby-static-exports:vm_append_files", - "@com_stripe_ruby_typer//compiler/IREmitter/Payload/patches:vm_append_files", - ], - configure_flags = [ - "--enable-shared", - "--sysconfdir=/etc", - "--localstatedir=/var", - "--disable-maintainer-mode", - "--disable-dependency-tracking", - "--disable-jit-support", - "--disable-install-doc", - ] + select({ - # Enforce that we don't need Ruby to build in release builds. - # (In non-release builds, we allow for an available system Ruby to - # speed up the build.) - "@com_stripe_ruby_typer//tools/config:release": ["--with-baseruby=no"], - "//conditions:default": [], - }), - copts = [ - "-g", - "-fstack-protector-strong", - "-Wformat", - "-Werror=format-security", - # The Ruby build is very noisy without this flag. - "-Wno-compound-token-split-by-macro", - ] + select({ - "@com_stripe_ruby_typer//tools/config:dbg": [], - "//conditions:default": ["-O2"], - }), - cppopts = [ - "-Wdate-time", - "-D_FORTIFY_SOURCE=2", - ], - extra_srcs = [ - "@com_stripe_ruby_typer//sorbet_version:sorbet_version_srcs", - "@com_stripe_ruby_typer//compiler/IREmitter/Payload:vm_payload_srcs", - ], - gems = [ - "@bundler_stripe//file", - ], - linkopts = select({ - "@platforms//os:linux": [ - "-Wl,-Bsymbolic-functions", - "-Wl,-z,relro", - "-Wl,-z,noexecstack", - ], - "@platforms//os:osx": [ - "-mlinker-version=400", - ], - "//conditions:default": [], - }), - rubygems = "@rubygems_update_stripe//file", - deps = select({ - "@platforms//os:osx": [ - "@system_ssl_darwin//:ssl", - "@system_ssl_darwin//:crypto", - ], - "@platforms//os:linux": [ - "@system_ssl_linux//:ssl", - "@system_ssl_linux//:crypto", - ], - }), -) diff --git a/third_party/ruby/sorbet_ruby_2_7_for_compiler.patch b/third_party/ruby/sorbet_ruby_2_7_for_compiler.patch deleted file mode 100644 index 0a03994d25..0000000000 --- a/third_party/ruby/sorbet_ruby_2_7_for_compiler.patch +++ /dev/null @@ -1,3617 +0,0 @@ -From c780e0d9fe693ef62f0cf5f351731a59cd9c1101 Mon Sep 17 00:00:00 2001 -From: Nathan Froyd -Date: Fri, 15 Jan 2021 10:26:17 -0500 -Subject: [PATCH 01/30] add a VM_METHOD_TYPE_SORBET - ---- - class.c | 6 ++++ - gc.c | 3 ++ - include/ruby/intern.h | 2 ++ - method.h | 9 ++++++ - proc.c | 5 ++++ - vm_eval.c | 56 +++++++++++++++++++++++++++++++++++ - vm_insnhelper.c | 69 +++++++++++++++++++++++++++++++++++++++++++ - vm_method.c | 31 +++++++++++++++++++ - 8 files changed, 181 insertions(+) - -diff --git a/class.c b/class.c -index c866d1d72721..70b8deba2759 100644 ---- a/class.c -+++ b/class.c -@@ -1757,6 +1757,12 @@ rb_define_singleton_method(VALUE obj, const char *name, VALUE (*func)(ANYARGS), - rb_define_method(singleton_class_of(obj), name, func, argc); - } - -+void -+rb_define_singleton_sorbet_method(VALUE obj, const char *name, VALUE(*func)(ANYARGS), int argc) -+{ -+ rb_add_method_sorbet(singleton_class_of(obj), rb_intern(name), func, argc, METHOD_VISI_PUBLIC); -+} -+ - #ifdef rb_define_module_function - #undef rb_define_module_function - #endif -diff --git a/gc.c b/gc.c -index 73faf46b128b..e41d05706f61 100644 ---- a/gc.c -+++ b/gc.c -@@ -4902,6 +4902,7 @@ mark_method_entry(rb_objspace_t *objspace, const rb_method_entry_t *me) - case VM_METHOD_TYPE_OPTIMIZED: - case VM_METHOD_TYPE_UNDEF: - case VM_METHOD_TYPE_NOTIMPLEMENTED: -+ case VM_METHOD_TYPE_SORBET: - break; - } - } -@@ -8031,6 +8032,7 @@ gc_ref_update_method_entry(rb_objspace_t *objspace, rb_method_entry_t *me) - case VM_METHOD_TYPE_OPTIMIZED: - case VM_METHOD_TYPE_UNDEF: - case VM_METHOD_TYPE_NOTIMPLEMENTED: -+ case VM_METHOD_TYPE_SORBET: - break; - } - } -@@ -11413,6 +11415,7 @@ rb_method_type_name(rb_method_type_t type) - case VM_METHOD_TYPE_ALIAS: return "alias"; - case VM_METHOD_TYPE_REFINED: return "refined"; - case VM_METHOD_TYPE_CFUNC: return "cfunc"; -+ case VM_METHOD_TYPE_SORBET: return "sorbet"; - case VM_METHOD_TYPE_ZSUPER: return "zsuper"; - case VM_METHOD_TYPE_MISSING: return "missing"; - case VM_METHOD_TYPE_OPTIMIZED: return "optimized"; -diff --git a/include/ruby/intern.h b/include/ruby/intern.h -index 2f60fb569ecb..0a6d503eb4f4 100644 ---- a/include/ruby/intern.h -+++ b/include/ruby/intern.h -@@ -237,6 +237,8 @@ void rb_undef(VALUE, ID); - void rb_define_protected_method(VALUE, const char*, VALUE (*)(ANYARGS), int); - void rb_define_private_method(VALUE, const char*, VALUE (*)(ANYARGS), int); - void rb_define_singleton_method(VALUE, const char*, VALUE(*)(ANYARGS), int); -+/* included so we don't expose singleton_class_of outside of class.c */ -+void rb_define_singleton_sorbet_method(VALUE, const char*, VALUE(*)(ANYARGS), int); - VALUE rb_singleton_class(VALUE); - /* compar.c */ - int rb_cmpint(VALUE, VALUE, VALUE); -diff --git a/method.h b/method.h -index b26caaa92d66..cbcf949d2bd0 100644 ---- a/method.h -+++ b/method.h -@@ -101,6 +101,7 @@ METHOD_ENTRY_FLAGS_COPY(rb_method_entry_t *dst, const rb_method_entry_t *src) - typedef enum { - VM_METHOD_TYPE_ISEQ, /*!< Ruby method */ - VM_METHOD_TYPE_CFUNC, /*!< C method */ -+ VM_METHOD_TYPE_SORBET, - VM_METHOD_TYPE_ATTRSET, /*!< attr_writer or attr_accessor */ - VM_METHOD_TYPE_IVAR, /*!< attr_reader or attr_accessor */ - VM_METHOD_TYPE_BMETHOD, -@@ -134,6 +135,12 @@ typedef struct rb_method_cfunc_struct { - int argc; - } rb_method_cfunc_t; - -+typedef struct rb_method_sorbet_struct { -+ VALUE (*func)(ANYARGS); -+ /* no need for invoker, since there's only the (recv, argc, argv) call style */ -+ /* similarly, no need for argc */ -+} rb_method_sorbet_t; -+ - typedef struct rb_method_attr_struct { - ID id; - VALUE location; /* should be marked */ -@@ -168,6 +175,7 @@ struct rb_method_definition_struct { - union { - rb_method_iseq_t iseq; - rb_method_cfunc_t cfunc; -+ rb_method_sorbet_t sorbet; - rb_method_attr_t attr; - rb_method_alias_t alias; - rb_method_refined_t refined; -@@ -189,6 +197,7 @@ STATIC_ASSERT(sizeof_method_def, offsetof(rb_method_definition_t, body)==8); - UNDEFINED_METHOD_ENTRY_P((def)->body.refined.orig_me)) - - void rb_add_method_cfunc(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc, rb_method_visibility_t visi); -+void rb_add_method_sorbet(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc, rb_method_visibility_t visi); - void rb_add_method_iseq(VALUE klass, ID mid, const rb_iseq_t *iseq, rb_cref_t *cref, rb_method_visibility_t visi); - void rb_add_refined_method_entry(VALUE refined_class, ID mid); - void rb_add_method(VALUE klass, ID mid, rb_method_type_t type, void *option, rb_method_visibility_t visi); -diff --git a/proc.c b/proc.c -index e189c20886de..66a99ae7c517 100644 ---- a/proc.c -+++ b/proc.c -@@ -2546,6 +2546,10 @@ rb_method_entry_min_max_arity(const rb_method_entry_t *me, int *max) - case VM_METHOD_TYPE_REFINED: - *max = UNLIMITED_ARGUMENTS; - return 0; -+ case VM_METHOD_TYPE_SORBET: -+ /* TODO(froydnj): at some point, we want argument information for these. */ -+ *max = UNLIMITED_ARGUMENTS; -+ return 0; - } - rb_bug("rb_method_entry_min_max_arity: invalid method entry type (%d)", def->type); - UNREACHABLE_RETURN(Qnil); -@@ -2682,6 +2686,7 @@ method_def_iseq(const rb_method_definition_t *def) - case VM_METHOD_TYPE_OPTIMIZED: - case VM_METHOD_TYPE_MISSING: - case VM_METHOD_TYPE_REFINED: -+ case VM_METHOD_TYPE_SORBET: - break; - } - return NULL; -diff --git a/vm_eval.c b/vm_eval.c -index 76e56fac8a49..387dec4195f8 100644 ---- a/vm_eval.c -+++ b/vm_eval.c -@@ -105,6 +105,59 @@ vm_call0_cfunc(rb_execution_context_t *ec, struct rb_calling_info *calling, stru - return vm_call0_cfunc_with_frame(ec, calling, cd, argv); - } - -+static VALUE -+vm_call0_sorbet_with_frame(rb_execution_context_t* ec, struct rb_calling_info *calling, struct rb_call_data *cd, const VALUE *argv) -+{ -+ const struct rb_call_info *ci = &cd->ci; -+ const struct rb_call_cache *cc = &cd->cc; -+ VALUE val; -+ const rb_callable_method_entry_t *me = cc->me; -+ const rb_method_sorbet_t *sorbet = UNALIGNED_MEMBER_PTR(me->def, body.sorbet); -+ -+ VALUE recv = calling->recv; -+ int argc = calling->argc; -+ ID mid = ci->mid; -+ VALUE block_handler = calling->block_handler; -+ /* We are close enough to VM_METHOD_TYPE_CFUNC that we claim our frames are C function frames */ -+ int frame_flags = VM_FRAME_MAGIC_CFUNC | VM_FRAME_FLAG_CFRAME | VM_ENV_FLAG_LOCAL; -+ -+ if (calling->kw_splat) { -+ if (argc > 0 && RB_TYPE_P(argv[argc-1], T_HASH) && RHASH_EMPTY_P(argv[argc-1])) { -+ frame_flags |= VM_FRAME_FLAG_CFRAME_EMPTY_KW; -+ argc--; -+ } -+ else { -+ frame_flags |= VM_FRAME_FLAG_CFRAME_KW; -+ } -+ } -+ -+ RUBY_DTRACE_CMETHOD_ENTRY_HOOK(ec, me->owner, me->def->original_id); -+ EXEC_EVENT_HOOK(ec, RUBY_EVENT_C_CALL, recv, me->def->original_id, mid, me->owner, Qnil); -+ { -+ rb_control_frame_t *reg_cfp = ec->cfp; -+ -+ vm_push_frame(ec, 0, frame_flags, recv, -+ block_handler, (VALUE)me, -+ 0, reg_cfp->sp, 0, 0); -+ -+ /* TODO: eventually we want to pass cd in here to assist with kwargs parsing */ -+ val = (*sorbet->func)(argc, argv, recv); -+ -+ CHECK_CFP_CONSISTENCY("vm_call0_sorbet_with_frame"); -+ rb_vm_pop_frame(ec); -+ } -+ EXEC_EVENT_HOOK(ec, RUBY_EVENT_C_RETURN, recv, me->def->original_id, mid, me->owner, val); -+ RUBY_DTRACE_CMETHOD_RETURN_HOOK(ec, me->owner, me->def->original_id); -+ -+ return val; -+} -+ -+static VALUE -+vm_call0_sorbet(rb_execution_context_t *ec, struct rb_calling_info *calling, struct rb_call_data *cd, const VALUE *argv) -+{ -+ return vm_call0_sorbet_with_frame(ec, calling, cd, argv); -+} -+ - /* `ci' should point temporal value (on stack value) */ - static VALUE - vm_call0_body(rb_execution_context_t *ec, struct rb_calling_info *calling, struct rb_call_data *cd, const VALUE *argv) -@@ -139,6 +192,9 @@ vm_call0_body(rb_execution_context_t *ec, struct rb_calling_info *calling, struc - case VM_METHOD_TYPE_CFUNC: - ret = vm_call0_cfunc(ec, calling, cd, argv); - goto success; -+ case VM_METHOD_TYPE_SORBET: -+ ret = vm_call0_sorbet(ec, calling, cd, argv); -+ goto success; - case VM_METHOD_TYPE_ATTRSET: - if (calling->kw_splat && - calling->argc > 0 && -diff --git a/vm_insnhelper.c b/vm_insnhelper.c -index c0d9092a67da..77ab0930a35c 100644 ---- a/vm_insnhelper.c -+++ b/vm_insnhelper.c -@@ -2469,6 +2469,7 @@ vm_method_cfunc_entry(const rb_callable_method_entry_t *me) - METHOD_BUG(MISSING); - METHOD_BUG(REFINED); - METHOD_BUG(ALIAS); -+ METHOD_BUG(SORBET); - # undef METHOD_BUG - default: - rb_bug("wrong method type: %d", me->def->type); -@@ -2539,6 +2540,70 @@ vm_call_cfunc(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb - return vm_call_cfunc_with_frame(ec, reg_cfp, calling, cd, empty_kw_splat); - } - -+/* -- Remove empty_kw_splat In 3.0 -- */ -+static VALUE -+vm_call_sorbet_with_frame(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, struct rb_call_data *cd, int empty_kw_splat) -+{ -+ const struct rb_call_info *ci = &cd->ci; -+ const struct rb_call_cache *cc = &cd->cc; -+ VALUE val; -+ const rb_callable_method_entry_t *me = cc->me; -+ /* TODO: verify this is a VM_METHOD_TYPE_SORBET? */ -+ const rb_method_sorbet_t *sorbet = UNALIGNED_MEMBER_PTR(me->def, body.sorbet); -+ -+ VALUE recv = calling->recv; -+ VALUE block_handler = calling->block_handler; -+ /* We are close enough to VM_METHOD_TYPE_CFUNC that we claim our frames are C function frames */ -+ VALUE frame_type = VM_FRAME_MAGIC_CFUNC | VM_FRAME_FLAG_CFRAME | VM_ENV_FLAG_LOCAL; -+ int argc = calling->argc; -+ int orig_argc = argc; -+ -+ if (UNLIKELY(calling->kw_splat)) { -+ frame_type |= VM_FRAME_FLAG_CFRAME_KW; -+ } -+ else if (UNLIKELY(empty_kw_splat)) { -+ frame_type |= VM_FRAME_FLAG_CFRAME_EMPTY_KW; -+ } -+ -+ RUBY_DTRACE_CMETHOD_ENTRY_HOOK(ec, me->owner, me->def->original_id); -+ EXEC_EVENT_HOOK(ec, RUBY_EVENT_C_CALL, recv, me->def->original_id, ci->mid, me->owner, Qundef); -+ -+ vm_push_frame(ec, NULL, frame_type, recv, -+ block_handler, (VALUE)me, -+ 0, ec->cfp->sp, 0, 0); -+ -+ reg_cfp->sp -= orig_argc + 1; -+ /* TODO: eventually we want to pass cd in here to assist with kwargs parsing */ -+ val = (*sorbet->func)(argc, reg_cfp->sp + 1, recv); -+ -+ CHECK_CFP_CONSISTENCY("vm_call_sorbet"); -+ -+ rb_vm_pop_frame(ec); -+ -+ EXEC_EVENT_HOOK(ec, RUBY_EVENT_C_RETURN, recv, me->def->original_id, ci->mid, me->owner, val); -+ RUBY_DTRACE_CMETHOD_RETURN_HOOK(ec, me->owner, me->def->original_id); -+ -+ return val; -+} -+ -+static VALUE -+vm_call_sorbet(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, struct rb_call_data *cd) -+{ -+ const struct rb_call_info *ci = &cd->ci; -+ int empty_kw_splat; -+ RB_DEBUG_COUNTER_INC(ccf_cfunc); -+ -+ /* TODO: we'll want to tweak this to not munge the send args. */ -+ CALLER_SETUP_ARG(reg_cfp, calling, ci); -+ empty_kw_splat = calling->kw_splat; -+ CALLER_REMOVE_EMPTY_KW_SPLAT(reg_cfp, calling, ci); -+ if (empty_kw_splat && calling->kw_splat) { -+ empty_kw_splat = 0; -+ } -+ return vm_call_sorbet_with_frame(ec, reg_cfp, calling, cd, empty_kw_splat); -+} -+ -+ - static VALUE - vm_call_ivar(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling, struct rb_call_data *cd) - { -@@ -2924,6 +2989,10 @@ vm_call_method_each_type(rb_execution_context_t *ec, rb_control_frame_t *cfp, st - CC_SET_FASTPATH(cc, vm_call_cfunc, TRUE); - return vm_call_cfunc(ec, cfp, calling, cd); - -+ case VM_METHOD_TYPE_SORBET: -+ CC_SET_FASTPATH(cc, vm_call_sorbet, TRUE); -+ return vm_call_sorbet(ec, cfp, calling, cd); -+ - case VM_METHOD_TYPE_ATTRSET: - CALLER_SETUP_ARG(cfp, calling, ci); - if (calling->argc == 1 && calling->kw_splat && RHASH_EMPTY_P(cfp->sp[-1])) { -diff --git a/vm_method.c b/vm_method.c -index 450446878950..246de5720e4f 100644 ---- a/vm_method.c -+++ b/vm_method.c -@@ -145,6 +145,20 @@ rb_add_method_cfunc(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc, rb_me - } - } - -+void -+rb_add_method_sorbet(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc, rb_method_visibility_t visi) -+{ -+ if (argc != -1) rb_raise(rb_eArgError, "Incorrect arity for sorbet method"); -+ if (func == rb_f_notimplement) { -+ rb_define_notimplement_method_id(klass, mid, visi); -+ } -+ else { -+ rb_method_sorbet_t opt; -+ opt.func = func; -+ rb_add_method(klass, mid, VM_METHOD_TYPE_SORBET, &opt, visi); -+ } -+} -+ - static void - rb_method_definition_release(rb_method_definition_t *def, int complemented) - { -@@ -228,6 +242,12 @@ setup_method_cfunc_struct(rb_method_cfunc_t *cfunc, VALUE (*func)(), int argc) - cfunc->invoker = call_cfunc_invoker_func(argc); - } - -+static void -+setup_method_sorbet_struct(rb_method_sorbet_t *sorbet, VALUE (*func)()) -+{ -+ sorbet->func = func; -+} -+ - MJIT_FUNC_EXPORTED void - rb_method_definition_set(const rb_method_entry_t *me, rb_method_definition_t *def, void *opts) - { -@@ -261,6 +281,12 @@ rb_method_definition_set(const rb_method_entry_t *me, rb_method_definition_t *de - setup_method_cfunc_struct(UNALIGNED_MEMBER_PTR(def, body.cfunc), cfunc->func, cfunc->argc); - return; - } -+ case VM_METHOD_TYPE_SORBET: -+ { -+ rb_method_sorbet_t *sorbet = (rb_method_sorbet_t *)opts; -+ setup_method_sorbet_struct(UNALIGNED_MEMBER_PTR(def, body.sorbet), sorbet->func); -+ return; -+ } - case VM_METHOD_TYPE_ATTRSET: - case VM_METHOD_TYPE_IVAR: - { -@@ -340,6 +366,7 @@ method_definition_reset(const rb_method_entry_t *me) - case VM_METHOD_TYPE_OPTIMIZED: - case VM_METHOD_TYPE_UNDEF: - case VM_METHOD_TYPE_NOTIMPLEMENTED: -+ case VM_METHOD_TYPE_SORBET: - break; - } - } -@@ -1530,6 +1557,8 @@ rb_method_definition_eq(const rb_method_definition_t *d1, const rb_method_defini - return 1; - case VM_METHOD_TYPE_OPTIMIZED: - return d1->body.optimize_type == d2->body.optimize_type; -+ case VM_METHOD_TYPE_SORBET: -+ return d1->body.sorbet.func == d2->body.sorbet.func; - case VM_METHOD_TYPE_REFINED: - case VM_METHOD_TYPE_ALIAS: - break; -@@ -1564,6 +1593,8 @@ rb_hash_method_definition(st_index_t hash, const rb_method_definition_t *def) - return hash; - case VM_METHOD_TYPE_OPTIMIZED: - return rb_hash_uint(hash, def->body.optimize_type); -+ case VM_METHOD_TYPE_SORBET: -+ return rb_hash_uint(hash, (st_index_t)def->body.sorbet.func); - case VM_METHOD_TYPE_REFINED: - case VM_METHOD_TYPE_ALIAS: - break; /* unreachable */ - -From b413fda816838412316ecf833005a70d4d2fa65d Mon Sep 17 00:00:00 2001 -From: Nathan Froyd -Date: Thu, 28 Jan 2021 15:32:56 -0500 -Subject: [PATCH 02/30] add locals_size and stack_max information to sorbet - methods - ---- - class.c | 4 ++-- - include/ruby/intern.h | 2 +- - method.h | 6 +++++- - vm_eval.c | 2 +- - vm_insnhelper.c | 3 ++- - vm_method.c | 11 ++++++++--- - 6 files changed, 19 insertions(+), 9 deletions(-) - -diff --git a/class.c b/class.c -index 70b8deba2759..a43527bed749 100644 ---- a/class.c -+++ b/class.c -@@ -1758,9 +1758,9 @@ rb_define_singleton_method(VALUE obj, const char *name, VALUE (*func)(ANYARGS), - } - - void --rb_define_singleton_sorbet_method(VALUE obj, const char *name, VALUE(*func)(ANYARGS), int argc) -+rb_define_singleton_sorbet_method(VALUE obj, const char *name, VALUE(*func)(ANYARGS), int argc, int locals_size, int stack_max) - { -- rb_add_method_sorbet(singleton_class_of(obj), rb_intern(name), func, argc, METHOD_VISI_PUBLIC); -+ rb_add_method_sorbet(singleton_class_of(obj), rb_intern(name), func, argc, METHOD_VISI_PUBLIC, locals_size, stack_max); - } - - #ifdef rb_define_module_function -diff --git a/include/ruby/intern.h b/include/ruby/intern.h -index 0a6d503eb4f4..993ef52a8aec 100644 ---- a/include/ruby/intern.h -+++ b/include/ruby/intern.h -@@ -238,7 +238,7 @@ void rb_define_protected_method(VALUE, const char*, VALUE (*)(ANYARGS), int); - void rb_define_private_method(VALUE, const char*, VALUE (*)(ANYARGS), int); - void rb_define_singleton_method(VALUE, const char*, VALUE(*)(ANYARGS), int); - /* included so we don't expose singleton_class_of outside of class.c */ --void rb_define_singleton_sorbet_method(VALUE, const char*, VALUE(*)(ANYARGS), int); -+void rb_define_singleton_sorbet_method(VALUE, const char*, VALUE(*)(ANYARGS), int, int, int); - VALUE rb_singleton_class(VALUE); - /* compar.c */ - int rb_cmpint(VALUE, VALUE, VALUE); -diff --git a/method.h b/method.h -index cbcf949d2bd0..ff2fb29b80eb 100644 ---- a/method.h -+++ b/method.h -@@ -136,9 +136,13 @@ typedef struct rb_method_cfunc_struct { - } rb_method_cfunc_t; - - typedef struct rb_method_sorbet_struct { -+ /* cf. rb_method_cfunc_struct */ - VALUE (*func)(ANYARGS); - /* no need for invoker, since there's only the (recv, argc, argv) call style */ - /* similarly, no need for argc */ -+ -+ int locals_size; /* cf. rb_iseq_constant_body::local_table_size */ -+ int stack_max; /* cf. rb_iseq_constant_body::stack_max */ - } rb_method_sorbet_t; - - typedef struct rb_method_attr_struct { -@@ -197,7 +201,7 @@ STATIC_ASSERT(sizeof_method_def, offsetof(rb_method_definition_t, body)==8); - UNDEFINED_METHOD_ENTRY_P((def)->body.refined.orig_me)) - - void rb_add_method_cfunc(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc, rb_method_visibility_t visi); --void rb_add_method_sorbet(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc, rb_method_visibility_t visi); -+void rb_add_method_sorbet(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc, rb_method_visibility_t visi, int locals_size, int stack_max); - void rb_add_method_iseq(VALUE klass, ID mid, const rb_iseq_t *iseq, rb_cref_t *cref, rb_method_visibility_t visi); - void rb_add_refined_method_entry(VALUE refined_class, ID mid); - void rb_add_method(VALUE klass, ID mid, rb_method_type_t type, void *option, rb_method_visibility_t visi); -diff --git a/vm_eval.c b/vm_eval.c -index 387dec4195f8..031a33a49e86 100644 ---- a/vm_eval.c -+++ b/vm_eval.c -@@ -138,7 +138,7 @@ vm_call0_sorbet_with_frame(rb_execution_context_t* ec, struct rb_calling_info *c - - vm_push_frame(ec, 0, frame_flags, recv, - block_handler, (VALUE)me, -- 0, reg_cfp->sp, 0, 0); -+ 0, reg_cfp->sp, sorbet->locals_size, sorbet->stack_max); - - /* TODO: eventually we want to pass cd in here to assist with kwargs parsing */ - val = (*sorbet->func)(argc, argv, recv); -diff --git a/vm_insnhelper.c b/vm_insnhelper.c -index 77ab0930a35c..4de22d357bef 100644 ---- a/vm_insnhelper.c -+++ b/vm_insnhelper.c -@@ -2568,9 +2568,10 @@ vm_call_sorbet_with_frame(rb_execution_context_t *ec, rb_control_frame_t *reg_cf - RUBY_DTRACE_CMETHOD_ENTRY_HOOK(ec, me->owner, me->def->original_id); - EXEC_EVENT_HOOK(ec, RUBY_EVENT_C_CALL, recv, me->def->original_id, ci->mid, me->owner, Qundef); - -+ /* TODO(froydnj): should we adjust locals_size based on number of args we're passing? */ - vm_push_frame(ec, NULL, frame_type, recv, - block_handler, (VALUE)me, -- 0, ec->cfp->sp, 0, 0); -+ 0, ec->cfp->sp, sorbet->locals_size, sorbet->stack_max); - - reg_cfp->sp -= orig_argc + 1; - /* TODO: eventually we want to pass cd in here to assist with kwargs parsing */ -diff --git a/vm_method.c b/vm_method.c -index 246de5720e4f..5d5b8863cb21 100644 ---- a/vm_method.c -+++ b/vm_method.c -@@ -146,7 +146,8 @@ rb_add_method_cfunc(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc, rb_me - } - - void --rb_add_method_sorbet(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc, rb_method_visibility_t visi) -+rb_add_method_sorbet(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc, rb_method_visibility_t visi, -+ int locals_size, int stack_max) - { - if (argc != -1) rb_raise(rb_eArgError, "Incorrect arity for sorbet method"); - if (func == rb_f_notimplement) { -@@ -155,6 +156,8 @@ rb_add_method_sorbet(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc, rb_m - else { - rb_method_sorbet_t opt; - opt.func = func; -+ opt.locals_size = locals_size; -+ opt.stack_max = stack_max; - rb_add_method(klass, mid, VM_METHOD_TYPE_SORBET, &opt, visi); - } - } -@@ -243,9 +246,11 @@ setup_method_cfunc_struct(rb_method_cfunc_t *cfunc, VALUE (*func)(), int argc) - } - - static void --setup_method_sorbet_struct(rb_method_sorbet_t *sorbet, VALUE (*func)()) -+setup_method_sorbet_struct(rb_method_sorbet_t *sorbet, VALUE (*func)(), int locals_size, int stack_max) - { - sorbet->func = func; -+ sorbet->locals_size = locals_size; -+ sorbet->stack_max = stack_max; - } - - MJIT_FUNC_EXPORTED void -@@ -284,7 +289,7 @@ rb_method_definition_set(const rb_method_entry_t *me, rb_method_definition_t *de - case VM_METHOD_TYPE_SORBET: - { - rb_method_sorbet_t *sorbet = (rb_method_sorbet_t *)opts; -- setup_method_sorbet_struct(UNALIGNED_MEMBER_PTR(def, body.sorbet), sorbet->func); -+ setup_method_sorbet_struct(UNALIGNED_MEMBER_PTR(def, body.sorbet), sorbet->func, sorbet->locals_size, sorbet->stack_max); - return; - } - case VM_METHOD_TYPE_ATTRSET: - -From 343b149cd874f99f6153287b21a19c307285e22c Mon Sep 17 00:00:00 2001 -From: Nathan Froyd -Date: Fri, 29 Jan 2021 11:52:33 -0500 -Subject: [PATCH 03/30] change sorbet methods to take a precomputed iseqptr - ---- - class.c | 4 ++-- - include/ruby/intern.h | 2 +- - method.h | 7 ++++++- - vm_eval.c | 2 +- - vm_insnhelper.c | 2 +- - vm_method.c | 12 +++++++----- - 6 files changed, 18 insertions(+), 11 deletions(-) - -diff --git a/class.c b/class.c -index a43527bed749..81d5c8f403ba 100644 ---- a/class.c -+++ b/class.c -@@ -1758,9 +1758,9 @@ rb_define_singleton_method(VALUE obj, const char *name, VALUE (*func)(ANYARGS), - } - - void --rb_define_singleton_sorbet_method(VALUE obj, const char *name, VALUE(*func)(ANYARGS), int argc, int locals_size, int stack_max) -+rb_define_singleton_sorbet_method(VALUE obj, const char *name, VALUE(*func)(ANYARGS), int argc, void *iseqptr) - { -- rb_add_method_sorbet(singleton_class_of(obj), rb_intern(name), func, argc, METHOD_VISI_PUBLIC, locals_size, stack_max); -+ rb_add_method_sorbet(singleton_class_of(obj), rb_intern(name), func, argc, METHOD_VISI_PUBLIC, iseqptr); - } - - #ifdef rb_define_module_function -diff --git a/include/ruby/intern.h b/include/ruby/intern.h -index 993ef52a8aec..bf346d28f11a 100644 ---- a/include/ruby/intern.h -+++ b/include/ruby/intern.h -@@ -238,7 +238,7 @@ void rb_define_protected_method(VALUE, const char*, VALUE (*)(ANYARGS), int); - void rb_define_private_method(VALUE, const char*, VALUE (*)(ANYARGS), int); - void rb_define_singleton_method(VALUE, const char*, VALUE(*)(ANYARGS), int); - /* included so we don't expose singleton_class_of outside of class.c */ --void rb_define_singleton_sorbet_method(VALUE, const char*, VALUE(*)(ANYARGS), int, int, int); -+void rb_define_singleton_sorbet_method(VALUE, const char*, VALUE(*)(ANYARGS), int, void *); - VALUE rb_singleton_class(VALUE); - /* compar.c */ - int rb_cmpint(VALUE, VALUE, VALUE); -diff --git a/method.h b/method.h -index ff2fb29b80eb..bf3acba1b7da 100644 ---- a/method.h -+++ b/method.h -@@ -141,6 +141,11 @@ typedef struct rb_method_sorbet_struct { - /* no need for invoker, since there's only the (recv, argc, argv) call style */ - /* similarly, no need for argc */ - -+ rb_iseq_t *iseqptr; -+ /* We load these out of iseqptr to save indirections when they are needed. -+ * The size of the struct is therefore no bigger than rb_method_cfunc_struct -+ * (on 64-bit platforms) due to structure padding and how things pack. -+ */ - int locals_size; /* cf. rb_iseq_constant_body::local_table_size */ - int stack_max; /* cf. rb_iseq_constant_body::stack_max */ - } rb_method_sorbet_t; -@@ -201,7 +206,7 @@ STATIC_ASSERT(sizeof_method_def, offsetof(rb_method_definition_t, body)==8); - UNDEFINED_METHOD_ENTRY_P((def)->body.refined.orig_me)) - - void rb_add_method_cfunc(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc, rb_method_visibility_t visi); --void rb_add_method_sorbet(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc, rb_method_visibility_t visi, int locals_size, int stack_max); -+void rb_add_method_sorbet(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc, rb_method_visibility_t visi, void *iseqptr); - void rb_add_method_iseq(VALUE klass, ID mid, const rb_iseq_t *iseq, rb_cref_t *cref, rb_method_visibility_t visi); - void rb_add_refined_method_entry(VALUE refined_class, ID mid); - void rb_add_method(VALUE klass, ID mid, rb_method_type_t type, void *option, rb_method_visibility_t visi); -diff --git a/vm_eval.c b/vm_eval.c -index 031a33a49e86..fd385a62538e 100644 ---- a/vm_eval.c -+++ b/vm_eval.c -@@ -136,7 +136,7 @@ vm_call0_sorbet_with_frame(rb_execution_context_t* ec, struct rb_calling_info *c - { - rb_control_frame_t *reg_cfp = ec->cfp; - -- vm_push_frame(ec, 0, frame_flags, recv, -+ vm_push_frame(ec, sorbet->iseqptr, frame_flags, recv, - block_handler, (VALUE)me, - 0, reg_cfp->sp, sorbet->locals_size, sorbet->stack_max); - -diff --git a/vm_insnhelper.c b/vm_insnhelper.c -index 4de22d357bef..efb978fd5986 100644 ---- a/vm_insnhelper.c -+++ b/vm_insnhelper.c -@@ -2569,7 +2569,7 @@ vm_call_sorbet_with_frame(rb_execution_context_t *ec, rb_control_frame_t *reg_cf - EXEC_EVENT_HOOK(ec, RUBY_EVENT_C_CALL, recv, me->def->original_id, ci->mid, me->owner, Qundef); - - /* TODO(froydnj): should we adjust locals_size based on number of args we're passing? */ -- vm_push_frame(ec, NULL, frame_type, recv, -+ vm_push_frame(ec, sorbet->iseqptr, frame_type, recv, - block_handler, (VALUE)me, - 0, ec->cfp->sp, sorbet->locals_size, sorbet->stack_max); - -diff --git a/vm_method.c b/vm_method.c -index 5d5b8863cb21..e805326656a8 100644 ---- a/vm_method.c -+++ b/vm_method.c -@@ -147,7 +147,7 @@ rb_add_method_cfunc(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc, rb_me - - void - rb_add_method_sorbet(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc, rb_method_visibility_t visi, -- int locals_size, int stack_max) -+ void *iseqptr) - { - if (argc != -1) rb_raise(rb_eArgError, "Incorrect arity for sorbet method"); - if (func == rb_f_notimplement) { -@@ -156,8 +156,9 @@ rb_add_method_sorbet(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc, rb_m - else { - rb_method_sorbet_t opt; - opt.func = func; -- opt.locals_size = locals_size; -- opt.stack_max = stack_max; -+ opt.iseqptr = (rb_iseq_t *)iseqptr; -+ opt.locals_size = opt.iseqptr->body->local_table_size; -+ opt.stack_max = opt.iseqptr->body->stack_max; - rb_add_method(klass, mid, VM_METHOD_TYPE_SORBET, &opt, visi); - } - } -@@ -246,9 +247,10 @@ setup_method_cfunc_struct(rb_method_cfunc_t *cfunc, VALUE (*func)(), int argc) - } - - static void --setup_method_sorbet_struct(rb_method_sorbet_t *sorbet, VALUE (*func)(), int locals_size, int stack_max) -+setup_method_sorbet_struct(rb_method_sorbet_t *sorbet, VALUE (*func)(), rb_iseq_t *iseqptr, int locals_size, int stack_max) - { - sorbet->func = func; -+ sorbet->iseqptr = iseqptr; - sorbet->locals_size = locals_size; - sorbet->stack_max = stack_max; - } -@@ -289,7 +291,7 @@ rb_method_definition_set(const rb_method_entry_t *me, rb_method_definition_t *de - case VM_METHOD_TYPE_SORBET: - { - rb_method_sorbet_t *sorbet = (rb_method_sorbet_t *)opts; -- setup_method_sorbet_struct(UNALIGNED_MEMBER_PTR(def, body.sorbet), sorbet->func, sorbet->locals_size, sorbet->stack_max); -+ setup_method_sorbet_struct(UNALIGNED_MEMBER_PTR(def, body.sorbet), sorbet->func, sorbet->iseqptr, sorbet->locals_size, sorbet->stack_max); - return; - } - case VM_METHOD_TYPE_ATTRSET: - -From 1f7a077e9480a6fef9c7ac21e963fb9b70daee1c Mon Sep 17 00:00:00 2001 -From: Nathan Froyd -Date: Tue, 6 Apr 2021 14:14:10 -0400 -Subject: [PATCH 04/30] remove event hooks from sorbet methods - ---- - vm_insnhelper.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/vm_insnhelper.c b/vm_insnhelper.c -index efb978fd5986..696fc289a833 100644 ---- a/vm_insnhelper.c -+++ b/vm_insnhelper.c -@@ -2566,7 +2566,7 @@ vm_call_sorbet_with_frame(rb_execution_context_t *ec, rb_control_frame_t *reg_cf - } - - RUBY_DTRACE_CMETHOD_ENTRY_HOOK(ec, me->owner, me->def->original_id); -- EXEC_EVENT_HOOK(ec, RUBY_EVENT_C_CALL, recv, me->def->original_id, ci->mid, me->owner, Qundef); -+ /*EXEC_EVENT_HOOK(ec, RUBY_EVENT_C_CALL, recv, me->def->original_id, ci->mid, me->owner, Qundef);*/ - - /* TODO(froydnj): should we adjust locals_size based on number of args we're passing? */ - vm_push_frame(ec, sorbet->iseqptr, frame_type, recv, -@@ -2581,7 +2581,7 @@ vm_call_sorbet_with_frame(rb_execution_context_t *ec, rb_control_frame_t *reg_cf - - rb_vm_pop_frame(ec); - -- EXEC_EVENT_HOOK(ec, RUBY_EVENT_C_RETURN, recv, me->def->original_id, ci->mid, me->owner, val); -+ /*EXEC_EVENT_HOOK(ec, RUBY_EVENT_C_RETURN, recv, me->def->original_id, ci->mid, me->owner, val);*/ - RUBY_DTRACE_CMETHOD_RETURN_HOOK(ec, me->owner, me->def->original_id); - - return val; - -From e2cde7a9545d5d2c08319f305c4832da9ead9694 Mon Sep 17 00:00:00 2001 -From: Nathan Froyd -Date: Wed, 28 Apr 2021 14:27:15 -0400 -Subject: [PATCH 05/30] restruct sorbet calling to enable fast paths for - non-kwargs, non-splat calls - ---- - vm_insnhelper.c | 279 +++++++++++++++++++++++++++++++++++++++++++++--- - 1 file changed, 262 insertions(+), 17 deletions(-) - -diff --git a/vm_insnhelper.c b/vm_insnhelper.c -index 696fc289a833..334227ff41cc 100644 ---- a/vm_insnhelper.c -+++ b/vm_insnhelper.c -@@ -1894,6 +1894,7 @@ static VALUE vm_call_method_each_type(rb_execution_context_t *ec, rb_control_fra - static inline VALUE vm_call_method(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling, struct rb_call_data *cd); - - static vm_call_handler vm_call_iseq_setup_func(const struct rb_call_info *ci, const int param_size, const int local_size); -+static vm_call_handler vm_call_sorbet_fast_func(const struct rb_call_info *ci, const int param_size, const int local_size); - - static VALUE - vm_call_iseq_setup_tailcall_0start(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling, struct rb_call_data *cd) -@@ -2542,12 +2543,9 @@ vm_call_cfunc(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb - - /* -- Remove empty_kw_splat In 3.0 -- */ - static VALUE --vm_call_sorbet_with_frame(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, struct rb_call_data *cd, int empty_kw_splat) -+vm_call_sorbet_with_frame_normal(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, const rb_callable_method_entry_t *me, int check_kw_splat, int empty_kw_splat, int param_size, int local_size) - { -- const struct rb_call_info *ci = &cd->ci; -- const struct rb_call_cache *cc = &cd->cc; - VALUE val; -- const rb_callable_method_entry_t *me = cc->me; - /* TODO: verify this is a VM_METHOD_TYPE_SORBET? */ - const rb_method_sorbet_t *sorbet = UNALIGNED_MEMBER_PTR(me->def, body.sorbet); - -@@ -2555,25 +2553,24 @@ vm_call_sorbet_with_frame(rb_execution_context_t *ec, rb_control_frame_t *reg_cf - VALUE block_handler = calling->block_handler; - /* We are close enough to VM_METHOD_TYPE_CFUNC that we claim our frames are C function frames */ - VALUE frame_type = VM_FRAME_MAGIC_CFUNC | VM_FRAME_FLAG_CFRAME | VM_ENV_FLAG_LOCAL; -- int argc = calling->argc; -- int orig_argc = argc; -+ int argc = param_size; - -- if (UNLIKELY(calling->kw_splat)) { -- frame_type |= VM_FRAME_FLAG_CFRAME_KW; -- } -- else if (UNLIKELY(empty_kw_splat)) { -- frame_type |= VM_FRAME_FLAG_CFRAME_EMPTY_KW; -+ if (check_kw_splat) { -+ if (UNLIKELY(calling->kw_splat)) { -+ frame_type |= VM_FRAME_FLAG_CFRAME_KW; -+ } -+ else if (UNLIKELY(empty_kw_splat)) { -+ frame_type |= VM_FRAME_FLAG_CFRAME_EMPTY_KW; -+ } - } - - RUBY_DTRACE_CMETHOD_ENTRY_HOOK(ec, me->owner, me->def->original_id); -- /*EXEC_EVENT_HOOK(ec, RUBY_EVENT_C_CALL, recv, me->def->original_id, ci->mid, me->owner, Qundef);*/ - -- /* TODO(froydnj): should we adjust locals_size based on number of args we're passing? */ - vm_push_frame(ec, sorbet->iseqptr, frame_type, recv, -- block_handler, (VALUE)me, -- 0, ec->cfp->sp, sorbet->locals_size, sorbet->stack_max); -+ block_handler, (VALUE)me, -+ 0, ec->cfp->sp, local_size, sorbet->stack_max); - -- reg_cfp->sp -= orig_argc + 1; -+ reg_cfp->sp -= argc + 1; - /* TODO: eventually we want to pass cd in here to assist with kwargs parsing */ - val = (*sorbet->func)(argc, reg_cfp->sp + 1, recv); - -@@ -2581,12 +2578,260 @@ vm_call_sorbet_with_frame(rb_execution_context_t *ec, rb_control_frame_t *reg_cf - - rb_vm_pop_frame(ec); - -- /*EXEC_EVENT_HOOK(ec, RUBY_EVENT_C_RETURN, recv, me->def->original_id, ci->mid, me->owner, val);*/ - RUBY_DTRACE_CMETHOD_RETURN_HOOK(ec, me->owner, me->def->original_id); - - return val; - } - -+/* These would normally be generated by tool/mk_call_iseq_optimized.rb. -+ * -+ * They live here because of a bootstrapping problem: the Ruby tarball we apply -+ * patches to has already had various auto-generated files generated so that building -+ * from the tarball doesn't require a pre-existing Ruby. However, if we patch the -+ * aforementioned file, then the generating script looks newer than the generated -+ * file, so `make` decides to regenerate the latter. Except if you don't have a Ruby -+ * available, you can't. Ergo, we have this cut-and-pasted code. -+ */ -+ -+static VALUE -+vm_call_sorbet_fast_0params_0locals(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling, struct rb_call_data *cd) -+{ -+ const int check_kw_splat = 0; -+ const int empty_kw_splat = 0; -+ return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, 0, 0); -+} -+ -+static VALUE -+vm_call_sorbet_fast_0params_1locals(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling, struct rb_call_data *cd) -+{ -+ const int check_kw_splat = 0; -+ const int empty_kw_splat = 0; -+ return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, 0, 1); -+} -+ -+static VALUE -+vm_call_sorbet_fast_0params_2locals(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling, struct rb_call_data *cd) -+{ -+ const int check_kw_splat = 0; -+ const int empty_kw_splat = 0; -+ return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, 0, 2); -+} -+ -+static VALUE -+vm_call_sorbet_fast_0params_3locals(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling, struct rb_call_data *cd) -+{ -+ const int check_kw_splat = 0; -+ const int empty_kw_splat = 0; -+ return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, 0, 3); -+} -+ -+static VALUE -+vm_call_sorbet_fast_0params_4locals(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling, struct rb_call_data *cd) -+{ -+ const int check_kw_splat = 0; -+ const int empty_kw_splat = 0; -+ return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, 0, 4); -+} -+ -+static VALUE -+vm_call_sorbet_fast_0params_5locals(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling, struct rb_call_data *cd) -+{ -+ const int check_kw_splat = 0; -+ const int empty_kw_splat = 0; -+ return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, 0, 5); -+} -+ -+static VALUE -+vm_call_sorbet_fast_1params_0locals(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling, struct rb_call_data *cd) -+{ -+ const int check_kw_splat = 0; -+ const int empty_kw_splat = 0; -+ return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, 1, 0); -+} -+ -+static VALUE -+vm_call_sorbet_fast_1params_1locals(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling, struct rb_call_data *cd) -+{ -+ const int check_kw_splat = 0; -+ const int empty_kw_splat = 0; -+ return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, 1, 1); -+} -+ -+static VALUE -+vm_call_sorbet_fast_1params_2locals(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling, struct rb_call_data *cd) -+{ -+ const int check_kw_splat = 0; -+ const int empty_kw_splat = 0; -+ return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, 1, 2); -+} -+ -+static VALUE -+vm_call_sorbet_fast_1params_3locals(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling, struct rb_call_data *cd) -+{ -+ const int check_kw_splat = 0; -+ const int empty_kw_splat = 0; -+ return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, 1, 3); -+} -+ -+static VALUE -+vm_call_sorbet_fast_1params_4locals(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling, struct rb_call_data *cd) -+{ -+ const int check_kw_splat = 0; -+ const int empty_kw_splat = 0; -+ return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, 1, 4); -+} -+ -+static VALUE -+vm_call_sorbet_fast_1params_5locals(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling, struct rb_call_data *cd) -+{ -+ const int check_kw_splat = 0; -+ const int empty_kw_splat = 0; -+ return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, 1, 5); -+} -+ -+static VALUE -+vm_call_sorbet_fast_2params_0locals(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling, struct rb_call_data *cd) -+{ -+ const int check_kw_splat = 0; -+ const int empty_kw_splat = 0; -+ return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, 2, 0); -+} -+ -+static VALUE -+vm_call_sorbet_fast_2params_1locals(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling, struct rb_call_data *cd) -+{ -+ const int check_kw_splat = 0; -+ const int empty_kw_splat = 0; -+ return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, 2, 1); -+} -+ -+static VALUE -+vm_call_sorbet_fast_2params_2locals(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling, struct rb_call_data *cd) -+{ -+ const int check_kw_splat = 0; -+ const int empty_kw_splat = 0; -+ return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, 2, 2); -+} -+ -+static VALUE -+vm_call_sorbet_fast_2params_3locals(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling, struct rb_call_data *cd) -+{ -+ const int check_kw_splat = 0; -+ const int empty_kw_splat = 0; -+ return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, 2, 3); -+} -+ -+static VALUE -+vm_call_sorbet_fast_2params_4locals(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling, struct rb_call_data *cd) -+{ -+ const int check_kw_splat = 0; -+ const int empty_kw_splat = 0; -+ return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, 2, 4); -+} -+ -+static VALUE -+vm_call_sorbet_fast_2params_5locals(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling, struct rb_call_data *cd) -+{ -+ const int check_kw_splat = 0; -+ const int empty_kw_splat = 0; -+ return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, 2, 5); -+} -+ -+static VALUE -+vm_call_sorbet_fast_3params_0locals(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling, struct rb_call_data *cd) -+{ -+ const int check_kw_splat = 0; -+ const int empty_kw_splat = 0; -+ return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, 3, 0); -+} -+ -+static VALUE -+vm_call_sorbet_fast_3params_1locals(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling, struct rb_call_data *cd) -+{ -+ const int check_kw_splat = 0; -+ const int empty_kw_splat = 0; -+ return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, 3, 1); -+} -+ -+static VALUE -+vm_call_sorbet_fast_3params_2locals(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling, struct rb_call_data *cd) -+{ -+ const int check_kw_splat = 0; -+ const int empty_kw_splat = 0; -+ return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, 3, 2); -+} -+ -+static VALUE -+vm_call_sorbet_fast_3params_3locals(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling, struct rb_call_data *cd) -+{ -+ const int check_kw_splat = 0; -+ const int empty_kw_splat = 0; -+ return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, 3, 3); -+} -+ -+static VALUE -+vm_call_sorbet_fast_3params_4locals(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling, struct rb_call_data *cd) -+{ -+ const int check_kw_splat = 0; -+ const int empty_kw_splat = 0; -+ return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, 3, 4); -+} -+ -+static VALUE -+vm_call_sorbet_fast_3params_5locals(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling, struct rb_call_data *cd) -+{ -+ const int check_kw_splat = 0; -+ const int empty_kw_splat = 0; -+ return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, 3, 5); -+} -+ -+static const vm_call_handler vm_call_sorbet_handlers[][6] = { -+{vm_call_sorbet_fast_0params_0locals, -+ vm_call_sorbet_fast_0params_1locals, -+ vm_call_sorbet_fast_0params_2locals, -+ vm_call_sorbet_fast_0params_3locals, -+ vm_call_sorbet_fast_0params_4locals, -+ vm_call_sorbet_fast_0params_5locals}, -+{vm_call_sorbet_fast_1params_0locals, -+ vm_call_sorbet_fast_1params_1locals, -+ vm_call_sorbet_fast_1params_2locals, -+ vm_call_sorbet_fast_1params_3locals, -+ vm_call_sorbet_fast_1params_4locals, -+ vm_call_sorbet_fast_1params_5locals}, -+{vm_call_sorbet_fast_2params_0locals, -+ vm_call_sorbet_fast_2params_1locals, -+ vm_call_sorbet_fast_2params_2locals, -+ vm_call_sorbet_fast_2params_3locals, -+ vm_call_sorbet_fast_2params_4locals, -+ vm_call_sorbet_fast_2params_5locals}, -+{vm_call_sorbet_fast_3params_0locals, -+ vm_call_sorbet_fast_3params_1locals, -+ vm_call_sorbet_fast_3params_2locals, -+ vm_call_sorbet_fast_3params_3locals, -+ vm_call_sorbet_fast_3params_4locals, -+ vm_call_sorbet_fast_3params_5locals} -+}; -+ -+static inline vm_call_handler -+vm_call_sorbet_fast_func(const struct rb_call_info *ci, const int param_size, const int local_size) -+{ -+ /* Don't have to handle VM_CALL_TAILCALL. */ -+ if (param_size <= 3 && local_size <= 5) { -+ return vm_call_sorbet_handlers[param_size][local_size]; -+ } -+ return &vm_call_sorbet; -+} -+ -+static VALUE -+vm_call_sorbet_with_frame(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, struct rb_call_data *cd, int empty_kw_splat) -+{ -+ const int check_kw_splat = 1; -+ const rb_callable_method_entry_t *me = cd->cc.me; -+ /* TODO: verify this is a VM_METHOD_TYPE_SORBET? */ -+ const rb_method_sorbet_t *sorbet = UNALIGNED_MEMBER_PTR(me->def, body.sorbet); -+ return vm_call_sorbet_with_frame_normal(ec, reg_cfp, calling, me, check_kw_splat, empty_kw_splat, calling->argc, sorbet->locals_size); -+} -+ - static VALUE - vm_call_sorbet(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, struct rb_call_data *cd) - { - -From e69dd7e2e2ad78137b0b727fd01ad45b68ec8953 Mon Sep 17 00:00:00 2001 -From: Nathan Froyd -Date: Thu, 8 Apr 2021 10:41:08 -0400 -Subject: [PATCH 06/30] add parameter information slot to sorbet methods - ---- - class.c | 4 +- - include/ruby/intern.h | 2 +- - method.h | 73 ++++++++++++++++++++--- - proc.c | 132 +++++++++++++++++++++++++++++++++++++++++- - vm_eval.c | 2 +- - vm_insnhelper.c | 6 +- - vm_method.c | 13 ++--- - 7 files changed, 207 insertions(+), 25 deletions(-) - -diff --git a/class.c b/class.c -index 81d5c8f403ba..bc6fd864a15e 100644 ---- a/class.c -+++ b/class.c -@@ -1758,9 +1758,9 @@ rb_define_singleton_method(VALUE obj, const char *name, VALUE (*func)(ANYARGS), - } - - void --rb_define_singleton_sorbet_method(VALUE obj, const char *name, VALUE(*func)(ANYARGS), int argc, void *iseqptr) -+rb_define_singleton_sorbet_method(VALUE obj, const char *name, VALUE(*func)(ANYARGS), const void *param, int argc, void *iseqptr) - { -- rb_add_method_sorbet(singleton_class_of(obj), rb_intern(name), func, argc, METHOD_VISI_PUBLIC, iseqptr); -+ rb_add_method_sorbet(singleton_class_of(obj), rb_intern(name), func, (const rb_sorbet_param_t *)param, argc, METHOD_VISI_PUBLIC, iseqptr); - } - - #ifdef rb_define_module_function -diff --git a/include/ruby/intern.h b/include/ruby/intern.h -index bf346d28f11a..fc855290e45f 100644 ---- a/include/ruby/intern.h -+++ b/include/ruby/intern.h -@@ -238,7 +238,7 @@ void rb_define_protected_method(VALUE, const char*, VALUE (*)(ANYARGS), int); - void rb_define_private_method(VALUE, const char*, VALUE (*)(ANYARGS), int); - void rb_define_singleton_method(VALUE, const char*, VALUE(*)(ANYARGS), int); - /* included so we don't expose singleton_class_of outside of class.c */ --void rb_define_singleton_sorbet_method(VALUE, const char*, VALUE(*)(ANYARGS), int, void *); -+void rb_define_singleton_sorbet_method(VALUE, const char*, VALUE(*)(ANYARGS), const void *, int, void *); - VALUE rb_singleton_class(VALUE); - /* compar.c */ - int rb_cmpint(VALUE, VALUE, VALUE); -diff --git a/method.h b/method.h -index bf3acba1b7da..1890ee8253a5 100644 ---- a/method.h -+++ b/method.h -@@ -135,19 +135,78 @@ typedef struct rb_method_cfunc_struct { - int argc; - } rb_method_cfunc_t; - -+typedef struct rb_sorbet_param_struct { -+ /** -+ * parameter information -+ * -+ * def m(a1, a2, ..., aM, # mandatory -+ * b1=(...), b2=(...), ..., bN=(...), # optional -+ * *c, # rest -+ * d1, d2, ..., dO, # post -+ * e1:(...), e2:(...), ..., eK:(...), # keyword -+ * **f, # keyword_rest -+ * &g) # block -+ * => -+ * -+ * lead_num = M -+ * opt_num = N -+ * rest_start = M+N -+ * post_start = M+N+(*1) -+ * post_num = O -+ * keyword_num = K -+ * block_start = M+N+(*1)+O+K -+ * keyword_bits = M+N+(*1)+O+K+(&1) -+ * size = M+N+O+(*1)+K+(&1)+(**1) // parameter size. -+ */ -+ -+ struct { -+ unsigned int has_lead : 1; -+ unsigned int has_opt : 1; -+ unsigned int has_rest : 1; -+ unsigned int has_post : 1; -+ unsigned int has_kw : 1; -+ unsigned int has_kwrest : 1; -+ unsigned int has_block : 1; -+ -+ unsigned int ambiguous_param0 : 1; /* {|a|} */ -+ unsigned int accepts_no_kwarg : 1; -+ unsigned int ruby2_keywords: 1; -+ } flags; -+ -+ unsigned int size; -+ -+ int lead_num; -+ int opt_num; -+ int rest_start; -+ int post_start; -+ int post_num; -+ int block_start; -+ -+ /* M + N entries. This is similar to rb_iseq_constant_body.local_table, but -+ * Sorbet optimizes that to only include the variables that escape, so it is -+ * not suited to describing parameter information for functions. -+ */ -+ const ID *pos_table; -+ -+ /* Similar to rb_iseq_param_keyword, but inlined into the parent structure -+ * so we don't need a separate allocation. We also don't need to track -+ * information about default values here. -+ */ -+ int kw_num; -+ int kw_required_num; -+ int kw_bits_start; -+ int kw_rest_start; -+ const ID *kw_table; -+} rb_sorbet_param_t; -+ - typedef struct rb_method_sorbet_struct { - /* cf. rb_method_cfunc_struct */ - VALUE (*func)(ANYARGS); - /* no need for invoker, since there's only the (recv, argc, argv) call style */ - /* similarly, no need for argc */ - -+ const rb_sorbet_param_t *param; /* cf. rb_iseq_constant_body.param */ - rb_iseq_t *iseqptr; -- /* We load these out of iseqptr to save indirections when they are needed. -- * The size of the struct is therefore no bigger than rb_method_cfunc_struct -- * (on 64-bit platforms) due to structure padding and how things pack. -- */ -- int locals_size; /* cf. rb_iseq_constant_body::local_table_size */ -- int stack_max; /* cf. rb_iseq_constant_body::stack_max */ - } rb_method_sorbet_t; - - typedef struct rb_method_attr_struct { -@@ -206,7 +265,7 @@ STATIC_ASSERT(sizeof_method_def, offsetof(rb_method_definition_t, body)==8); - UNDEFINED_METHOD_ENTRY_P((def)->body.refined.orig_me)) - - void rb_add_method_cfunc(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc, rb_method_visibility_t visi); --void rb_add_method_sorbet(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc, rb_method_visibility_t visi, void *iseqptr); -+void rb_add_method_sorbet(VALUE klass, ID mid, VALUE (*func)(ANYARGS), const rb_sorbet_param_t *param, int argc, rb_method_visibility_t visi, void *iseqptr); - void rb_add_method_iseq(VALUE klass, ID mid, const rb_iseq_t *iseq, rb_cref_t *cref, rb_method_visibility_t visi); - void rb_add_refined_method_entry(VALUE refined_class, ID mid); - void rb_add_method(VALUE klass, ID mid, rb_method_type_t type, void *option, rb_method_visibility_t visi); -diff --git a/proc.c b/proc.c -index 66a99ae7c517..87c16530c55d 100644 ---- a/proc.c -+++ b/proc.c -@@ -1067,6 +1067,22 @@ rb_iseq_min_max_arity(const rb_iseq_t *iseq, int *max) - return iseq->body->param.lead_num + iseq->body->param.post_num + (iseq->body->param.flags.has_kw && iseq->body->param.keyword->required_num > 0); - } - -+static inline int -+rb_sorbet_min_max_arity(const rb_sorbet_param_t *param, int *max) -+{ -+ /* TODO(froydnj): remove when the compiler fills all this information in */ -+ if (param == NULL) { -+ *max = UNLIMITED_ARGUMENTS; -+ return 0; -+ } -+ -+ *max = param->flags.has_rest == FALSE ? -+ param->lead_num + param->opt_num + param->post_num + -+ (param->flags.has_kw == TRUE || param->flags.has_kwrest == TRUE) -+ : UNLIMITED_ARGUMENTS; -+ return param->lead_num + param->post_num + (param->flags.has_kw && param->kw_required_num > 0); -+} -+ - static int - rb_vm_block_min_max_arity(const struct rb_block *block, int *max) - { -@@ -2547,9 +2563,7 @@ rb_method_entry_min_max_arity(const rb_method_entry_t *me, int *max) - *max = UNLIMITED_ARGUMENTS; - return 0; - case VM_METHOD_TYPE_SORBET: -- /* TODO(froydnj): at some point, we want argument information for these. */ -- *max = UNLIMITED_ARGUMENTS; -- return 0; -+ return rb_sorbet_min_max_arity(def->body.sorbet.param, max); - } - rb_bug("rb_method_entry_min_max_arity: invalid method entry type (%d)", def->type); - UNREACHABLE_RETURN(Qnil); -@@ -2747,6 +2761,114 @@ rb_method_location(VALUE method) - return method_def_location(rb_method_def(method)); - } - -+static const rb_sorbet_param_t * -+rb_method_sorbet_param(VALUE method) -+{ -+ const rb_method_definition_t *def = rb_method_def(method); -+ if (def->type != VM_METHOD_TYPE_SORBET) { -+ return NULL; -+ } -+ return def->body.sorbet.param; -+} -+ -+static VALUE -+rb_sorbet_parameters(const rb_sorbet_param_t *param) -+{ -+ /* cf. rb_iseq_parameters */ -+ int i, r, endopt; -+ VALUE a, args = rb_ary_new2(param->size); -+ ID req, opt, rest, block, nokey, key, keyreq, keyrest; -+#define PARAM_TYPE(type) rb_ary_push(a = rb_ary_new2(2), ID2SYM(type)) -+#define PARAM_ID(i) param->pos_table[(i)] -+#define PARAM(i, type) ( \ -+ PARAM_TYPE(type), \ -+ rb_id2str(PARAM_ID(i)) ? \ -+ rb_ary_push(a, ID2SYM(PARAM_ID(i))) : \ -+ a) -+ -+ /* TODO(froydnj): do we need to care about the is_proc distinction that -+ * rb_iseq_parameters makes? -+ */ -+ CONST_ID(req, "req"); -+ CONST_ID(opt, "opt"); -+ -+ for (i = 0; i < param->lead_num; ++i) { -+ rb_ary_push(args, PARAM(i, req)); -+ } -+ -+ endopt = param->lead_num + param->opt_num; -+ for (; i < endopt; ++i) { -+ PARAM_TYPE(opt); -+ if (rb_id2str(PARAM_ID(i))) { -+ rb_ary_push(a, ID2SYM(PARAM_ID(i))); -+ } -+ rb_ary_push(args, a); -+ } -+ -+ if (param->flags.has_rest) { -+ CONST_ID(rest, "rest"); -+ PARAM_TYPE(rest); -+ const ID *id = ¶m->pos_table[param->rest_start]; -+ if (rb_id2str(*id)) { -+ rb_ary_push(a, ID2SYM(*id)); -+ } -+ rb_ary_push(args, a); -+ } -+ -+ r = param->post_start + param->post_num; -+ for (i = param->post_start; i < r; ++i) { -+ rb_ary_push(args, PARAM(i, req)); -+ } -+ if (param->flags.accepts_no_kwarg) { -+ CONST_ID(nokey, "nokey"); -+ PARAM_TYPE(nokey); -+ rb_ary_push(args, a); -+ } -+ if (param->flags.has_kw) { -+ i = 0; -+ if (param->kw_required_num > 0) { -+ CONST_ID(keyreq, "keyreq"); -+ for (; i < param->kw_required_num; ++i) { -+ PARAM_TYPE(keyreq); -+ const ID *id = ¶m->kw_table[i]; -+ if (rb_id2str(*id)) { -+ rb_ary_push(a, ID2SYM(*id)); -+ } -+ rb_ary_push(args, a); -+ } -+ } -+ CONST_ID(key, "key"); -+ for (; i < param->kw_num; ++i) { -+ PARAM_TYPE(key); -+ const ID *id = ¶m->kw_table[i]; -+ if (rb_id2str(*id)) { -+ rb_ary_push(a, ID2SYM(*id)); -+ } -+ rb_ary_push(args, a); -+ } -+ } -+ if (param->flags.has_kwrest) { -+ CONST_ID(keyrest, "keyrest"); -+ PARAM_TYPE(keyrest); -+ const ID *id = ¶m->kw_table[param->kw_num]; -+ if (rb_id2str(*id)) { -+ rb_ary_push(a, ID2SYM(*id)); -+ } -+ rb_ary_push(args, a); -+ } -+ if (param->flags.has_block) { -+ CONST_ID(block, "block"); -+ PARAM_TYPE(block); -+ const ID *id = ¶m->pos_table[param->block_start]; -+ if (rb_id2str(*id)) { -+ rb_ary_push(a, ID2SYM(*id)); -+ } -+ rb_ary_push(args, a); -+ } -+ -+ return args; -+} -+ - /* - * call-seq: - * meth.parameters -> array -@@ -2771,6 +2893,10 @@ rb_method_parameters(VALUE method) - { - const rb_iseq_t *iseq = rb_method_iseq(method); - if (!iseq) { -+ const rb_sorbet_param_t *param = rb_method_sorbet_param(method); -+ if (param) { -+ return rb_sorbet_parameters(param); -+ } - return rb_unnamed_parameters(method_arity(method)); - } - return rb_iseq_parameters(iseq, 0); -diff --git a/vm_eval.c b/vm_eval.c -index fd385a62538e..72a77524ede5 100644 ---- a/vm_eval.c -+++ b/vm_eval.c -@@ -138,7 +138,7 @@ vm_call0_sorbet_with_frame(rb_execution_context_t* ec, struct rb_calling_info *c - - vm_push_frame(ec, sorbet->iseqptr, frame_flags, recv, - block_handler, (VALUE)me, -- 0, reg_cfp->sp, sorbet->locals_size, sorbet->stack_max); -+ 0, reg_cfp->sp, sorbet->iseqptr->body->local_table_size, sorbet->iseqptr->body->stack_max); - - /* TODO: eventually we want to pass cd in here to assist with kwargs parsing */ - val = (*sorbet->func)(argc, argv, recv); -diff --git a/vm_insnhelper.c b/vm_insnhelper.c -index 334227ff41cc..15fb2764bd66 100644 ---- a/vm_insnhelper.c -+++ b/vm_insnhelper.c -@@ -2542,7 +2542,7 @@ vm_call_cfunc(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb - } - - /* -- Remove empty_kw_splat In 3.0 -- */ --static VALUE -+static inline VALUE - vm_call_sorbet_with_frame_normal(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, const rb_callable_method_entry_t *me, int check_kw_splat, int empty_kw_splat, int param_size, int local_size) - { - VALUE val; -@@ -2568,7 +2568,7 @@ vm_call_sorbet_with_frame_normal(rb_execution_context_t *ec, rb_control_frame_t - - vm_push_frame(ec, sorbet->iseqptr, frame_type, recv, - block_handler, (VALUE)me, -- 0, ec->cfp->sp, local_size, sorbet->stack_max); -+ 0, ec->cfp->sp, local_size, sorbet->iseqptr->body->stack_max); - - reg_cfp->sp -= argc + 1; - /* TODO: eventually we want to pass cd in here to assist with kwargs parsing */ -@@ -2829,7 +2829,7 @@ vm_call_sorbet_with_frame(rb_execution_context_t *ec, rb_control_frame_t *reg_cf - const rb_callable_method_entry_t *me = cd->cc.me; - /* TODO: verify this is a VM_METHOD_TYPE_SORBET? */ - const rb_method_sorbet_t *sorbet = UNALIGNED_MEMBER_PTR(me->def, body.sorbet); -- return vm_call_sorbet_with_frame_normal(ec, reg_cfp, calling, me, check_kw_splat, empty_kw_splat, calling->argc, sorbet->locals_size); -+ return vm_call_sorbet_with_frame_normal(ec, reg_cfp, calling, me, check_kw_splat, empty_kw_splat, calling->argc, sorbet->iseqptr->body->local_table_size); - } - - static VALUE -diff --git a/vm_method.c b/vm_method.c -index e805326656a8..6acdb09541a7 100644 ---- a/vm_method.c -+++ b/vm_method.c -@@ -146,8 +146,7 @@ rb_add_method_cfunc(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc, rb_me - } - - void --rb_add_method_sorbet(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc, rb_method_visibility_t visi, -- void *iseqptr) -+rb_add_method_sorbet(VALUE klass, ID mid, VALUE (*func)(ANYARGS), const rb_sorbet_param_t *param, int argc, rb_method_visibility_t visi, void *iseqptr) - { - if (argc != -1) rb_raise(rb_eArgError, "Incorrect arity for sorbet method"); - if (func == rb_f_notimplement) { -@@ -156,9 +155,8 @@ rb_add_method_sorbet(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc, rb_m - else { - rb_method_sorbet_t opt; - opt.func = func; -+ opt.param = param; - opt.iseqptr = (rb_iseq_t *)iseqptr; -- opt.locals_size = opt.iseqptr->body->local_table_size; -- opt.stack_max = opt.iseqptr->body->stack_max; - rb_add_method(klass, mid, VM_METHOD_TYPE_SORBET, &opt, visi); - } - } -@@ -247,12 +245,11 @@ setup_method_cfunc_struct(rb_method_cfunc_t *cfunc, VALUE (*func)(), int argc) - } - - static void --setup_method_sorbet_struct(rb_method_sorbet_t *sorbet, VALUE (*func)(), rb_iseq_t *iseqptr, int locals_size, int stack_max) -+setup_method_sorbet_struct(rb_method_sorbet_t *sorbet, VALUE (*func)(), const rb_sorbet_param_t *param, rb_iseq_t *iseqptr) - { - sorbet->func = func; -+ sorbet->param = param; - sorbet->iseqptr = iseqptr; -- sorbet->locals_size = locals_size; -- sorbet->stack_max = stack_max; - } - - MJIT_FUNC_EXPORTED void -@@ -291,7 +288,7 @@ rb_method_definition_set(const rb_method_entry_t *me, rb_method_definition_t *de - case VM_METHOD_TYPE_SORBET: - { - rb_method_sorbet_t *sorbet = (rb_method_sorbet_t *)opts; -- setup_method_sorbet_struct(UNALIGNED_MEMBER_PTR(def, body.sorbet), sorbet->func, sorbet->iseqptr, sorbet->locals_size, sorbet->stack_max); -+ setup_method_sorbet_struct(UNALIGNED_MEMBER_PTR(def, body.sorbet), sorbet->func, sorbet->param, sorbet->iseqptr); - return; - } - case VM_METHOD_TYPE_ATTRSET: - -From 68af8cc532d9eb780703240b06f51c2fe9b89f0e Mon Sep 17 00:00:00 2001 -From: Nathan Froyd -Date: Thu, 8 Apr 2021 15:04:26 -0400 -Subject: [PATCH 07/30] add a fast path for sorbet functions with only required - arguments - ---- - vm_insnhelper.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++- - 1 file changed, 84 insertions(+), 1 deletion(-) - -diff --git a/vm_insnhelper.c b/vm_insnhelper.c -index 15fb2764bd66..f91fcf8b2ef2 100644 ---- a/vm_insnhelper.c -+++ b/vm_insnhelper.c -@@ -2541,6 +2541,45 @@ vm_call_cfunc(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb - return vm_call_cfunc_with_frame(ec, reg_cfp, calling, cd, empty_kw_splat); - } - -+/* cf. rb_simple_iseq_p, but we store our parameter information differently */ -+static bool -+vm_call_sorbet_simple_p(const rb_method_sorbet_t *sorbet) -+{ -+ return sorbet->param->flags.has_opt == FALSE && -+ sorbet->param->flags.has_rest == FALSE && -+ sorbet->param->flags.has_post == FALSE && -+ sorbet->param->flags.has_kw == FALSE && -+ sorbet->param->flags.has_kwrest == FALSE && -+ sorbet->param->flags.accepts_no_kwarg == FALSE && -+ sorbet->param->flags.has_block == FALSE; -+} -+ -+/* This call combines vm_call_iseq_optimizable_p and logic in vm_callee_setup_arg */ -+static bool -+vm_call_sorbet_optimizable_p(const struct rb_call_info *ci, const struct rb_call_cache *cc, -+ const rb_method_sorbet_t *sorbet) -+{ -+ if (!vm_call_iseq_optimizable_p(ci, cc)) { -+ return false; -+ } -+ -+ /* vm_callee_setup_arg */ -+ if (UNLIKELY(ci->flag & VM_CALL_KW_SPLAT)) { -+ return false; -+ } -+ -+ /* We only handle simple calls to functions with required args, unlike -+ * vm_callee_setup_arg */ -+ if (!vm_call_sorbet_simple_p(sorbet)) { -+ return false; -+ } -+ -+ /* This callsite is to a method that only takes required arguments. */ -+ /* TODO: change this to handle more of the cases that vm_callee_setup_arg does, -+ * like optarg-only and kwarg-only functions. */ -+ return true; -+} -+ - /* -- Remove empty_kw_splat In 3.0 -- */ - static inline VALUE - vm_call_sorbet_with_frame_normal(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, const rb_callable_method_entry_t *me, int check_kw_splat, int empty_kw_splat, int param_size, int local_size) -@@ -2849,6 +2888,50 @@ vm_call_sorbet(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct r - return vm_call_sorbet_with_frame(ec, reg_cfp, calling, cd, empty_kw_splat); - } - -+/* At this point, we've already determined that the method we're calling is a -+ * Sorbet method, and we have a fastpath to call vm_call_sorbet in place. -+ * Depending on the particular function we're calling, we might be able to do -+ * better, which is what this function is trying to decide. -+ */ -+static VALUE -+vm_call_sorbet_maybe_setup_fastpath(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling, struct rb_call_data *cd) -+{ -+ const struct rb_call_info *ci = &cd->ci; -+ struct rb_call_cache *cc = &cd->cc; -+ const rb_method_sorbet_t *sorbet = UNALIGNED_MEMBER_PTR(cc->me->def, body.sorbet); -+ -+ /* Just take the normal path, we'll call vm_call_sorbet directly next time. */ -+ if (!vm_call_sorbet_optimizable_p(ci, cc, sorbet)) { -+ return vm_call_sorbet(ec, cfp, calling, cd); -+ } -+ -+ /* We know that the method we're calling takes only required arguments. -+ * But we need to verify that the method is being passed only required -+ * arguments and there aren't any kwarg fixups that we need to do. We -+ * only need to do this once, cf. vm_callee_setup_arg. -+ */ -+ CALLER_SETUP_ARG(cfp, calling, ci); -+ int empty_kw_splat = calling->kw_splat; -+ CALLER_REMOVE_EMPTY_KW_SPLAT(cfp, calling, ci); -+ if (empty_kw_splat && calling->kw_splat) { -+ empty_kw_splat = 0; -+ } -+ -+ if (UNLIKELY(calling->argc != sorbet->param->lead_num)) { -+ /* vm_callee_setup_arg calls argument_arity_error, but our iseq is not -+ * set up in the way that function expects. We don't declare and call -+ * sorbet_raiseArity here because it's nice to have a Ruby with just -+ * the Sorbet calling convention patches applied be able to compile -+ * and run Ruby's testsuite. Instead, just call the function "normally" -+ * and let the argument checking in the function itself handle raising -+ * the error. -+ */ -+ return vm_call_sorbet_with_frame(ec, cfp, calling, cd, empty_kw_splat); -+ } -+ -+ CC_SET_FASTPATH(cc, vm_call_sorbet_fast_func(ci, sorbet->param->size, sorbet->iseqptr->body->local_table_size), TRUE); -+ return vm_call_sorbet(ec, cfp, calling, cd); -+} - - static VALUE - vm_call_ivar(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling, struct rb_call_data *cd) -@@ -3237,7 +3320,7 @@ vm_call_method_each_type(rb_execution_context_t *ec, rb_control_frame_t *cfp, st - - case VM_METHOD_TYPE_SORBET: - CC_SET_FASTPATH(cc, vm_call_sorbet, TRUE); -- return vm_call_sorbet(ec, cfp, calling, cd); -+ return vm_call_sorbet_maybe_setup_fastpath(ec, cfp, calling, cd); - - case VM_METHOD_TYPE_ATTRSET: - CALLER_SETUP_ARG(cfp, calling, ci); - -From b9355a96db799a444389759e7eecff4a460df0e3 Mon Sep 17 00:00:00 2001 -From: Nathan Froyd -Date: Fri, 16 Apr 2021 10:30:51 -0400 -Subject: [PATCH 08/30] remove VM_FRAME_MAGIC_CFRAME setting from sorbet - calling convention - ---- - vm_eval.c | 7 +++++-- - vm_insnhelper.c | 7 +++++-- - 2 files changed, 10 insertions(+), 4 deletions(-) - -diff --git a/vm_eval.c b/vm_eval.c -index 72a77524ede5..d18e79e783a0 100644 ---- a/vm_eval.c -+++ b/vm_eval.c -@@ -118,8 +118,11 @@ vm_call0_sorbet_with_frame(rb_execution_context_t* ec, struct rb_calling_info *c - int argc = calling->argc; - ID mid = ci->mid; - VALUE block_handler = calling->block_handler; -- /* We are close enough to VM_METHOD_TYPE_CFUNC that we claim our frames are C function frames */ -- int frame_flags = VM_FRAME_MAGIC_CFUNC | VM_FRAME_FLAG_CFRAME | VM_ENV_FLAG_LOCAL; -+ /* We are close enough to VM_METHOD_TYPE_CFUNC that we claim our frames are C function frames. -+ * We do not set VM_FRAME_MAGIC_CFRAME because we do maintain local variables that can -+ * be accessed by Binding#local_variables and that need to be accessed by blocks/closures. -+ */ -+ int frame_flags = VM_FRAME_MAGIC_CFUNC | VM_ENV_FLAG_LOCAL; - - if (calling->kw_splat) { - if (argc > 0 && RB_TYPE_P(argv[argc-1], T_HASH) && RHASH_EMPTY_P(argv[argc-1])) { -diff --git a/vm_insnhelper.c b/vm_insnhelper.c -index f91fcf8b2ef2..54333f6ab7d4 100644 ---- a/vm_insnhelper.c -+++ b/vm_insnhelper.c -@@ -2590,8 +2590,11 @@ vm_call_sorbet_with_frame_normal(rb_execution_context_t *ec, rb_control_frame_t - - VALUE recv = calling->recv; - VALUE block_handler = calling->block_handler; -- /* We are close enough to VM_METHOD_TYPE_CFUNC that we claim our frames are C function frames */ -- VALUE frame_type = VM_FRAME_MAGIC_CFUNC | VM_FRAME_FLAG_CFRAME | VM_ENV_FLAG_LOCAL; -+ /* We are close enough to VM_METHOD_TYPE_CFUNC that we claim our frames are C function frames. -+ * We do not set VM_FRAME_MAGIC_CFRAME because we do maintain local variables that can -+ * be accessed by Binding#local_variables and that need to be accessed by blocks/closures. -+ */ -+ VALUE frame_type = VM_FRAME_MAGIC_CFUNC | VM_ENV_FLAG_LOCAL; - int argc = param_size; - - if (check_kw_splat) { - -From 7e5c9183bd985c6f637bf03ca25687a746d450f3 Mon Sep 17 00:00:00 2001 -From: Nathan Froyd -Date: Fri, 16 Apr 2021 10:44:38 -0400 -Subject: [PATCH 09/30] don't pass argc when defining sorbet methods, as it's - unnecessary - ---- - class.c | 4 ++-- - include/ruby/intern.h | 2 +- - method.h | 8 ++++---- - vm_method.c | 5 ++--- - 4 files changed, 9 insertions(+), 10 deletions(-) - -diff --git a/class.c b/class.c -index bc6fd864a15e..52f5908ca72b 100644 ---- a/class.c -+++ b/class.c -@@ -1758,9 +1758,9 @@ rb_define_singleton_method(VALUE obj, const char *name, VALUE (*func)(ANYARGS), - } - - void --rb_define_singleton_sorbet_method(VALUE obj, const char *name, VALUE(*func)(ANYARGS), const void *param, int argc, void *iseqptr) -+rb_define_singleton_sorbet_method(VALUE obj, const char *name, VALUE(*func)(int, VALUE *, VALUE), const void *param, void *iseqptr) - { -- rb_add_method_sorbet(singleton_class_of(obj), rb_intern(name), func, (const rb_sorbet_param_t *)param, argc, METHOD_VISI_PUBLIC, iseqptr); -+ rb_add_method_sorbet(singleton_class_of(obj), rb_intern(name), func, (const rb_sorbet_param_t *)param, METHOD_VISI_PUBLIC, iseqptr); - } - - #ifdef rb_define_module_function -diff --git a/include/ruby/intern.h b/include/ruby/intern.h -index fc855290e45f..e2b127d4168e 100644 ---- a/include/ruby/intern.h -+++ b/include/ruby/intern.h -@@ -238,7 +238,7 @@ void rb_define_protected_method(VALUE, const char*, VALUE (*)(ANYARGS), int); - void rb_define_private_method(VALUE, const char*, VALUE (*)(ANYARGS), int); - void rb_define_singleton_method(VALUE, const char*, VALUE(*)(ANYARGS), int); - /* included so we don't expose singleton_class_of outside of class.c */ --void rb_define_singleton_sorbet_method(VALUE, const char*, VALUE(*)(ANYARGS), const void *, int, void *); -+void rb_define_singleton_sorbet_method(VALUE, const char*, VALUE(*)(int, VALUE *, VALUE), const void *, void *); - VALUE rb_singleton_class(VALUE); - /* compar.c */ - int rb_cmpint(VALUE, VALUE, VALUE); -diff --git a/method.h b/method.h -index 1890ee8253a5..a630af9501d9 100644 ---- a/method.h -+++ b/method.h -@@ -200,9 +200,9 @@ typedef struct rb_sorbet_param_struct { - } rb_sorbet_param_t; - - typedef struct rb_method_sorbet_struct { -- /* cf. rb_method_cfunc_struct */ -- VALUE (*func)(ANYARGS); -- /* no need for invoker, since there's only the (recv, argc, argv) call style */ -+ /* cf. rb_method_cfunc_struct, but we only support one argument style */ -+ VALUE (*func)(int, VALUE *, VALUE); -+ /* no need for invoker, since there's only the (argc, argv, recv) call style */ - /* similarly, no need for argc */ - - const rb_sorbet_param_t *param; /* cf. rb_iseq_constant_body.param */ -@@ -265,7 +265,7 @@ STATIC_ASSERT(sizeof_method_def, offsetof(rb_method_definition_t, body)==8); - UNDEFINED_METHOD_ENTRY_P((def)->body.refined.orig_me)) - - void rb_add_method_cfunc(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc, rb_method_visibility_t visi); --void rb_add_method_sorbet(VALUE klass, ID mid, VALUE (*func)(ANYARGS), const rb_sorbet_param_t *param, int argc, rb_method_visibility_t visi, void *iseqptr); -+void rb_add_method_sorbet(VALUE klass, ID mid, VALUE (*func)(int, VALUE *, VALUE), const rb_sorbet_param_t *param, rb_method_visibility_t visi, void *iseqptr); - void rb_add_method_iseq(VALUE klass, ID mid, const rb_iseq_t *iseq, rb_cref_t *cref, rb_method_visibility_t visi); - void rb_add_refined_method_entry(VALUE refined_class, ID mid); - void rb_add_method(VALUE klass, ID mid, rb_method_type_t type, void *option, rb_method_visibility_t visi); -diff --git a/vm_method.c b/vm_method.c -index 6acdb09541a7..18d38f874aa1 100644 ---- a/vm_method.c -+++ b/vm_method.c -@@ -146,9 +146,8 @@ rb_add_method_cfunc(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc, rb_me - } - - void --rb_add_method_sorbet(VALUE klass, ID mid, VALUE (*func)(ANYARGS), const rb_sorbet_param_t *param, int argc, rb_method_visibility_t visi, void *iseqptr) -+rb_add_method_sorbet(VALUE klass, ID mid, VALUE (*func)(int, VALUE *, VALUE), const rb_sorbet_param_t *param, rb_method_visibility_t visi, void *iseqptr) - { -- if (argc != -1) rb_raise(rb_eArgError, "Incorrect arity for sorbet method"); - if (func == rb_f_notimplement) { - rb_define_notimplement_method_id(klass, mid, visi); - } -@@ -245,7 +244,7 @@ setup_method_cfunc_struct(rb_method_cfunc_t *cfunc, VALUE (*func)(), int argc) - } - - static void --setup_method_sorbet_struct(rb_method_sorbet_t *sorbet, VALUE (*func)(), const rb_sorbet_param_t *param, rb_iseq_t *iseqptr) -+setup_method_sorbet_struct(rb_method_sorbet_t *sorbet, VALUE (*func)(int, VALUE*, VALUE), const rb_sorbet_param_t *param, rb_iseq_t *iseqptr) - { - sorbet->func = func; - sorbet->param = param; - -From 6c09090c9d6a0484b9977e88a1733c696203bb7f Mon Sep 17 00:00:00 2001 -From: Nathan Froyd -Date: Fri, 16 Apr 2021 10:59:46 -0400 -Subject: [PATCH 10/30] delete some TODOs that are no longer applicable - -We don't need to verify `VM_METHOD_TYPE_SORBET` because callers have -already validated this for us. - -We do still need to munge the send args, but the fastpath -implementations that this patch starts to implement can avoid those -mungings when possible. So we need to add more of those fastpath -implementations. ---- - vm_insnhelper.c | 3 --- - 1 file changed, 3 deletions(-) - -diff --git a/vm_insnhelper.c b/vm_insnhelper.c -index 54333f6ab7d4..2ced5655e88b 100644 ---- a/vm_insnhelper.c -+++ b/vm_insnhelper.c -@@ -2585,7 +2585,6 @@ static inline VALUE - vm_call_sorbet_with_frame_normal(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, const rb_callable_method_entry_t *me, int check_kw_splat, int empty_kw_splat, int param_size, int local_size) - { - VALUE val; -- /* TODO: verify this is a VM_METHOD_TYPE_SORBET? */ - const rb_method_sorbet_t *sorbet = UNALIGNED_MEMBER_PTR(me->def, body.sorbet); - - VALUE recv = calling->recv; -@@ -2869,7 +2868,6 @@ vm_call_sorbet_with_frame(rb_execution_context_t *ec, rb_control_frame_t *reg_cf - { - const int check_kw_splat = 1; - const rb_callable_method_entry_t *me = cd->cc.me; -- /* TODO: verify this is a VM_METHOD_TYPE_SORBET? */ - const rb_method_sorbet_t *sorbet = UNALIGNED_MEMBER_PTR(me->def, body.sorbet); - return vm_call_sorbet_with_frame_normal(ec, reg_cfp, calling, me, check_kw_splat, empty_kw_splat, calling->argc, sorbet->iseqptr->body->local_table_size); - } -@@ -2881,7 +2879,6 @@ vm_call_sorbet(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct r - int empty_kw_splat; - RB_DEBUG_COUNTER_INC(ccf_cfunc); - -- /* TODO: we'll want to tweak this to not munge the send args. */ - CALLER_SETUP_ARG(reg_cfp, calling, ci); - empty_kw_splat = calling->kw_splat; - CALLER_REMOVE_EMPTY_KW_SPLAT(reg_cfp, calling, ci); - -From 9bf4e09d6210a4c11f2ce7d7d6763334a9d2bb92 Mon Sep 17 00:00:00 2001 -From: Nathan Froyd -Date: Fri, 16 Apr 2021 11:58:59 -0400 -Subject: [PATCH 11/30] explain fastpath setting in - vm_call_sorbet_maybe_setup_fastpath - ---- - vm_insnhelper.c | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/vm_insnhelper.c b/vm_insnhelper.c -index 2ced5655e88b..491928899803 100644 ---- a/vm_insnhelper.c -+++ b/vm_insnhelper.c -@@ -2929,6 +2929,10 @@ vm_call_sorbet_maybe_setup_fastpath(rb_execution_context_t *ec, rb_control_frame - return vm_call_sorbet_with_frame(ec, cfp, calling, cd, empty_kw_splat); - } - -+ /* vm_call_method_each_type has already set the fastpath to vm_call_sorbet, -+ * which handles all of the cases above. We've done all of those checks so that -+ * we know a different fastpath is available, which we set here. -+ */ - CC_SET_FASTPATH(cc, vm_call_sorbet_fast_func(ci, sorbet->param->size, sorbet->iseqptr->body->local_table_size), TRUE); - return vm_call_sorbet(ec, cfp, calling, cd); - } - -From 5baf06b400a5164efae5c80f20e2c9ec75b04458 Mon Sep 17 00:00:00 2001 -From: Nathan Froyd -Date: Fri, 16 Apr 2021 12:03:59 -0400 -Subject: [PATCH 12/30] do fastpath optimizations for all positional args - ---- - vm_insnhelper.c | 13 ++++++------- - 1 file changed, 6 insertions(+), 7 deletions(-) - -diff --git a/vm_insnhelper.c b/vm_insnhelper.c -index 491928899803..bb93f8aea0bc 100644 ---- a/vm_insnhelper.c -+++ b/vm_insnhelper.c -@@ -2545,8 +2545,7 @@ vm_call_cfunc(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb - static bool - vm_call_sorbet_simple_p(const rb_method_sorbet_t *sorbet) - { -- return sorbet->param->flags.has_opt == FALSE && -- sorbet->param->flags.has_rest == FALSE && -+ return sorbet->param->flags.has_rest == FALSE && - sorbet->param->flags.has_post == FALSE && - sorbet->param->flags.has_kw == FALSE && - sorbet->param->flags.has_kwrest == FALSE && -@@ -2568,15 +2567,15 @@ vm_call_sorbet_optimizable_p(const struct rb_call_info *ci, const struct rb_call - return false; - } - -- /* We only handle simple calls to functions with required args, unlike -+ /* We only handle simple calls to functions with positional args, unlike - * vm_callee_setup_arg */ - if (!vm_call_sorbet_simple_p(sorbet)) { - return false; - } - -- /* This callsite is to a method that only takes required arguments. */ -+ /* This callsite is to a method that only takes positional arguments. */ - /* TODO: change this to handle more of the cases that vm_callee_setup_arg does, -- * like optarg-only and kwarg-only functions. */ -+ * like kwarg-only functions. */ - return true; - } - -@@ -2905,8 +2904,8 @@ vm_call_sorbet_maybe_setup_fastpath(rb_execution_context_t *ec, rb_control_frame - return vm_call_sorbet(ec, cfp, calling, cd); - } - -- /* We know that the method we're calling takes only required arguments. -- * But we need to verify that the method is being passed only required -+ /* We know that the method we're calling takes only positional arguments. -+ * But we need to verify that the method is being passed only positional - * arguments and there aren't any kwarg fixups that we need to do. We - * only need to do this once, cf. vm_callee_setup_arg. - */ - -From 0fbdce696c5206e84d9b75e1f601d0be0c6774a8 Mon Sep 17 00:00:00 2001 -From: Nathan Froyd -Date: Fri, 23 Apr 2021 10:14:03 -0400 -Subject: [PATCH 13/30] add back event hooks to sorbet calls - ---- - vm_insnhelper.c | 7 +++++-- - 1 file changed, 5 insertions(+), 2 deletions(-) - -diff --git a/vm_insnhelper.c b/vm_insnhelper.c -index bb93f8aea0bc..a361d382f1a9 100644 ---- a/vm_insnhelper.c -+++ b/vm_insnhelper.c -@@ -2581,8 +2581,9 @@ vm_call_sorbet_optimizable_p(const struct rb_call_info *ci, const struct rb_call - - /* -- Remove empty_kw_splat In 3.0 -- */ - static inline VALUE --vm_call_sorbet_with_frame_normal(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, const rb_callable_method_entry_t *me, int check_kw_splat, int empty_kw_splat, int param_size, int local_size) -+vm_call_sorbet_with_frame_normal(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, struct rb_call_data *cd, const rb_callable_method_entry_t *me, int check_kw_splat, int empty_kw_splat, int param_size, int local_size) - { -+ const struct rb_call_info *ci = &cd->ci; - VALUE val; - const rb_method_sorbet_t *sorbet = UNALIGNED_MEMBER_PTR(me->def, body.sorbet); - -@@ -2605,6 +2606,7 @@ vm_call_sorbet_with_frame_normal(rb_execution_context_t *ec, rb_control_frame_t - } - - RUBY_DTRACE_CMETHOD_ENTRY_HOOK(ec, me->owner, me->def->original_id); -+ EXEC_EVENT_HOOK(ec, RUBY_EVENT_C_CALL, recv, me->def->original_id, ci->mid, me->owner, Qundef); - - vm_push_frame(ec, sorbet->iseqptr, frame_type, recv, - block_handler, (VALUE)me, -@@ -2618,6 +2620,7 @@ vm_call_sorbet_with_frame_normal(rb_execution_context_t *ec, rb_control_frame_t - - rb_vm_pop_frame(ec); - -+ EXEC_EVENT_HOOK(ec, RUBY_EVENT_C_RETURN, recv, me->def->original_id, ci->mid, me->owner, val); - RUBY_DTRACE_CMETHOD_RETURN_HOOK(ec, me->owner, me->def->original_id); - - return val; -@@ -2868,7 +2871,7 @@ vm_call_sorbet_with_frame(rb_execution_context_t *ec, rb_control_frame_t *reg_cf - const int check_kw_splat = 1; - const rb_callable_method_entry_t *me = cd->cc.me; - const rb_method_sorbet_t *sorbet = UNALIGNED_MEMBER_PTR(me->def, body.sorbet); -- return vm_call_sorbet_with_frame_normal(ec, reg_cfp, calling, me, check_kw_splat, empty_kw_splat, calling->argc, sorbet->iseqptr->body->local_table_size); -+ return vm_call_sorbet_with_frame_normal(ec, reg_cfp, calling, cd, me, check_kw_splat, empty_kw_splat, calling->argc, sorbet->iseqptr->body->local_table_size); - } - - static VALUE - -From 71f0921d70fc960cace11a16aaee302df07a1345 Mon Sep 17 00:00:00 2001 -From: Nathan Froyd -Date: Wed, 28 Apr 2021 10:42:40 -0400 -Subject: [PATCH 14/30] disable fastpath for optional arguments - ---- - vm_insnhelper.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/vm_insnhelper.c b/vm_insnhelper.c -index a361d382f1a9..d07887dec803 100644 ---- a/vm_insnhelper.c -+++ b/vm_insnhelper.c -@@ -2545,7 +2545,8 @@ vm_call_cfunc(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb - static bool - vm_call_sorbet_simple_p(const rb_method_sorbet_t *sorbet) - { -- return sorbet->param->flags.has_rest == FALSE && -+ return sorbet->param->flags.has_opt == FALSE && -+ sorbet->param->flags.has_rest == FALSE && - sorbet->param->flags.has_post == FALSE && - sorbet->param->flags.has_kw == FALSE && - sorbet->param->flags.has_kwrest == FALSE && - -From 9f54cc4d7f7c0dfc58233c99983b1a6bcf8f661f Mon Sep 17 00:00:00 2001 -From: Nathan Froyd -Date: Wed, 28 Apr 2021 14:31:53 -0400 -Subject: [PATCH 15/30] move fastpath helpers to where they compile - ---- - vm_insnhelper.c | 50 ++++++++++++++++++++++++------------------------- - 1 file changed, 25 insertions(+), 25 deletions(-) - -diff --git a/vm_insnhelper.c b/vm_insnhelper.c -index d07887dec803..d95cc3ba1c9d 100644 ---- a/vm_insnhelper.c -+++ b/vm_insnhelper.c -@@ -2627,6 +2627,31 @@ vm_call_sorbet_with_frame_normal(rb_execution_context_t *ec, rb_control_frame_t - return val; - } - -+static VALUE -+vm_call_sorbet_with_frame(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, struct rb_call_data *cd, int empty_kw_splat) -+{ -+ const int check_kw_splat = 1; -+ const rb_callable_method_entry_t *me = cd->cc.me; -+ const rb_method_sorbet_t *sorbet = UNALIGNED_MEMBER_PTR(me->def, body.sorbet); -+ return vm_call_sorbet_with_frame_normal(ec, reg_cfp, calling, cd, me, check_kw_splat, empty_kw_splat, calling->argc, sorbet->iseqptr->body->local_table_size); -+} -+ -+static VALUE -+vm_call_sorbet(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, struct rb_call_data *cd) -+{ -+ const struct rb_call_info *ci = &cd->ci; -+ int empty_kw_splat; -+ RB_DEBUG_COUNTER_INC(ccf_cfunc); -+ -+ CALLER_SETUP_ARG(reg_cfp, calling, ci); -+ empty_kw_splat = calling->kw_splat; -+ CALLER_REMOVE_EMPTY_KW_SPLAT(reg_cfp, calling, ci); -+ if (empty_kw_splat && calling->kw_splat) { -+ empty_kw_splat = 0; -+ } -+ return vm_call_sorbet_with_frame(ec, reg_cfp, calling, cd, empty_kw_splat); -+} -+ - /* These would normally be generated by tool/mk_call_iseq_optimized.rb. - * - * They live here because of a bootstrapping problem: the Ruby tarball we apply -@@ -2866,31 +2891,6 @@ vm_call_sorbet_fast_func(const struct rb_call_info *ci, const int param_size, co - return &vm_call_sorbet; - } - --static VALUE --vm_call_sorbet_with_frame(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, struct rb_call_data *cd, int empty_kw_splat) --{ -- const int check_kw_splat = 1; -- const rb_callable_method_entry_t *me = cd->cc.me; -- const rb_method_sorbet_t *sorbet = UNALIGNED_MEMBER_PTR(me->def, body.sorbet); -- return vm_call_sorbet_with_frame_normal(ec, reg_cfp, calling, cd, me, check_kw_splat, empty_kw_splat, calling->argc, sorbet->iseqptr->body->local_table_size); --} -- --static VALUE --vm_call_sorbet(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, struct rb_call_data *cd) --{ -- const struct rb_call_info *ci = &cd->ci; -- int empty_kw_splat; -- RB_DEBUG_COUNTER_INC(ccf_cfunc); -- -- CALLER_SETUP_ARG(reg_cfp, calling, ci); -- empty_kw_splat = calling->kw_splat; -- CALLER_REMOVE_EMPTY_KW_SPLAT(reg_cfp, calling, ci); -- if (empty_kw_splat && calling->kw_splat) { -- empty_kw_splat = 0; -- } -- return vm_call_sorbet_with_frame(ec, reg_cfp, calling, cd, empty_kw_splat); --} -- - /* At this point, we've already determined that the method we're calling is a - * Sorbet method, and we have a fastpath to call vm_call_sorbet in place. - * Depending on the particular function we're calling, we might be able to do - -From 35646afb50bcd190112cf46e3d16fca18fb507eb Mon Sep 17 00:00:00 2001 -From: Nathan Froyd -Date: Tue, 11 May 2021 09:19:44 -0400 -Subject: [PATCH 16/30] add a control frame parameter to sorbet methods - ---- - class.c | 2 +- - include/ruby/intern.h | 2 -- - method.h | 9 +++++++-- - vm_eval.c | 8 ++++---- - vm_insnhelper.c | 10 +++++----- - vm_method.c | 19 +++++++------------ - 6 files changed, 24 insertions(+), 26 deletions(-) - -diff --git a/class.c b/class.c -index 52f5908ca72b..f290b5f52270 100644 ---- a/class.c -+++ b/class.c -@@ -1758,7 +1758,7 @@ rb_define_singleton_method(VALUE obj, const char *name, VALUE (*func)(ANYARGS), - } - - void --rb_define_singleton_sorbet_method(VALUE obj, const char *name, VALUE(*func)(int, VALUE *, VALUE), const void *param, void *iseqptr) -+rb_define_singleton_sorbet_method(VALUE obj, const char *name, rb_sorbet_func_t func, const void *param, void *iseqptr) - { - rb_add_method_sorbet(singleton_class_of(obj), rb_intern(name), func, (const rb_sorbet_param_t *)param, METHOD_VISI_PUBLIC, iseqptr); - } -diff --git a/include/ruby/intern.h b/include/ruby/intern.h -index e2b127d4168e..2f60fb569ecb 100644 ---- a/include/ruby/intern.h -+++ b/include/ruby/intern.h -@@ -237,8 +237,6 @@ void rb_undef(VALUE, ID); - void rb_define_protected_method(VALUE, const char*, VALUE (*)(ANYARGS), int); - void rb_define_private_method(VALUE, const char*, VALUE (*)(ANYARGS), int); - void rb_define_singleton_method(VALUE, const char*, VALUE(*)(ANYARGS), int); --/* included so we don't expose singleton_class_of outside of class.c */ --void rb_define_singleton_sorbet_method(VALUE, const char*, VALUE(*)(int, VALUE *, VALUE), const void *, void *); - VALUE rb_singleton_class(VALUE); - /* compar.c */ - int rb_cmpint(VALUE, VALUE, VALUE); -diff --git a/method.h b/method.h -index a630af9501d9..842b85c15809 100644 ---- a/method.h -+++ b/method.h -@@ -199,9 +199,11 @@ typedef struct rb_sorbet_param_struct { - const ID *kw_table; - } rb_sorbet_param_t; - -+typedef VALUE (*rb_sorbet_func_t)(int, VALUE *, VALUE, struct rb_control_frame_struct *); -+ - typedef struct rb_method_sorbet_struct { - /* cf. rb_method_cfunc_struct, but we only support one argument style */ -- VALUE (*func)(int, VALUE *, VALUE); -+ rb_sorbet_func_t func; - /* no need for invoker, since there's only the (argc, argv, recv) call style */ - /* similarly, no need for argc */ - -@@ -265,7 +267,10 @@ STATIC_ASSERT(sizeof_method_def, offsetof(rb_method_definition_t, body)==8); - UNDEFINED_METHOD_ENTRY_P((def)->body.refined.orig_me)) - - void rb_add_method_cfunc(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc, rb_method_visibility_t visi); --void rb_add_method_sorbet(VALUE klass, ID mid, VALUE (*func)(int, VALUE *, VALUE), const rb_sorbet_param_t *param, rb_method_visibility_t visi, void *iseqptr); -+void rb_add_method_sorbet(VALUE klass, ID mid, rb_sorbet_func_t func, const rb_sorbet_param_t *param, rb_method_visibility_t visi, void *iseqptr); -+/* included so we don't expose singleton_class_of outside of class.c */ -+/* we can't use rb_sorbet_func_t here because it's not exported */ -+void rb_define_singleton_sorbet_method(VALUE, const char*, rb_sorbet_func_t, const void *, void *); - void rb_add_method_iseq(VALUE klass, ID mid, const rb_iseq_t *iseq, rb_cref_t *cref, rb_method_visibility_t visi); - void rb_add_refined_method_entry(VALUE refined_class, ID mid); - void rb_add_method(VALUE klass, ID mid, rb_method_type_t type, void *option, rb_method_visibility_t visi); -diff --git a/vm_eval.c b/vm_eval.c -index d18e79e783a0..52805d317f02 100644 ---- a/vm_eval.c -+++ b/vm_eval.c -@@ -139,12 +139,12 @@ vm_call0_sorbet_with_frame(rb_execution_context_t* ec, struct rb_calling_info *c - { - rb_control_frame_t *reg_cfp = ec->cfp; - -- vm_push_frame(ec, sorbet->iseqptr, frame_flags, recv, -- block_handler, (VALUE)me, -- 0, reg_cfp->sp, sorbet->iseqptr->body->local_table_size, sorbet->iseqptr->body->stack_max); -+ rb_control_frame_t *new_cfp = vm_push_frame(ec, sorbet->iseqptr, frame_flags, recv, -+ block_handler, (VALUE)me, -+ 0, reg_cfp->sp, sorbet->iseqptr->body->local_table_size, sorbet->iseqptr->body->stack_max); - - /* TODO: eventually we want to pass cd in here to assist with kwargs parsing */ -- val = (*sorbet->func)(argc, argv, recv); -+ val = (*sorbet->func)(argc, argv, recv, new_cfp); - - CHECK_CFP_CONSISTENCY("vm_call0_sorbet_with_frame"); - rb_vm_pop_frame(ec); -diff --git a/vm_insnhelper.c b/vm_insnhelper.c -index d95cc3ba1c9d..ae6b87b9123c 100644 ---- a/vm_insnhelper.c -+++ b/vm_insnhelper.c -@@ -2609,17 +2609,17 @@ vm_call_sorbet_with_frame_normal(rb_execution_context_t *ec, rb_control_frame_t - RUBY_DTRACE_CMETHOD_ENTRY_HOOK(ec, me->owner, me->def->original_id); - EXEC_EVENT_HOOK(ec, RUBY_EVENT_C_CALL, recv, me->def->original_id, ci->mid, me->owner, Qundef); - -- vm_push_frame(ec, sorbet->iseqptr, frame_type, recv, -- block_handler, (VALUE)me, -- 0, ec->cfp->sp, local_size, sorbet->iseqptr->body->stack_max); -+ rb_control_frame_t *new_cfp = vm_push_frame(ec, sorbet->iseqptr, frame_type, recv, -+ block_handler, (VALUE)me, -+ 0, ec->cfp->sp, local_size, sorbet->iseqptr->body->stack_max); - - reg_cfp->sp -= argc + 1; - /* TODO: eventually we want to pass cd in here to assist with kwargs parsing */ -- val = (*sorbet->func)(argc, reg_cfp->sp + 1, recv); -+ val = (*sorbet->func)(argc, reg_cfp->sp + 1, recv, new_cfp); - - CHECK_CFP_CONSISTENCY("vm_call_sorbet"); - -- rb_vm_pop_frame(ec); -+ vm_pop_frame(ec, new_cfp, new_cfp->ep); - - EXEC_EVENT_HOOK(ec, RUBY_EVENT_C_RETURN, recv, me->def->original_id, ci->mid, me->owner, val); - RUBY_DTRACE_CMETHOD_RETURN_HOOK(ec, me->owner, me->def->original_id); -diff --git a/vm_method.c b/vm_method.c -index 18d38f874aa1..781feb6557e8 100644 ---- a/vm_method.c -+++ b/vm_method.c -@@ -146,18 +146,13 @@ rb_add_method_cfunc(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc, rb_me - } - - void --rb_add_method_sorbet(VALUE klass, ID mid, VALUE (*func)(int, VALUE *, VALUE), const rb_sorbet_param_t *param, rb_method_visibility_t visi, void *iseqptr) -+rb_add_method_sorbet(VALUE klass, ID mid, rb_sorbet_func_t func, const rb_sorbet_param_t *param, rb_method_visibility_t visi, void *iseqptr) - { -- if (func == rb_f_notimplement) { -- rb_define_notimplement_method_id(klass, mid, visi); -- } -- else { -- rb_method_sorbet_t opt; -- opt.func = func; -- opt.param = param; -- opt.iseqptr = (rb_iseq_t *)iseqptr; -- rb_add_method(klass, mid, VM_METHOD_TYPE_SORBET, &opt, visi); -- } -+ rb_method_sorbet_t opt; -+ opt.func = func; -+ opt.param = param; -+ opt.iseqptr = (rb_iseq_t *)iseqptr; -+ rb_add_method(klass, mid, VM_METHOD_TYPE_SORBET, &opt, visi); - } - - static void -@@ -244,7 +239,7 @@ setup_method_cfunc_struct(rb_method_cfunc_t *cfunc, VALUE (*func)(), int argc) - } - - static void --setup_method_sorbet_struct(rb_method_sorbet_t *sorbet, VALUE (*func)(int, VALUE*, VALUE), const rb_sorbet_param_t *param, rb_iseq_t *iseqptr) -+setup_method_sorbet_struct(rb_method_sorbet_t *sorbet, rb_sorbet_func_t func, const rb_sorbet_param_t *param, rb_iseq_t *iseqptr) - { - sorbet->func = func; - sorbet->param = param; - -From 48fe82a0fb0b125d1b784650200155999211870e Mon Sep 17 00:00:00 2001 -From: Nathan Froyd -Date: Wed, 12 May 2021 15:35:36 -0400 -Subject: [PATCH 17/30] record method entries for VM_METHOD_TYPE_SORBET in - backtraces - ---- - vm_backtrace.c | 4 +++- - 1 file changed, 3 insertions(+), 1 deletion(-) - -diff --git a/vm_backtrace.c b/vm_backtrace.c -index f6b4e8e1d6e4..1c4a7ff2784b 100644 ---- a/vm_backtrace.c -+++ b/vm_backtrace.c -@@ -1321,7 +1321,7 @@ rb_profile_frames(int start, int limit, VALUE *buff, int *lines) - - /* record frame info */ - cme = rb_vm_frame_method_entry(cfp); -- if (cme && cme->def->type == VM_METHOD_TYPE_ISEQ) { -+ if (cme && (cme->def->type == VM_METHOD_TYPE_ISEQ || cme->def->type == VM_METHOD_TYPE_SORBET)) { - buff[i] = (VALUE)cme; - } - else { -@@ -1353,6 +1353,8 @@ frame2iseq(VALUE frame) - switch (cme->def->type) { - case VM_METHOD_TYPE_ISEQ: - return cme->def->body.iseq.iseqptr; -+ case VM_METHOD_TYPE_SORBET: -+ return cme->def->body.sorbet.iseqptr; - default: - return NULL; - } - -From ee25d5416591d8e563f78323138604ea84aa9547 Mon Sep 17 00:00:00 2001 -From: Nathan Froyd -Date: Fri, 14 May 2021 10:30:25 -0400 -Subject: [PATCH 18/30] provide location information for sorbet methods - ---- - proc.c | 5 +++++ - 1 file changed, 5 insertions(+) - -diff --git a/proc.c b/proc.c -index 87c16530c55d..d10ae0c4f4b9 100644 ---- a/proc.c -+++ b/proc.c -@@ -2700,6 +2700,9 @@ method_def_iseq(const rb_method_definition_t *def) - case VM_METHOD_TYPE_OPTIMIZED: - case VM_METHOD_TYPE_MISSING: - case VM_METHOD_TYPE_REFINED: -+ /* don't return iseqptr here because sorbet method iseqs don't necessarily -+ * have all the information required by all the places that call method_def_iseq -+ */ - case VM_METHOD_TYPE_SORBET: - break; - } -@@ -2736,6 +2739,8 @@ method_def_location(const rb_method_definition_t *def) - if (!def->body.attr.location) - return Qnil; - return rb_ary_dup(def->body.attr.location); -+ } else if (def->type == VM_METHOD_TYPE_SORBET) { -+ return iseq_location(def->body.sorbet.iseqptr); - } - return iseq_location(method_def_iseq(def)); - } - -From f425aa7056f94cbb0781ccd6596c2d00cb3c0e88 Mon Sep 17 00:00:00 2001 -From: Nathan Froyd -Date: Mon, 24 May 2021 11:06:14 -0400 -Subject: [PATCH 19/30] always inline vm_call_sorbet_with_frame_normal - ---- - vm_insnhelper.c | 9 ++++++++- - 1 file changed, 8 insertions(+), 1 deletion(-) - -diff --git a/vm_insnhelper.c b/vm_insnhelper.c -index ae6b87b9123c..824efb54bd66 100644 ---- a/vm_insnhelper.c -+++ b/vm_insnhelper.c -@@ -2581,7 +2581,14 @@ vm_call_sorbet_optimizable_p(const struct rb_call_info *ci, const struct rb_call - } - - /* -- Remove empty_kw_splat In 3.0 -- */ --static inline VALUE -+/* Inlining this into the fastpath (vm_call_sorbet_fast_*) functions means the -+ * compiler can collapse away some of the stack manipulation in vm_push_frame. -+ * Compare the ALWAYS_INLINE declaration on vm_call_iseq_setup_normal, which -+ * works on the same principles. -+ */ -+ALWAYS_INLINE(static VALUE vm_call_sorbet_with_frame_normal(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, struct rb_call_data *cd, const rb_callable_method_entry_t *me, int check_kw_splat, int empty_kw_splat, int param_size, int local_size)); -+ -+static VALUE - vm_call_sorbet_with_frame_normal(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, struct rb_call_data *cd, const rb_callable_method_entry_t *me, int check_kw_splat, int empty_kw_splat, int param_size, int local_size) - { - const struct rb_call_info *ci = &cd->ci; - -From 197b973f6ebb478aab9aa3364abe7be4e9e596e8 Mon Sep 17 00:00:00 2001 -From: Adam Procter -Date: Wed, 11 Aug 2021 11:22:07 -0700 -Subject: [PATCH 20/30] Apply gc-remove-write-barrier.patch - ---- - class.c | 9 +++------ - eval.c | 3 +-- - method.h | 4 ++++ - vm_method.c | 29 +++++++++++++++++++++++++++-- - 4 files changed, 35 insertions(+), 10 deletions(-) - -diff --git a/class.c b/class.c -index c866d1d72721..a5d37408f704 100644 ---- a/class.c -+++ b/class.c -@@ -826,8 +826,7 @@ rb_include_class_new(VALUE module, VALUE super) - { - VALUE klass = class_alloc(T_ICLASS, rb_cClass); - -- RCLASS_M_TBL(OBJ_WB_UNPROTECT(klass)) = -- RCLASS_M_TBL(OBJ_WB_UNPROTECT(module)); /* TODO: unprotected? */ -+ RCLASS_M_TBL(klass) = RCLASS_M_TBL(module); - - RCLASS_SET_ORIGIN(klass, module == RCLASS_ORIGIN(module) ? klass : RCLASS_ORIGIN(module)); - if (BUILTIN_TYPE(module) == T_ICLASS) { -@@ -964,13 +963,12 @@ move_refined_method(ID key, VALUE value, void *data) - const rb_method_entry_t *orig_me = me->def->body.refined.orig_me, *new_me; - RB_OBJ_WRITE(me, &me->def->body.refined.orig_me, NULL); - new_me = rb_method_entry_clone(me); -- rb_id_table_insert(tbl, key, (VALUE)new_me); -- RB_OBJ_WRITTEN(klass, Qundef, new_me); -+ rb_method_table_insert(klass, tbl, key, new_me); - rb_method_entry_copy(me, orig_me); - return ID_TABLE_CONTINUE; - } - else { -- rb_id_table_insert(tbl, key, (VALUE)me); -+ rb_method_table_insert(klass, tbl, key, me); - return ID_TABLE_DELETE; - } - } -@@ -985,7 +983,6 @@ ensure_origin(VALUE klass) - VALUE origin = RCLASS_ORIGIN(klass); - if (origin == klass) { - origin = class_alloc(T_ICLASS, klass); -- OBJ_WB_UNPROTECT(origin); /* TODO: conservative shading. Need more survey. */ - RCLASS_SET_SUPER(origin, RCLASS_SUPER(klass)); - RCLASS_SET_SUPER(klass, origin); - RCLASS_SET_ORIGIN(klass, origin); -diff --git a/eval.c b/eval.c -index 08f7ba97de27..8665c45116be 100644 ---- a/eval.c -+++ b/eval.c -@@ -1412,8 +1412,7 @@ rb_using_refinement(rb_cref_t *cref, VALUE klass, VALUE module) - c = iclass = rb_include_class_new(module, superclass); - RB_OBJ_WRITE(c, &RCLASS_REFINED_CLASS(c), klass); - -- RCLASS_M_TBL(OBJ_WB_UNPROTECT(c)) = -- RCLASS_M_TBL(OBJ_WB_UNPROTECT(module)); /* TODO: check unprotecting */ -+ RCLASS_M_TBL(c) = RCLASS_M_TBL(module); - - module = RCLASS_SUPER(module); - while (module && module != klass) { -diff --git a/method.h b/method.h -index b26caaa92d66..96ca223e0811 100644 ---- a/method.h -+++ b/method.h -@@ -180,6 +180,8 @@ struct rb_method_definition_struct { - uintptr_t method_serial; - }; - -+struct rb_id_table; -+ - typedef struct rb_method_definition_struct rb_method_definition_t; - STATIC_ASSERT(sizeof_method_def, offsetof(rb_method_definition_t, body)==8); - -@@ -222,6 +224,8 @@ const rb_method_entry_t *rb_method_entry_clone(const rb_method_entry_t *me); - const rb_callable_method_entry_t *rb_method_entry_complement_defined_class(const rb_method_entry_t *src_me, ID called_id, VALUE defined_class); - void rb_method_entry_copy(rb_method_entry_t *dst, const rb_method_entry_t *src); - -+void rb_method_table_insert(VALUE klass, struct rb_id_table *table, ID method_id, const rb_method_entry_t *me); -+ - void rb_scope_visibility_set(rb_method_visibility_t); - - VALUE rb_unnamed_parameters(int arity); -diff --git a/vm_method.c b/vm_method.c -index 450446878950..e1819f5c8048 100644 ---- a/vm_method.c -+++ b/vm_method.c -@@ -116,6 +116,31 @@ rb_clear_method_cache_by_class(VALUE klass) - } - } - -+void -+rb_method_table_insert(VALUE klass, struct rb_id_table *table, ID method_id, const rb_method_entry_t *me) -+{ -+ VALUE table_owner = klass; -+ if (RB_TYPE_P(klass, T_ICLASS) && !FL_TEST(klass, RICLASS_IS_ORIGIN)) { -+ bool owner_found = false; -+ VALUE owner = RBASIC(klass)->klass; -+ // Loop in case the owning class has prepended modules -+ while (owner) { -+ if (RCLASS_M_TBL(owner) == table) { -+ owner_found = true; -+ break; -+ } -+ owner = RCLASS_SUPER(owner); -+ } -+ if (!owner_found) rb_bug("failed to find method table owner"); -+ table_owner = owner; -+ } -+ -+ VM_ASSERT(RB_TYPE_P(table_owner, T_CLASS) || RB_TYPE_P(table_owner, T_ICLASS) || RB_TYPE_P(table_owner, T_MODULE)); -+ VM_ASSERT(table == RCLASS_M_TBL(table_owner)); -+ rb_id_table_insert(table, method_id, (VALUE)me); -+ RB_OBJ_WRITTEN(table_owner, Qundef, (VALUE)me); -+} -+ - VALUE - rb_f_notimplement(int argc, const VALUE *argv, VALUE obj, VALUE marker) - { -@@ -638,8 +663,7 @@ rb_method_entry_make(VALUE klass, ID mid, VALUE defined_class, rb_method_visibil - make_method_entry_refined(klass, me); - } - -- rb_id_table_insert(mtbl, mid, (VALUE)me); -- RB_OBJ_WRITTEN(klass, Qundef, (VALUE)me); -+ rb_method_table_insert(klass, mtbl, mid, me); - - VM_ASSERT(me->def != NULL); - -@@ -863,6 +887,7 @@ prepare_callable_method_entry(VALUE defined_class, ID id, const rb_method_entry_ - } - cme = rb_method_entry_complement_defined_class(me, me->called_id, defined_class); - rb_id_table_insert(mtbl, id, (VALUE)cme); -+ RB_OBJ_WRITTEN(defined_class, Qundef, (VALUE)cme); - VM_ASSERT(callable_method_entry_p(cme)); - } - } - -From b647146e41059bf0b6a4960f09e321de2b447eb1 Mon Sep 17 00:00:00 2001 -From: Adam Procter -Date: Wed, 12 May 2021 15:26:53 -0700 -Subject: [PATCH 21/30] Apply VM_FRAME_FLAG_LAMBDA patch from sorbet_llvm - ---- - vm.c | 4 ++-- - vm_insnhelper.c | 17 ++++++++++------- - 2 files changed, 12 insertions(+), 9 deletions(-) - -diff --git a/vm.c b/vm.c -index fc144be5b6b1..f1ba52b5f266 100644 ---- a/vm.c -+++ b/vm.c -@@ -1138,7 +1138,7 @@ invoke_block_from_c_bh(rb_execution_context_t *ec, VALUE block_handler, - case block_handler_type_ifunc: - return vm_yield_with_cfunc(ec, VM_BH_TO_IFUNC_BLOCK(block_handler), - VM_BH_TO_IFUNC_BLOCK(block_handler)->self, -- argc, argv, kw_splat, passed_block_handler, NULL); -+ argc, argv, kw_splat, passed_block_handler, is_lambda, NULL); - case block_handler_type_symbol: - return vm_yield_with_symbol(ec, VM_BH_TO_SYMBOL(block_handler), - argc, argv, kw_splat, passed_block_handler); -@@ -1219,7 +1219,7 @@ invoke_block_from_c_proc(rb_execution_context_t *ec, const rb_proc_t *proc, - argc--; - kw_splat = 2; - } -- return vm_yield_with_cfunc(ec, &block->as.captured, self, argc, argv, kw_splat, passed_block_handler, me); -+ return vm_yield_with_cfunc(ec, &block->as.captured, self, argc, argv, kw_splat, passed_block_handler, is_lambda, me); - case block_type_symbol: - return vm_yield_with_symbol(ec, block->as.symbol, argc, argv, kw_splat, passed_block_handler); - case block_type_proc: -diff --git a/vm_insnhelper.c b/vm_insnhelper.c -index c0d9092a67da..10428817b3ff 100644 ---- a/vm_insnhelper.c -+++ b/vm_insnhelper.c -@@ -3182,14 +3182,17 @@ static VALUE - vm_yield_with_cfunc(rb_execution_context_t *ec, - const struct rb_captured_block *captured, - VALUE self, int argc, const VALUE *argv, int kw_splat, VALUE block_handler, -- const rb_callable_method_entry_t *me) -+ int is_lambda, const rb_callable_method_entry_t *me) - { -- int is_lambda = FALSE; /* TODO */ -+ /* is_lambda = FALSE; */ /* Unpatched VM hard-wires this to FALSE. We take it as an arg. */ - VALUE val, arg, blockarg; - int frame_flag; - const struct vm_ifunc *ifunc = captured->code.ifunc; - -- if (is_lambda) { -+ /* In the unpatched VM, is_lambda is hard-wired to FALSE, so this boxing never actually -+ happens. Now that we plumb is_lambda in from the caller, letting the boxing happen causes -+ GC assertions in Ruby's test suite. Not sure why this happens. */ -+ if (0 /*is_lambda*/) { - arg = rb_ary_new4(argc, argv); - } - else if (argc == 0) { -@@ -3201,7 +3204,7 @@ vm_yield_with_cfunc(rb_execution_context_t *ec, - - blockarg = rb_vm_bh_to_procval(ec, block_handler); - -- frame_flag = VM_FRAME_MAGIC_IFUNC | VM_FRAME_FLAG_CFRAME | (me ? VM_FRAME_FLAG_BMETHOD : 0); -+ frame_flag = VM_FRAME_MAGIC_IFUNC | VM_FRAME_FLAG_CFRAME | (me ? VM_FRAME_FLAG_BMETHOD : 0) | (is_lambda ? VM_FRAME_FLAG_LAMBDA : 0); - switch (kw_splat) { - case 1: - frame_flag |= VM_FRAME_FLAG_CFRAME_KW; -@@ -3364,7 +3367,7 @@ vm_invoke_symbol_block(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, - static VALUE - vm_invoke_ifunc_block(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, - struct rb_calling_info *calling, const struct rb_call_info *ci, -- const struct rb_captured_block *captured) -+ int is_lambda, const struct rb_captured_block *captured) - { - VALUE val; - int argc; -@@ -3378,7 +3381,7 @@ vm_invoke_ifunc_block(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, - kw_splat = calling->kw_splat; - } - argc = calling->argc; -- val = vm_yield_with_cfunc(ec, captured, captured->self, argc, STACK_ADDR_FROM_TOP(argc), kw_splat, calling->block_handler, NULL); -+ val = vm_yield_with_cfunc(ec, captured, captured->self, argc, STACK_ADDR_FROM_TOP(argc), kw_splat, calling->block_handler, is_lambda, NULL); - POPN(argc); /* TODO: should put before C/yield? */ - return val; - } -@@ -3418,7 +3421,7 @@ vm_invoke_block(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, - case block_handler_type_ifunc: - { - const struct rb_captured_block *captured = VM_BH_TO_IFUNC_BLOCK(block_handler); -- return vm_invoke_ifunc_block(ec, reg_cfp, calling, ci, captured); -+ return vm_invoke_ifunc_block(ec, reg_cfp, calling, ci, is_lambda, captured); - } - case block_handler_type_proc: - is_lambda = block_proc_is_lambda(VM_BH_TO_PROC(block_handler)); - -From 286be5a98159ab45c9aadf10d21aef739be47b25 Mon Sep 17 00:00:00 2001 -From: Adam Procter -Date: Tue, 18 May 2021 14:47:45 -0700 -Subject: [PATCH 22/30] POPN before vm_yield_with_cfunc - ---- - vm_insnhelper.c | 12 +++++++++--- - 1 file changed, 9 insertions(+), 3 deletions(-) - -diff --git a/vm_insnhelper.c b/vm_insnhelper.c -index 10428817b3ff..fe29925833bd 100644 ---- a/vm_insnhelper.c -+++ b/vm_insnhelper.c -@@ -3219,7 +3219,7 @@ vm_yield_with_cfunc(rb_execution_context_t *ec, - self, - VM_GUARDED_PREV_EP(captured->ep), - (VALUE)me, -- 0, ec->cfp->sp, 0, 0); -+ 0, ec->cfp->sp + argc, 0, 0); - val = (*ifunc->func)(arg, (VALUE)ifunc->data, argc, argv, blockarg); - rb_vm_pop_frame(ec); - -@@ -3381,8 +3381,14 @@ vm_invoke_ifunc_block(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, - kw_splat = calling->kw_splat; - } - argc = calling->argc; -- val = vm_yield_with_cfunc(ec, captured, captured->self, argc, STACK_ADDR_FROM_TOP(argc), kw_splat, calling->block_handler, is_lambda, NULL); -- POPN(argc); /* TODO: should put before C/yield? */ -+ const VALUE *argv = STACK_ADDR_FROM_TOP(argc); -+ /* -+ * In sorbet_ruby we move this POPN above vm_yield_call_with_cfunc, so that -+ * if compiled code for a block uses EC_JUMP_TAG, the state will be in the -+ * expected state. -+ */ -+ POPN(argc); -+ val = vm_yield_with_cfunc(ec, captured, captured->self, argc, argv, kw_splat, calling->block_handler, is_lambda, NULL); - return val; - } - - -From 2a075c37925f7846d2c0ba3b3be0a5b8080c6af6 Mon Sep 17 00:00:00 2001 -From: Adam Procter -Date: Fri, 9 Jul 2021 16:04:36 -0700 -Subject: [PATCH 23/30] Only adjust the stack pointer in cases where POPN was - going to happen on the way out - ---- - vm.c | 4 ++-- - vm_insnhelper.c | 10 +++++++--- - 2 files changed, 9 insertions(+), 5 deletions(-) - -diff --git a/vm.c b/vm.c -index f1ba52b5f266..1d92bf6c4a47 100644 ---- a/vm.c -+++ b/vm.c -@@ -1138,7 +1138,7 @@ invoke_block_from_c_bh(rb_execution_context_t *ec, VALUE block_handler, - case block_handler_type_ifunc: - return vm_yield_with_cfunc(ec, VM_BH_TO_IFUNC_BLOCK(block_handler), - VM_BH_TO_IFUNC_BLOCK(block_handler)->self, -- argc, argv, kw_splat, passed_block_handler, is_lambda, NULL); -+ argc, argv, kw_splat, passed_block_handler, is_lambda, NULL, 0); - case block_handler_type_symbol: - return vm_yield_with_symbol(ec, VM_BH_TO_SYMBOL(block_handler), - argc, argv, kw_splat, passed_block_handler); -@@ -1219,7 +1219,7 @@ invoke_block_from_c_proc(rb_execution_context_t *ec, const rb_proc_t *proc, - argc--; - kw_splat = 2; - } -- return vm_yield_with_cfunc(ec, &block->as.captured, self, argc, argv, kw_splat, passed_block_handler, is_lambda, me); -+ return vm_yield_with_cfunc(ec, &block->as.captured, self, argc, argv, kw_splat, passed_block_handler, is_lambda, me, 0); - case block_type_symbol: - return vm_yield_with_symbol(ec, block->as.symbol, argc, argv, kw_splat, passed_block_handler); - case block_type_proc: -diff --git a/vm_insnhelper.c b/vm_insnhelper.c -index fe29925833bd..03a47b5885c2 100644 ---- a/vm_insnhelper.c -+++ b/vm_insnhelper.c -@@ -3182,7 +3182,7 @@ static VALUE - vm_yield_with_cfunc(rb_execution_context_t *ec, - const struct rb_captured_block *captured, - VALUE self, int argc, const VALUE *argv, int kw_splat, VALUE block_handler, -- int is_lambda, const rb_callable_method_entry_t *me) -+ int is_lambda, const rb_callable_method_entry_t *me, int sp_adjust) - { - /* is_lambda = FALSE; */ /* Unpatched VM hard-wires this to FALSE. We take it as an arg. */ - VALUE val, arg, blockarg; -@@ -3219,7 +3219,7 @@ vm_yield_with_cfunc(rb_execution_context_t *ec, - self, - VM_GUARDED_PREV_EP(captured->ep), - (VALUE)me, -- 0, ec->cfp->sp + argc, 0, 0); -+ 0, ec->cfp->sp + sp_adjust, 0, 0); - val = (*ifunc->func)(arg, (VALUE)ifunc->data, argc, argv, blockarg); - rb_vm_pop_frame(ec); - -@@ -3386,9 +3386,13 @@ vm_invoke_ifunc_block(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, - * In sorbet_ruby we move this POPN above vm_yield_call_with_cfunc, so that - * if compiled code for a block uses EC_JUMP_TAG, the state will be in the - * expected state. -+ * -+ * Note that we use sp_adjust (last arg of vm_yield_with_cfunc) to push the -+ * initial stack pointer of the callee frame past the arguments, since it -+ * still needs to be able to read them. - */ - POPN(argc); -- val = vm_yield_with_cfunc(ec, captured, captured->self, argc, argv, kw_splat, calling->block_handler, is_lambda, NULL); -+ val = vm_yield_with_cfunc(ec, captured, captured->self, argc, argv, kw_splat, calling->block_handler, is_lambda, NULL, argc); - return val; - } - - -From 8916b482974de925c3c7e0ef232384dfc73d691a Mon Sep 17 00:00:00 2001 -From: Adam Procter -Date: Tue, 20 Jul 2021 14:46:53 -0700 -Subject: [PATCH 24/30] Replace sp_adjust with a boolean flag; adjust by argc - if true - ---- - vm.c | 4 ++-- - vm_insnhelper.c | 9 +++++---- - 2 files changed, 7 insertions(+), 6 deletions(-) - -diff --git a/vm.c b/vm.c -index 1d92bf6c4a47..d52fd4a297fc 100644 ---- a/vm.c -+++ b/vm.c -@@ -1138,7 +1138,7 @@ invoke_block_from_c_bh(rb_execution_context_t *ec, VALUE block_handler, - case block_handler_type_ifunc: - return vm_yield_with_cfunc(ec, VM_BH_TO_IFUNC_BLOCK(block_handler), - VM_BH_TO_IFUNC_BLOCK(block_handler)->self, -- argc, argv, kw_splat, passed_block_handler, is_lambda, NULL, 0); -+ argc, argv, kw_splat, passed_block_handler, is_lambda, NULL, FALSE); - case block_handler_type_symbol: - return vm_yield_with_symbol(ec, VM_BH_TO_SYMBOL(block_handler), - argc, argv, kw_splat, passed_block_handler); -@@ -1219,7 +1219,7 @@ invoke_block_from_c_proc(rb_execution_context_t *ec, const rb_proc_t *proc, - argc--; - kw_splat = 2; - } -- return vm_yield_with_cfunc(ec, &block->as.captured, self, argc, argv, kw_splat, passed_block_handler, is_lambda, me, 0); -+ return vm_yield_with_cfunc(ec, &block->as.captured, self, argc, argv, kw_splat, passed_block_handler, is_lambda, me, FALSE); - case block_type_symbol: - return vm_yield_with_symbol(ec, block->as.symbol, argc, argv, kw_splat, passed_block_handler); - case block_type_proc: -diff --git a/vm_insnhelper.c b/vm_insnhelper.c -index 03a47b5885c2..cf39b11ff8c4 100644 ---- a/vm_insnhelper.c -+++ b/vm_insnhelper.c -@@ -3182,7 +3182,7 @@ static VALUE - vm_yield_with_cfunc(rb_execution_context_t *ec, - const struct rb_captured_block *captured, - VALUE self, int argc, const VALUE *argv, int kw_splat, VALUE block_handler, -- int is_lambda, const rb_callable_method_entry_t *me, int sp_adjust) -+ int is_lambda, const rb_callable_method_entry_t *me, bool args_on_stack) - { - /* is_lambda = FALSE; */ /* Unpatched VM hard-wires this to FALSE. We take it as an arg. */ - VALUE val, arg, blockarg; -@@ -3219,7 +3219,7 @@ vm_yield_with_cfunc(rb_execution_context_t *ec, - self, - VM_GUARDED_PREV_EP(captured->ep), - (VALUE)me, -- 0, ec->cfp->sp + sp_adjust, 0, 0); -+ 0, ec->cfp->sp + (args_on_stack ? argc : 0), 0, 0); - val = (*ifunc->func)(arg, (VALUE)ifunc->data, argc, argv, blockarg); - rb_vm_pop_frame(ec); - -@@ -3387,12 +3387,13 @@ vm_invoke_ifunc_block(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, - * if compiled code for a block uses EC_JUMP_TAG, the state will be in the - * expected state. - * -- * Note that we use sp_adjust (last arg of vm_yield_with_cfunc) to push the -+ * We pass "args_on_stack = TRUE" to vm_yield_with_cfunc to push the - * initial stack pointer of the callee frame past the arguments, since it - * still needs to be able to read them. - */ - POPN(argc); -- val = vm_yield_with_cfunc(ec, captured, captured->self, argc, argv, kw_splat, calling->block_handler, is_lambda, NULL, argc); -+ bool args_on_stack = TRUE; -+ val = vm_yield_with_cfunc(ec, captured, captured->self, argc, argv, kw_splat, calling->block_handler, is_lambda, NULL, args_on_stack); - return val; - } - - -From f567121ed09a6d9450a4d494ac52f1a5ae93e6cd Mon Sep 17 00:00:00 2001 -From: Adam Procter -Date: Wed, 11 Aug 2021 11:26:09 -0700 -Subject: [PATCH 25/30] Apply init-sorbet-t-modules.patch - ---- - inits.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/inits.c b/inits.c -index 79a6cf014ec8..8596e416d7b7 100644 ---- a/inits.c -+++ b/inits.c -@@ -77,6 +77,7 @@ rb_call_inits(void) - CALL(vm_trace); - CALL(pack); - CALL(warning); -+ CALL(Sorbet_T); - load_prelude(); - } - #undef CALL - -From 199da36596d186ad6c1abe9fdd9204e6688a4bd9 Mon Sep 17 00:00:00 2001 -From: Nathan Froyd -Date: Wed, 1 Sep 2021 16:59:03 -0400 -Subject: [PATCH 26/30] add crefs to sorbet methods - ---- - class.c | 4 ++-- - gc.c | 8 ++++++++ - method.h | 6 ++++-- - vm_insnhelper.c | 2 ++ - vm_method.c | 8 +++++--- - 5 files changed, 21 insertions(+), 7 deletions(-) - -diff --git a/class.c b/class.c -index 20def726c85b..7432a3e4f524 100644 ---- a/class.c -+++ b/class.c -@@ -1755,9 +1755,9 @@ rb_define_singleton_method(VALUE obj, const char *name, VALUE (*func)(ANYARGS), - } - - void --rb_define_singleton_sorbet_method(VALUE obj, const char *name, rb_sorbet_func_t func, const void *param, void *iseqptr) -+rb_define_singleton_sorbet_method(VALUE obj, const char *name, rb_sorbet_func_t func, const void *param, void *iseqptr, rb_cref_t *cref) - { -- rb_add_method_sorbet(singleton_class_of(obj), rb_intern(name), func, (const rb_sorbet_param_t *)param, METHOD_VISI_PUBLIC, iseqptr); -+ rb_add_method_sorbet(singleton_class_of(obj), rb_intern(name), func, (const rb_sorbet_param_t *)param, METHOD_VISI_PUBLIC, iseqptr, cref); - } - - #ifdef rb_define_module_function -diff --git a/gc.c b/gc.c -index e41d05706f61..9ec5463b1970 100644 ---- a/gc.c -+++ b/gc.c -@@ -4902,7 +4902,10 @@ mark_method_entry(rb_objspace_t *objspace, const rb_method_entry_t *me) - case VM_METHOD_TYPE_OPTIMIZED: - case VM_METHOD_TYPE_UNDEF: - case VM_METHOD_TYPE_NOTIMPLEMENTED: -+ break; - case VM_METHOD_TYPE_SORBET: -+ if (def->body.sorbet.iseqptr) gc_mark(objspace, (VALUE)def->body.sorbet.iseqptr); -+ gc_mark(objspace, (VALUE)def->body.sorbet.cref); - break; - } - } -@@ -8032,7 +8035,12 @@ gc_ref_update_method_entry(rb_objspace_t *objspace, rb_method_entry_t *me) - case VM_METHOD_TYPE_OPTIMIZED: - case VM_METHOD_TYPE_UNDEF: - case VM_METHOD_TYPE_NOTIMPLEMENTED: -+ break; - case VM_METHOD_TYPE_SORBET: -+ if (def->body.sorbet.iseqptr) { -+ TYPED_UPDATE_IF_MOVED(objspace, rb_iseq_t *, def->body.sorbet.iseqptr); -+ } -+ TYPED_UPDATE_IF_MOVED(objspace, rb_cref_t *, def->body.sorbet.cref); - break; - } - } -diff --git a/method.h b/method.h -index 75cc5785d25a..24a37a81112a 100644 ---- a/method.h -+++ b/method.h -@@ -209,6 +209,8 @@ typedef struct rb_method_sorbet_struct { - - const rb_sorbet_param_t *param; /* cf. rb_iseq_constant_body.param */ - rb_iseq_t *iseqptr; -+ -+ rb_cref_t * cref; /*!< class reference, should be marked */ - } rb_method_sorbet_t; - - typedef struct rb_method_attr_struct { -@@ -269,10 +271,10 @@ STATIC_ASSERT(sizeof_method_def, offsetof(rb_method_definition_t, body)==8); - UNDEFINED_METHOD_ENTRY_P((def)->body.refined.orig_me)) - - void rb_add_method_cfunc(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc, rb_method_visibility_t visi); --void rb_add_method_sorbet(VALUE klass, ID mid, rb_sorbet_func_t func, const rb_sorbet_param_t *param, rb_method_visibility_t visi, void *iseqptr); -+void rb_add_method_sorbet(VALUE klass, ID mid, rb_sorbet_func_t func, const rb_sorbet_param_t *param, rb_method_visibility_t visi, void *iseqptr, rb_cref_t *cref); - /* included so we don't expose singleton_class_of outside of class.c */ - /* we can't use rb_sorbet_func_t here because it's not exported */ --void rb_define_singleton_sorbet_method(VALUE, const char*, rb_sorbet_func_t, const void *, void *); -+void rb_define_singleton_sorbet_method(VALUE, const char*, rb_sorbet_func_t, const void *, void *, rb_cref_t *cref); - void rb_add_method_iseq(VALUE klass, ID mid, const rb_iseq_t *iseq, rb_cref_t *cref, rb_method_visibility_t visi); - void rb_add_refined_method_entry(VALUE refined_class, ID mid); - void rb_add_method(VALUE klass, ID mid, rb_method_type_t type, void *option, rb_method_visibility_t visi); -diff --git a/vm_insnhelper.c b/vm_insnhelper.c -index a891794d0a08..e4b05da569e7 100644 ---- a/vm_insnhelper.c -+++ b/vm_insnhelper.c -@@ -628,6 +628,8 @@ method_entry_cref(rb_callable_method_entry_t *me) - switch (me->def->type) { - case VM_METHOD_TYPE_ISEQ: - return me->def->body.iseq.cref; -+ case VM_METHOD_TYPE_SORBET: -+ return me->def->body.sorbet.cref; - default: - return NULL; - } -diff --git a/vm_method.c b/vm_method.c -index 948eedf35970..b65245481e5b 100644 ---- a/vm_method.c -+++ b/vm_method.c -@@ -171,12 +171,13 @@ rb_add_method_cfunc(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc, rb_me - } - - void --rb_add_method_sorbet(VALUE klass, ID mid, rb_sorbet_func_t func, const rb_sorbet_param_t *param, rb_method_visibility_t visi, void *iseqptr) -+rb_add_method_sorbet(VALUE klass, ID mid, rb_sorbet_func_t func, const rb_sorbet_param_t *param, rb_method_visibility_t visi, void *iseqptr, rb_cref_t *cref) - { - rb_method_sorbet_t opt; - opt.func = func; - opt.param = param; - opt.iseqptr = (rb_iseq_t *)iseqptr; -+ opt.cref = cref; - rb_add_method(klass, mid, VM_METHOD_TYPE_SORBET, &opt, visi); - } - -@@ -264,11 +265,12 @@ setup_method_cfunc_struct(rb_method_cfunc_t *cfunc, VALUE (*func)(), int argc) - } - - static void --setup_method_sorbet_struct(rb_method_sorbet_t *sorbet, rb_sorbet_func_t func, const rb_sorbet_param_t *param, rb_iseq_t *iseqptr) -+setup_method_sorbet_struct(rb_method_sorbet_t *sorbet, rb_sorbet_func_t func, const rb_sorbet_param_t *param, rb_iseq_t *iseqptr, rb_cref_t *cref) - { - sorbet->func = func; - sorbet->param = param; - sorbet->iseqptr = iseqptr; -+ sorbet->cref = cref; - } - - MJIT_FUNC_EXPORTED void -@@ -307,7 +309,7 @@ rb_method_definition_set(const rb_method_entry_t *me, rb_method_definition_t *de - case VM_METHOD_TYPE_SORBET: - { - rb_method_sorbet_t *sorbet = (rb_method_sorbet_t *)opts; -- setup_method_sorbet_struct(UNALIGNED_MEMBER_PTR(def, body.sorbet), sorbet->func, sorbet->param, sorbet->iseqptr); -+ setup_method_sorbet_struct(UNALIGNED_MEMBER_PTR(def, body.sorbet), sorbet->func, sorbet->param, sorbet->iseqptr, sorbet->cref); - return; - } - case VM_METHOD_TYPE_ATTRSET: - -From 9f6462569bc56f865b575c654f812d255c7bde4f Mon Sep 17 00:00:00 2001 -From: Adam Procter <71457435+aprocter-stripe@users.noreply.github.com> -Date: Fri, 10 Sep 2021 15:14:36 -0700 -Subject: [PATCH 27/30] Revert "add crefs to sorbet methods" (#2) - -This reverts commit 199da36596d186ad6c1abe9fdd9204e6688a4bd9. ---- - class.c | 4 ++-- - gc.c | 8 -------- - method.h | 6 ++---- - vm_insnhelper.c | 2 -- - vm_method.c | 8 +++----- - 5 files changed, 7 insertions(+), 21 deletions(-) - -diff --git a/class.c b/class.c -index 7432a3e4f524..20def726c85b 100644 ---- a/class.c -+++ b/class.c -@@ -1755,9 +1755,9 @@ rb_define_singleton_method(VALUE obj, const char *name, VALUE (*func)(ANYARGS), - } - - void --rb_define_singleton_sorbet_method(VALUE obj, const char *name, rb_sorbet_func_t func, const void *param, void *iseqptr, rb_cref_t *cref) -+rb_define_singleton_sorbet_method(VALUE obj, const char *name, rb_sorbet_func_t func, const void *param, void *iseqptr) - { -- rb_add_method_sorbet(singleton_class_of(obj), rb_intern(name), func, (const rb_sorbet_param_t *)param, METHOD_VISI_PUBLIC, iseqptr, cref); -+ rb_add_method_sorbet(singleton_class_of(obj), rb_intern(name), func, (const rb_sorbet_param_t *)param, METHOD_VISI_PUBLIC, iseqptr); - } - - #ifdef rb_define_module_function -diff --git a/gc.c b/gc.c -index 9ec5463b1970..e41d05706f61 100644 ---- a/gc.c -+++ b/gc.c -@@ -4902,10 +4902,7 @@ mark_method_entry(rb_objspace_t *objspace, const rb_method_entry_t *me) - case VM_METHOD_TYPE_OPTIMIZED: - case VM_METHOD_TYPE_UNDEF: - case VM_METHOD_TYPE_NOTIMPLEMENTED: -- break; - case VM_METHOD_TYPE_SORBET: -- if (def->body.sorbet.iseqptr) gc_mark(objspace, (VALUE)def->body.sorbet.iseqptr); -- gc_mark(objspace, (VALUE)def->body.sorbet.cref); - break; - } - } -@@ -8035,12 +8032,7 @@ gc_ref_update_method_entry(rb_objspace_t *objspace, rb_method_entry_t *me) - case VM_METHOD_TYPE_OPTIMIZED: - case VM_METHOD_TYPE_UNDEF: - case VM_METHOD_TYPE_NOTIMPLEMENTED: -- break; - case VM_METHOD_TYPE_SORBET: -- if (def->body.sorbet.iseqptr) { -- TYPED_UPDATE_IF_MOVED(objspace, rb_iseq_t *, def->body.sorbet.iseqptr); -- } -- TYPED_UPDATE_IF_MOVED(objspace, rb_cref_t *, def->body.sorbet.cref); - break; - } - } -diff --git a/method.h b/method.h -index 24a37a81112a..75cc5785d25a 100644 ---- a/method.h -+++ b/method.h -@@ -209,8 +209,6 @@ typedef struct rb_method_sorbet_struct { - - const rb_sorbet_param_t *param; /* cf. rb_iseq_constant_body.param */ - rb_iseq_t *iseqptr; -- -- rb_cref_t * cref; /*!< class reference, should be marked */ - } rb_method_sorbet_t; - - typedef struct rb_method_attr_struct { -@@ -271,10 +269,10 @@ STATIC_ASSERT(sizeof_method_def, offsetof(rb_method_definition_t, body)==8); - UNDEFINED_METHOD_ENTRY_P((def)->body.refined.orig_me)) - - void rb_add_method_cfunc(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc, rb_method_visibility_t visi); --void rb_add_method_sorbet(VALUE klass, ID mid, rb_sorbet_func_t func, const rb_sorbet_param_t *param, rb_method_visibility_t visi, void *iseqptr, rb_cref_t *cref); -+void rb_add_method_sorbet(VALUE klass, ID mid, rb_sorbet_func_t func, const rb_sorbet_param_t *param, rb_method_visibility_t visi, void *iseqptr); - /* included so we don't expose singleton_class_of outside of class.c */ - /* we can't use rb_sorbet_func_t here because it's not exported */ --void rb_define_singleton_sorbet_method(VALUE, const char*, rb_sorbet_func_t, const void *, void *, rb_cref_t *cref); -+void rb_define_singleton_sorbet_method(VALUE, const char*, rb_sorbet_func_t, const void *, void *); - void rb_add_method_iseq(VALUE klass, ID mid, const rb_iseq_t *iseq, rb_cref_t *cref, rb_method_visibility_t visi); - void rb_add_refined_method_entry(VALUE refined_class, ID mid); - void rb_add_method(VALUE klass, ID mid, rb_method_type_t type, void *option, rb_method_visibility_t visi); -diff --git a/vm_insnhelper.c b/vm_insnhelper.c -index e4b05da569e7..a891794d0a08 100644 ---- a/vm_insnhelper.c -+++ b/vm_insnhelper.c -@@ -628,8 +628,6 @@ method_entry_cref(rb_callable_method_entry_t *me) - switch (me->def->type) { - case VM_METHOD_TYPE_ISEQ: - return me->def->body.iseq.cref; -- case VM_METHOD_TYPE_SORBET: -- return me->def->body.sorbet.cref; - default: - return NULL; - } -diff --git a/vm_method.c b/vm_method.c -index b65245481e5b..948eedf35970 100644 ---- a/vm_method.c -+++ b/vm_method.c -@@ -171,13 +171,12 @@ rb_add_method_cfunc(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc, rb_me - } - - void --rb_add_method_sorbet(VALUE klass, ID mid, rb_sorbet_func_t func, const rb_sorbet_param_t *param, rb_method_visibility_t visi, void *iseqptr, rb_cref_t *cref) -+rb_add_method_sorbet(VALUE klass, ID mid, rb_sorbet_func_t func, const rb_sorbet_param_t *param, rb_method_visibility_t visi, void *iseqptr) - { - rb_method_sorbet_t opt; - opt.func = func; - opt.param = param; - opt.iseqptr = (rb_iseq_t *)iseqptr; -- opt.cref = cref; - rb_add_method(klass, mid, VM_METHOD_TYPE_SORBET, &opt, visi); - } - -@@ -265,12 +264,11 @@ setup_method_cfunc_struct(rb_method_cfunc_t *cfunc, VALUE (*func)(), int argc) - } - - static void --setup_method_sorbet_struct(rb_method_sorbet_t *sorbet, rb_sorbet_func_t func, const rb_sorbet_param_t *param, rb_iseq_t *iseqptr, rb_cref_t *cref) -+setup_method_sorbet_struct(rb_method_sorbet_t *sorbet, rb_sorbet_func_t func, const rb_sorbet_param_t *param, rb_iseq_t *iseqptr) - { - sorbet->func = func; - sorbet->param = param; - sorbet->iseqptr = iseqptr; -- sorbet->cref = cref; - } - - MJIT_FUNC_EXPORTED void -@@ -309,7 +307,7 @@ rb_method_definition_set(const rb_method_entry_t *me, rb_method_definition_t *de - case VM_METHOD_TYPE_SORBET: - { - rb_method_sorbet_t *sorbet = (rb_method_sorbet_t *)opts; -- setup_method_sorbet_struct(UNALIGNED_MEMBER_PTR(def, body.sorbet), sorbet->func, sorbet->param, sorbet->iseqptr, sorbet->cref); -+ setup_method_sorbet_struct(UNALIGNED_MEMBER_PTR(def, body.sorbet), sorbet->func, sorbet->param, sorbet->iseqptr); - return; - } - case VM_METHOD_TYPE_ATTRSET: - -From 46b9109c7ddcaa4e8bbc6b01af83357d966f4f43 Mon Sep 17 00:00:00 2001 -From: Nathan Froyd -Date: Wed, 29 Sep 2021 17:13:43 -0400 -Subject: [PATCH 28/30] pass the call data to sorbet method functions (#3) - ---- - method.h | 2 +- - vm_eval.c | 2 +- - vm_insnhelper.c | 2 +- - 3 files changed, 3 insertions(+), 3 deletions(-) - -diff --git a/method.h b/method.h -index 75cc5785d25a..294ed792843b 100644 ---- a/method.h -+++ b/method.h -@@ -199,7 +199,7 @@ typedef struct rb_sorbet_param_struct { - const ID *kw_table; - } rb_sorbet_param_t; - --typedef VALUE (*rb_sorbet_func_t)(int, VALUE *, VALUE, struct rb_control_frame_struct *); -+typedef VALUE (*rb_sorbet_func_t)(int, VALUE *, VALUE, struct rb_control_frame_struct *, void *); - - typedef struct rb_method_sorbet_struct { - /* cf. rb_method_cfunc_struct, but we only support one argument style */ -diff --git a/vm_eval.c b/vm_eval.c -index 52805d317f02..26d3d8906f3f 100644 ---- a/vm_eval.c -+++ b/vm_eval.c -@@ -144,7 +144,7 @@ vm_call0_sorbet_with_frame(rb_execution_context_t* ec, struct rb_calling_info *c - 0, reg_cfp->sp, sorbet->iseqptr->body->local_table_size, sorbet->iseqptr->body->stack_max); - - /* TODO: eventually we want to pass cd in here to assist with kwargs parsing */ -- val = (*sorbet->func)(argc, argv, recv, new_cfp); -+ val = (*sorbet->func)(argc, argv, recv, new_cfp, cd); - - CHECK_CFP_CONSISTENCY("vm_call0_sorbet_with_frame"); - rb_vm_pop_frame(ec); -diff --git a/vm_insnhelper.c b/vm_insnhelper.c -index a891794d0a08..05171ad04ca7 100644 ---- a/vm_insnhelper.c -+++ b/vm_insnhelper.c -@@ -2622,7 +2622,7 @@ vm_call_sorbet_with_frame_normal(rb_execution_context_t *ec, rb_control_frame_t - - reg_cfp->sp -= argc + 1; - /* TODO: eventually we want to pass cd in here to assist with kwargs parsing */ -- val = (*sorbet->func)(argc, reg_cfp->sp + 1, recv, new_cfp); -+ val = (*sorbet->func)(argc, reg_cfp->sp + 1, recv, new_cfp, cd); - - CHECK_CFP_CONSISTENCY("vm_call_sorbet"); - - -From 835f30277a15b0566fa0a23e33ccef4fde517243 Mon Sep 17 00:00:00 2001 -From: Nathan Froyd -Date: Tue, 5 Oct 2021 08:39:12 -0400 -Subject: [PATCH 29/30] pass rb_calling_info into sorbet method functions (#4) - ---- - method.h | 10 ++++++++-- - vm_eval.c | 3 +-- - vm_insnhelper.c | 3 +-- - 3 files changed, 10 insertions(+), 6 deletions(-) - -diff --git a/method.h b/method.h -index 294ed792843b..ff7f516c1e70 100644 ---- a/method.h -+++ b/method.h -@@ -199,7 +199,14 @@ typedef struct rb_sorbet_param_struct { - const ID *kw_table; - } rb_sorbet_param_t; - --typedef VALUE (*rb_sorbet_func_t)(int, VALUE *, VALUE, struct rb_control_frame_struct *, void *); -+/* The `void *` parameters are: -+ * -+ * - struct rb_calling_info * -+ * - struct rb_call_data * -+ * -+ * which we can't use here because they're not exported. -+ */ -+typedef VALUE (*rb_sorbet_func_t)(int, VALUE *, VALUE, struct rb_control_frame_struct *, void *, void *); - - typedef struct rb_method_sorbet_struct { - /* cf. rb_method_cfunc_struct, but we only support one argument style */ -@@ -271,7 +278,6 @@ STATIC_ASSERT(sizeof_method_def, offsetof(rb_method_definition_t, body)==8); - void rb_add_method_cfunc(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc, rb_method_visibility_t visi); - void rb_add_method_sorbet(VALUE klass, ID mid, rb_sorbet_func_t func, const rb_sorbet_param_t *param, rb_method_visibility_t visi, void *iseqptr); - /* included so we don't expose singleton_class_of outside of class.c */ --/* we can't use rb_sorbet_func_t here because it's not exported */ - void rb_define_singleton_sorbet_method(VALUE, const char*, rb_sorbet_func_t, const void *, void *); - void rb_add_method_iseq(VALUE klass, ID mid, const rb_iseq_t *iseq, rb_cref_t *cref, rb_method_visibility_t visi); - void rb_add_refined_method_entry(VALUE refined_class, ID mid); -diff --git a/vm_eval.c b/vm_eval.c -index 26d3d8906f3f..075096be8ec2 100644 ---- a/vm_eval.c -+++ b/vm_eval.c -@@ -143,8 +143,7 @@ vm_call0_sorbet_with_frame(rb_execution_context_t* ec, struct rb_calling_info *c - block_handler, (VALUE)me, - 0, reg_cfp->sp, sorbet->iseqptr->body->local_table_size, sorbet->iseqptr->body->stack_max); - -- /* TODO: eventually we want to pass cd in here to assist with kwargs parsing */ -- val = (*sorbet->func)(argc, argv, recv, new_cfp, cd); -+ val = (*sorbet->func)(argc, argv, recv, new_cfp, calling, cd); - - CHECK_CFP_CONSISTENCY("vm_call0_sorbet_with_frame"); - rb_vm_pop_frame(ec); -diff --git a/vm_insnhelper.c b/vm_insnhelper.c -index 05171ad04ca7..392278ee2a1a 100644 ---- a/vm_insnhelper.c -+++ b/vm_insnhelper.c -@@ -2621,8 +2621,7 @@ vm_call_sorbet_with_frame_normal(rb_execution_context_t *ec, rb_control_frame_t - 0, ec->cfp->sp, local_size, sorbet->iseqptr->body->stack_max); - - reg_cfp->sp -= argc + 1; -- /* TODO: eventually we want to pass cd in here to assist with kwargs parsing */ -- val = (*sorbet->func)(argc, reg_cfp->sp + 1, recv, new_cfp, cd); -+ val = (*sorbet->func)(argc, reg_cfp->sp + 1, recv, new_cfp, calling, cd); - - CHECK_CFP_CONSISTENCY("vm_call_sorbet"); - - -From 12d8c43330278a744d6e45135d1a8735f23f5afe Mon Sep 17 00:00:00 2001 -From: Nathan Froyd -Date: Wed, 13 Oct 2021 13:00:12 -0400 -Subject: [PATCH 30/30] add the ability to use interpreter-style keyword arg - parsing for compiled functions (#5) - ---- - vm_insnhelper.c | 308 ++++++++++++++++++++++++++++++++++++------------ - 1 file changed, 234 insertions(+), 74 deletions(-) - -diff --git a/vm_insnhelper.c b/vm_insnhelper.c -index 392278ee2a1a..f1d8966bb385 100644 ---- a/vm_insnhelper.c -+++ b/vm_insnhelper.c -@@ -2554,30 +2554,81 @@ vm_call_sorbet_simple_p(const rb_method_sorbet_t *sorbet) - sorbet->param->flags.has_block == FALSE; - } - --/* This call combines vm_call_iseq_optimizable_p and logic in vm_callee_setup_arg */ -+/* Return true if this method has kwargs than can be parsed efficiently by the -+ * compiler's kwarg parsing. */ - static bool -+vm_call_kwarg_simple_p(const rb_method_sorbet_t *sorbet) -+{ -+ const rb_sorbet_param_t *param = sorbet->param; -+ return param->flags.has_opt == FALSE && -+ param->flags.has_rest == FALSE && -+ param->flags.has_post == FALSE && -+ param->flags.has_kw == TRUE && -+ param->flags.has_kwrest == FALSE && -+ param->flags.has_block == FALSE; -+} -+ -+enum sorbet_method_opt_kind { -+ /* Not specially optimizable */ -+ SORBET_METHOD_OPT_NONE, -+ /* Only required parameters */ -+ SORBET_METHOD_OPT_REQ_PARAM_ONLY, -+ /* Keyword arguments without potential kwsplats */ -+ SORBET_METHOD_OPT_EFFICIENT_KWARGS, -+}; -+ -+/* This call combines vm_call_iseq_optimizable_p and logic in vm_callee_setup_arg */ -+static enum sorbet_method_opt_kind - vm_call_sorbet_optimizable_p(const struct rb_call_info *ci, const struct rb_call_cache *cc, -- const rb_method_sorbet_t *sorbet) -+ const struct rb_calling_info *calling, const rb_method_sorbet_t *sorbet) - { -- if (!vm_call_iseq_optimizable_p(ci, cc)) { -- return false; -+ /* Splat calls are generally not interesting, because they can introduce kwsplats -+ * and require extra processing anyway to resolve the splat. */ -+ if (IS_ARGS_SPLAT(ci)) { -+ return SORBET_METHOD_OPT_NONE; - } - -- /* vm_callee_setup_arg */ -- if (UNLIKELY(ci->flag & VM_CALL_KW_SPLAT)) { -- return false; -+ /* Similarly for keyword splats. */ -+ if (IS_ARGS_KW_SPLAT(ci)) { -+ return SORBET_METHOD_OPT_NONE; - } - -- /* We only handle simple calls to functions with positional args, unlike -- * vm_callee_setup_arg */ -- if (!vm_call_sorbet_simple_p(sorbet)) { -- return false; -+ /* Protected methods are weird, so don't deal with them. */ -+ if (METHOD_ENTRY_VISI(cc->me) == METHOD_VISI_PROTECTED) { -+ return SORBET_METHOD_OPT_NONE; - } - -- /* This callsite is to a method that only takes positional arguments. */ -- /* TODO: change this to handle more of the cases that vm_callee_setup_arg does, -- * like kwarg-only functions. */ -- return true; -+ /* Positional-argument-only methods are easy to optimize and very common. But -+ * make sure we're not providing keyword args here. */ -+ if (vm_call_sorbet_simple_p(sorbet) && !IS_ARGS_KEYWORD(ci)) { -+ return SORBET_METHOD_OPT_REQ_PARAM_ONLY; -+ } -+ -+ /* If we're calling a keyword-arg taking function that doesn't have other complex -+ * arguments, we can avoid turning the keyword args into a keyword splat. */ -+ if (vm_call_kwarg_simple_p(sorbet) && IS_ARGS_KEYWORD(ci)) { -+ /* Because Ruby keyword args can be rolled up into keyword splats to satisfy -+ * the last positional arg, the only case we can handle is when there are -+ * exactly the right number of positional args already. -+ * -+ * vm_callee_setup_arg has another case for no keyword args getting passed, -+ * but we don't handle that yet (and if we did handle it, we'd do it in the -+ * compiled code instead of in the VM, barring major changes to how the -+ * compiled code's argument parsing works). -+ */ -+ const int lead_num = sorbet->param->lead_num; -+ const int argc = calling->argc; -+ const struct rb_call_info_kw_arg *kw_arg = ((struct rb_call_info_with_kwarg *)ci)->kw_arg; -+ -+ if (argc - kw_arg->keyword_len == lead_num) { -+ return SORBET_METHOD_OPT_EFFICIENT_KWARGS; -+ } -+ -+ return SORBET_METHOD_OPT_NONE; -+ } -+ -+ /* We have something else that we haven't added an efficient case for. */ -+ return SORBET_METHOD_OPT_NONE; - } - - /* -- Remove empty_kw_splat In 3.0 -- */ -@@ -2586,10 +2637,10 @@ vm_call_sorbet_optimizable_p(const struct rb_call_info *ci, const struct rb_call - * Compare the ALWAYS_INLINE declaration on vm_call_iseq_setup_normal, which - * works on the same principles. - */ --ALWAYS_INLINE(static VALUE vm_call_sorbet_with_frame_normal(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, struct rb_call_data *cd, const rb_callable_method_entry_t *me, int check_kw_splat, int empty_kw_splat, int param_size, int local_size)); -+ALWAYS_INLINE(static VALUE vm_call_sorbet_with_frame_normal(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, struct rb_call_data *cd, const rb_callable_method_entry_t *me, int check_kw_splat, int empty_kw_splat, int argc, int param_size, int local_size)); - - static VALUE --vm_call_sorbet_with_frame_normal(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, struct rb_call_data *cd, const rb_callable_method_entry_t *me, int check_kw_splat, int empty_kw_splat, int param_size, int local_size) -+vm_call_sorbet_with_frame_normal(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, struct rb_call_data *cd, const rb_callable_method_entry_t *me, int check_kw_splat, int empty_kw_splat, int argc, int param_size, int local_size) - { - const struct rb_call_info *ci = &cd->ci; - VALUE val; -@@ -2602,7 +2653,6 @@ vm_call_sorbet_with_frame_normal(rb_execution_context_t *ec, rb_control_frame_t - * be accessed by Binding#local_variables and that need to be accessed by blocks/closures. - */ - VALUE frame_type = VM_FRAME_MAGIC_CFUNC | VM_ENV_FLAG_LOCAL; -- int argc = param_size; - - if (check_kw_splat) { - if (UNLIKELY(calling->kw_splat)) { -@@ -2620,7 +2670,7 @@ vm_call_sorbet_with_frame_normal(rb_execution_context_t *ec, rb_control_frame_t - block_handler, (VALUE)me, - 0, ec->cfp->sp, local_size, sorbet->iseqptr->body->stack_max); - -- reg_cfp->sp -= argc + 1; -+ reg_cfp->sp -= param_size + 1; - val = (*sorbet->func)(argc, reg_cfp->sp + 1, recv, new_cfp, calling, cd); - - CHECK_CFP_CONSISTENCY("vm_call_sorbet"); -@@ -2639,7 +2689,35 @@ vm_call_sorbet_with_frame(rb_execution_context_t *ec, rb_control_frame_t *reg_cf - const int check_kw_splat = 1; - const rb_callable_method_entry_t *me = cd->cc.me; - const rb_method_sorbet_t *sorbet = UNALIGNED_MEMBER_PTR(me->def, body.sorbet); -- return vm_call_sorbet_with_frame_normal(ec, reg_cfp, calling, cd, me, check_kw_splat, empty_kw_splat, calling->argc, sorbet->iseqptr->body->local_table_size); -+ const int argc = calling->argc; -+ const int param_size = calling->argc; -+ const int locals = sorbet->iseqptr->body->local_table_size; -+ return vm_call_sorbet_with_frame_normal(ec, reg_cfp, calling, cd, me, check_kw_splat, empty_kw_splat, argc, param_size, locals); -+} -+ -+static VALUE -+vm_call_sorbet_kwargs(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, struct rb_call_data *cd) -+{ -+ const struct rb_kwarg_call_data *kcd = (void *)cd; -+ const struct rb_call_info_with_kwarg *ci_kw = &kcd->ci_kw; -+ /* We have ensured that anything going through this function won't have kwsplats. */ -+ const int check_kw_splat = 0; -+ const int empty_kw_splat = 0; -+ const rb_callable_method_entry_t *me = cd->cc.me; -+ const rb_method_sorbet_t *sorbet = UNALIGNED_MEMBER_PTR(me->def, body.sorbet); -+ -+ /* Similarly to the above, we have ensured that anything going through this function -+ * won't have rest args, so we don't have to splat rest args via CALLER_SETUP_ARG. -+ * We also don't have to do the IS_ARGS_KEYWORD part of CALLER_SETUP_ARG, because -+ * we explicitly want to keep the interpreter-style of keyword arg passing. -+ * -+ * We do, however, need to adjust argc to make it reflect only the positional args -+ * being passed. -+ */ -+ const int argc = calling->argc - ci_kw->kw_arg->keyword_len; -+ const int param_size = calling->argc; -+ const int locals = sorbet->iseqptr->body->local_table_size; -+ return vm_call_sorbet_with_frame_normal(ec, reg_cfp, calling, cd, me, check_kw_splat, empty_kw_splat, argc, param_size, locals); - } - - static VALUE -@@ -2673,7 +2751,10 @@ vm_call_sorbet_fast_0params_0locals(rb_execution_context_t *ec, rb_control_frame - { - const int check_kw_splat = 0; - const int empty_kw_splat = 0; -- return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, 0, 0); -+ const int argc = 0; -+ const int params = 0; -+ const int locals = 0; -+ return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, argc, params, locals); - } - - static VALUE -@@ -2681,7 +2762,10 @@ vm_call_sorbet_fast_0params_1locals(rb_execution_context_t *ec, rb_control_frame - { - const int check_kw_splat = 0; - const int empty_kw_splat = 0; -- return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, 0, 1); -+ const int argc = 0; -+ const int params = 0; -+ const int locals = 1; -+ return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, argc, params, locals); - } - - static VALUE -@@ -2689,7 +2773,10 @@ vm_call_sorbet_fast_0params_2locals(rb_execution_context_t *ec, rb_control_frame - { - const int check_kw_splat = 0; - const int empty_kw_splat = 0; -- return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, 0, 2); -+ const int argc = 0; -+ const int params = 0; -+ const int locals = 2; -+ return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, argc, params, locals); - } - - static VALUE -@@ -2697,7 +2784,10 @@ vm_call_sorbet_fast_0params_3locals(rb_execution_context_t *ec, rb_control_frame - { - const int check_kw_splat = 0; - const int empty_kw_splat = 0; -- return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, 0, 3); -+ const int argc = 0; -+ const int params = 0; -+ const int locals = 3; -+ return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, argc, params, locals); - } - - static VALUE -@@ -2705,7 +2795,10 @@ vm_call_sorbet_fast_0params_4locals(rb_execution_context_t *ec, rb_control_frame - { - const int check_kw_splat = 0; - const int empty_kw_splat = 0; -- return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, 0, 4); -+ const int argc = 0; -+ const int params = 0; -+ const int locals = 4; -+ return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, argc, params, locals); - } - - static VALUE -@@ -2713,7 +2806,10 @@ vm_call_sorbet_fast_0params_5locals(rb_execution_context_t *ec, rb_control_frame - { - const int check_kw_splat = 0; - const int empty_kw_splat = 0; -- return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, 0, 5); -+ const int argc = 0; -+ const int params = 0; -+ const int locals = 5; -+ return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, argc, params, locals); - } - - static VALUE -@@ -2721,7 +2817,10 @@ vm_call_sorbet_fast_1params_0locals(rb_execution_context_t *ec, rb_control_frame - { - const int check_kw_splat = 0; - const int empty_kw_splat = 0; -- return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, 1, 0); -+ const int argc = 1; -+ const int params = 1; -+ const int locals = 0; -+ return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, argc, params, locals); - } - - static VALUE -@@ -2729,7 +2828,10 @@ vm_call_sorbet_fast_1params_1locals(rb_execution_context_t *ec, rb_control_frame - { - const int check_kw_splat = 0; - const int empty_kw_splat = 0; -- return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, 1, 1); -+ const int argc = 1; -+ const int params = 1; -+ const int locals = 1; -+ return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, argc, params, locals); - } - - static VALUE -@@ -2737,7 +2839,10 @@ vm_call_sorbet_fast_1params_2locals(rb_execution_context_t *ec, rb_control_frame - { - const int check_kw_splat = 0; - const int empty_kw_splat = 0; -- return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, 1, 2); -+ const int argc = 1; -+ const int params = 1; -+ const int locals = 2; -+ return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, argc, params, locals); - } - - static VALUE -@@ -2745,7 +2850,10 @@ vm_call_sorbet_fast_1params_3locals(rb_execution_context_t *ec, rb_control_frame - { - const int check_kw_splat = 0; - const int empty_kw_splat = 0; -- return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, 1, 3); -+ const int argc = 1; -+ const int params = 1; -+ const int locals = 3; -+ return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, argc, params, locals); - } - - static VALUE -@@ -2753,7 +2861,10 @@ vm_call_sorbet_fast_1params_4locals(rb_execution_context_t *ec, rb_control_frame - { - const int check_kw_splat = 0; - const int empty_kw_splat = 0; -- return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, 1, 4); -+ const int argc = 1; -+ const int params = 1; -+ const int locals = 4; -+ return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, argc, params, locals); - } - - static VALUE -@@ -2761,7 +2872,10 @@ vm_call_sorbet_fast_1params_5locals(rb_execution_context_t *ec, rb_control_frame - { - const int check_kw_splat = 0; - const int empty_kw_splat = 0; -- return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, 1, 5); -+ const int argc = 1; -+ const int params = 1; -+ const int locals = 5; -+ return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, argc, params, locals); - } - - static VALUE -@@ -2769,7 +2883,10 @@ vm_call_sorbet_fast_2params_0locals(rb_execution_context_t *ec, rb_control_frame - { - const int check_kw_splat = 0; - const int empty_kw_splat = 0; -- return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, 2, 0); -+ const int argc = 2; -+ const int params = 2; -+ const int locals = 0; -+ return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, argc, params, locals); - } - - static VALUE -@@ -2777,7 +2894,10 @@ vm_call_sorbet_fast_2params_1locals(rb_execution_context_t *ec, rb_control_frame - { - const int check_kw_splat = 0; - const int empty_kw_splat = 0; -- return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, 2, 1); -+ const int argc = 2; -+ const int params = 2; -+ const int locals = 1; -+ return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, argc, params, locals); - } - - static VALUE -@@ -2785,7 +2905,10 @@ vm_call_sorbet_fast_2params_2locals(rb_execution_context_t *ec, rb_control_frame - { - const int check_kw_splat = 0; - const int empty_kw_splat = 0; -- return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, 2, 2); -+ const int argc = 2; -+ const int params = 2; -+ const int locals = 2; -+ return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, argc, params, locals); - } - - static VALUE -@@ -2793,7 +2916,10 @@ vm_call_sorbet_fast_2params_3locals(rb_execution_context_t *ec, rb_control_frame - { - const int check_kw_splat = 0; - const int empty_kw_splat = 0; -- return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, 2, 3); -+ const int argc = 2; -+ const int params = 2; -+ const int locals = 3; -+ return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, argc, params, locals); - } - - static VALUE -@@ -2801,7 +2927,10 @@ vm_call_sorbet_fast_2params_4locals(rb_execution_context_t *ec, rb_control_frame - { - const int check_kw_splat = 0; - const int empty_kw_splat = 0; -- return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, 2, 4); -+ const int argc = 2; -+ const int params = 2; -+ const int locals = 4; -+ return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, argc, params, locals); - } - - static VALUE -@@ -2809,7 +2938,10 @@ vm_call_sorbet_fast_2params_5locals(rb_execution_context_t *ec, rb_control_frame - { - const int check_kw_splat = 0; - const int empty_kw_splat = 0; -- return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, 2, 5); -+ const int argc = 2; -+ const int params = 2; -+ const int locals = 5; -+ return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, argc, params, locals); - } - - static VALUE -@@ -2817,7 +2949,10 @@ vm_call_sorbet_fast_3params_0locals(rb_execution_context_t *ec, rb_control_frame - { - const int check_kw_splat = 0; - const int empty_kw_splat = 0; -- return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, 3, 0); -+ const int argc = 3; -+ const int params = 3; -+ const int locals = 0; -+ return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, argc, params, locals); - } - - static VALUE -@@ -2825,7 +2960,10 @@ vm_call_sorbet_fast_3params_1locals(rb_execution_context_t *ec, rb_control_frame - { - const int check_kw_splat = 0; - const int empty_kw_splat = 0; -- return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, 3, 1); -+ const int argc = 3; -+ const int params = 3; -+ const int locals = 1; -+ return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, argc, params, locals); - } - - static VALUE -@@ -2833,7 +2971,10 @@ vm_call_sorbet_fast_3params_2locals(rb_execution_context_t *ec, rb_control_frame - { - const int check_kw_splat = 0; - const int empty_kw_splat = 0; -- return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, 3, 2); -+ const int argc = 3; -+ const int params = 3; -+ const int locals = 2; -+ return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, argc, params, locals); - } - - static VALUE -@@ -2841,7 +2982,10 @@ vm_call_sorbet_fast_3params_3locals(rb_execution_context_t *ec, rb_control_frame - { - const int check_kw_splat = 0; - const int empty_kw_splat = 0; -- return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, 3, 3); -+ const int argc = 3; -+ const int params = 3; -+ const int locals = 3; -+ return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, argc, params, locals); - } - - static VALUE -@@ -2849,7 +2993,10 @@ vm_call_sorbet_fast_3params_4locals(rb_execution_context_t *ec, rb_control_frame - { - const int check_kw_splat = 0; - const int empty_kw_splat = 0; -- return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, 3, 4); -+ const int argc = 3; -+ const int params = 3; -+ const int locals = 4; -+ return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, argc, params, locals); - } - - static VALUE -@@ -2857,7 +3004,10 @@ vm_call_sorbet_fast_3params_5locals(rb_execution_context_t *ec, rb_control_frame - { - const int check_kw_splat = 0; - const int empty_kw_splat = 0; -- return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, 3, 5); -+ const int argc = 3; -+ const int params = 3; -+ const int locals = 5; -+ return vm_call_sorbet_with_frame_normal(ec, cfp, calling, cd, cd->cc.me, check_kw_splat, empty_kw_splat, argc, params, locals); - } - - static const vm_call_handler vm_call_sorbet_handlers[][6] = { -@@ -2908,42 +3058,52 @@ vm_call_sorbet_maybe_setup_fastpath(rb_execution_context_t *ec, rb_control_frame - const struct rb_call_info *ci = &cd->ci; - struct rb_call_cache *cc = &cd->cc; - const rb_method_sorbet_t *sorbet = UNALIGNED_MEMBER_PTR(cc->me->def, body.sorbet); -+ enum sorbet_method_opt_kind kind = vm_call_sorbet_optimizable_p(ci, cc, calling, sorbet); - -- /* Just take the normal path, we'll call vm_call_sorbet directly next time. */ -- if (!vm_call_sorbet_optimizable_p(ci, cc, sorbet)) { -+ /* vm_call_sorbet has already been set as the fastpath before we enter this -+ * function, so we're just trying to figure out if there's something even -+ * faster that we could use as the fastpath. If not, we can just call -+ * vm_call_sorbet here, and the fastpath code will be triggered next time. -+ */ -+ switch (kind) { -+ case SORBET_METHOD_OPT_NONE: - return vm_call_sorbet(ec, cfp, calling, cd); -- } -+ case SORBET_METHOD_OPT_EFFICIENT_KWARGS: -+ CC_SET_FASTPATH(cc, vm_call_sorbet_kwargs, TRUE); -+ return vm_call_sorbet_kwargs(ec, cfp, calling, cd); -+ case SORBET_METHOD_OPT_REQ_PARAM_ONLY: { -+ /* We know that the method we're calling takes only positional arguments. -+ * But we need to verify that the method is being passed only positional -+ * arguments and there aren't any kwarg fixups that we need to do. We -+ * only need to do this once, cf. vm_callee_setup_arg. -+ */ -+ CALLER_SETUP_ARG(cfp, calling, ci); -+ int empty_kw_splat = calling->kw_splat; -+ CALLER_REMOVE_EMPTY_KW_SPLAT(cfp, calling, ci); -+ if (empty_kw_splat && calling->kw_splat) { -+ empty_kw_splat = 0; -+ } - -- /* We know that the method we're calling takes only positional arguments. -- * But we need to verify that the method is being passed only positional -- * arguments and there aren't any kwarg fixups that we need to do. We -- * only need to do this once, cf. vm_callee_setup_arg. -- */ -- CALLER_SETUP_ARG(cfp, calling, ci); -- int empty_kw_splat = calling->kw_splat; -- CALLER_REMOVE_EMPTY_KW_SPLAT(cfp, calling, ci); -- if (empty_kw_splat && calling->kw_splat) { -- empty_kw_splat = 0; -- } -+ if (UNLIKELY(calling->argc != sorbet->param->lead_num)) { -+ /* vm_callee_setup_arg calls argument_arity_error, but our iseq is not -+ * set up in the way that function expects. We don't declare and call -+ * sorbet_raiseArity here because it's nice to have a Ruby with just -+ * the Sorbet calling convention patches applied be able to compile -+ * and run Ruby's testsuite. Instead, just call the function "normally" -+ * and let the argument checking in the function itself handle raising -+ * the error. -+ */ -+ return vm_call_sorbet_with_frame(ec, cfp, calling, cd, empty_kw_splat); -+ } - -- if (UNLIKELY(calling->argc != sorbet->param->lead_num)) { -- /* vm_callee_setup_arg calls argument_arity_error, but our iseq is not -- * set up in the way that function expects. We don't declare and call -- * sorbet_raiseArity here because it's nice to have a Ruby with just -- * the Sorbet calling convention patches applied be able to compile -- * and run Ruby's testsuite. Instead, just call the function "normally" -- * and let the argument checking in the function itself handle raising -- * the error. -+ /* vm_call_method_each_type has already set the fastpath to vm_call_sorbet, -+ * which handles all of the cases above. We've done all of those checks so that -+ * we know a different fastpath is available, which we set here. - */ -- return vm_call_sorbet_with_frame(ec, cfp, calling, cd, empty_kw_splat); -+ CC_SET_FASTPATH(cc, vm_call_sorbet_fast_func(ci, sorbet->param->size, sorbet->iseqptr->body->local_table_size), TRUE); -+ return vm_call_sorbet(ec, cfp, calling, cd); -+ } - } -- -- /* vm_call_method_each_type has already set the fastpath to vm_call_sorbet, -- * which handles all of the cases above. We've done all of those checks so that -- * we know a different fastpath is available, which we set here. -- */ -- CC_SET_FASTPATH(cc, vm_call_sorbet_fast_func(ci, sorbet->param->size, sorbet->iseqptr->body->local_table_size), TRUE); -- return vm_call_sorbet(ec, cfp, calling, cd); - } - - static VALUE diff --git a/third_party/ruby/thp.patch b/third_party/ruby/thp.patch new file mode 100644 index 0000000000..d647ed2c92 --- /dev/null +++ b/third_party/ruby/thp.patch @@ -0,0 +1,16 @@ +diff --git eval.c eval.c +index 844c537cc4..62ce1e142d 100644 +--- eval.c ++++ eval.c +@@ -77,7 +77,10 @@ ruby_setup(void) + * affect as many future pages as possible for CoW-friendliness + */ + #if defined(__linux__) && defined(PR_SET_THP_DISABLE) +- prctl(PR_SET_THP_DISABLE, 1, 0, 0, 0); ++ char *enable_thp = getenv("STRIPE_RUBY_ENABLE_THP"); ++ if (enable_thp == NULL || strncmp(enable_thp, "true", 4) != 0) { ++ prctl(PR_SET_THP_DISABLE, 1, 0, 0, 0); ++ } + #endif + Init_BareVM(); + Init_heap(); diff --git a/third_party/ruby_externals.bzl b/third_party/ruby_externals.bzl index 6a87cacbd0..d205eced28 100644 --- a/third_party/ruby_externals.bzl +++ b/third_party/ruby_externals.bzl @@ -5,16 +5,25 @@ def register_ruby_dependencies(): libyaml_version = "0.2.5" http_archive( name = "libyaml", - urls = _github_public_urls("yaml/libyaml/releases/download/{}/yaml-{}.tar.gz".format(libyaml_version, libyaml_version)), + url = "https://github.com/yaml/libyaml/releases/download/{}/yaml-{}.tar.gz".format(libyaml_version, libyaml_version), sha256 = "c642ae9b75fee120b2d96c712538bd2cf283228d2337df2cf2988e3c02678ef4", strip_prefix = "yaml-{}".format(libyaml_version), build_file = "@com_stripe_ruby_typer//third_party/ruby:libyaml.BUILD", ) + libffi_version = "3.4.5" + http_archive( + name = "libffi", + url = "https://github.com/libffi/libffi/releases/download/v{}/libffi-{}.tar.gz".format(libffi_version, libffi_version), + sha256 = "96fff4e589e3b239d888d9aa44b3ff30693c2ba1617f953925a70ddebcc102b2", + strip_prefix = "libffi-{}".format(libffi_version), + build_file = "@com_stripe_ruby_typer//third_party/ruby:libffi.BUILD", + ) + http_archive( name = "rules_rust", sha256 = "25209daff2ba21e818801c7b2dab0274c43808982d6aea9f796d899db6319146", - urls = _github_public_urls("bazelbuild/rules_rust/releases/download/0.21.1/rules_rust-v0.21.1.tar.gz"), + url = "https://github.com/bazelbuild/rules_rust/releases/download/0.21.1/rules_rust-v0.21.1.tar.gz", ) http_file( @@ -25,79 +34,50 @@ def register_ruby_dependencies(): http_file( name = "rubygems_update_stripe", + urls = _rubygems_urls("rubygems-update-3.5.4.gem"), + sha256 = "41d4c93a79426a7e034080cc367c696ee0ae5c26fcfef20bb58f950031c95924", + ) + + # Pre Ruby 3.0 needs an older rubygems + http_file( + name = "rubygems_update_stripe_ruby2", urls = _rubygems_urls("rubygems-update-3.3.3.gem"), sha256 = "610aef544e0c15ff3cd5492dff3f5f46bd2062896f4f62c7191432c6f1d681c9", ) ruby_build = "@com_stripe_ruby_typer//third_party/ruby:ruby.BUILD" - ruby_for_compiler_build = "@com_stripe_ruby_typer//third_party/ruby:ruby_for_compiler.BUILD" - - http_archive( - name = "sorbet_ruby_2_6", - urls = _ruby_urls("2.6/ruby-2.6.5.tar.gz"), - sha256 = "66976b716ecc1fd34f9b7c3c2b07bbd37631815377a2e3e85a5b194cfdcbed7d", - strip_prefix = "ruby-2.6.5", - build_file = ruby_build, - ) - - urls = _ruby_urls("2.7/ruby-2.7.2.tar.gz") - sha256 = "6e5706d0d4ee4e1e2f883db9d768586b4d06567debea353c796ec45e8321c3d4" - strip_prefix = "ruby-2.7.2" - - http_archive( - name = "sorbet_ruby_2_7_unpatched", - urls = urls, - sha256 = sha256, - strip_prefix = strip_prefix, - build_file = ruby_build, - ) - - http_archive( - name = "sorbet_ruby_2_7", - urls = urls, - sha256 = sha256, - strip_prefix = strip_prefix, - build_file = ruby_build, - patches = [ - "@com_stripe_ruby_typer//third_party/ruby:gc-remove-write-barrier.patch", - "@com_stripe_ruby_typer//third_party/ruby:dtoa.patch", - "@com_stripe_ruby_typer//third_party/ruby:penelope_procc.patch", - "@com_stripe_ruby_typer//third_party/ruby:gc-fix-malloc-increase-calculation.patch", # https://github.com/ruby/ruby/pull/4860 - "@com_stripe_ruby_typer//third_party/ruby:gc-add-need-major-by.patch", # https://github.com/ruby/ruby/pull/6791 - ], - ) - - http_archive( - name = "sorbet_ruby_2_7_for_compiler", - urls = urls, - sha256 = sha256, - strip_prefix = strip_prefix, - build_file = ruby_for_compiler_build, - patches = [ - "@com_stripe_ruby_typer//third_party/ruby:sorbet_ruby_2_7_for_compiler.patch", - "@com_stripe_ruby_typer//third_party/ruby:dtoa-p1.patch", - ], - patch_tool = "patch", - patch_args = ["-p1"], - ) + ruby_3_3_build = "@com_stripe_ruby_typer//third_party/ruby:ruby_3_3.BUILD" http_archive( name = "sorbet_ruby_3_1", - urls = _ruby_urls("3.1/ruby-3.1.4.tar.gz"), + url = "https://cache.ruby-lang.org/pub/ruby/3.1/ruby-3.1.4.tar.gz", sha256 = "a3d55879a0dfab1d7141fdf10d22a07dbf8e5cdc4415da1bde06127d5cc3c7b6", strip_prefix = "ruby-3.1.4", build_file = "@com_stripe_ruby_typer//third_party/ruby:ruby.BUILD", patches = [ "@com_stripe_ruby_typer//third_party/ruby:gc-add-need-major-by-3_1.patch", # https://github.com/ruby/ruby/pull/6791 + "@com_stripe_ruby_typer//third_party/ruby:thp.patch", + "@com_stripe_ruby_typer//third_party/ruby:gc-t-none-context.patch", + "@com_stripe_ruby_typer//third_party/ruby:gc-more-t-none-context.patch", + "@com_stripe_ruby_typer//third_party/ruby:gc-write-barrier-cme.patch", ], ) http_archive( - name = "sorbet_ruby_3_2", - urls = _ruby_urls("3.2/ruby-3.2.2.tar.gz"), - sha256 = "96c57558871a6748de5bc9f274e93f4b5aad06cd8f37befa0e8d94e7b8a423bc", - strip_prefix = "ruby-3.2.2", - build_file = ruby_build, + name = "sorbet_ruby_3_3", + url = "https://cache.ruby-lang.org/pub/ruby/3.3/ruby-3.3.3.tar.gz", + sha256 = "83c05b2177ee9c335b631b29b8c077b4770166d02fa527f3a9f6a40d13f3cce2", + strip_prefix = "ruby-3.3.3", + build_file = ruby_3_3_build, + patch_tool = "patch", + patch_args = ["-p1"], + patches = [ + "@com_stripe_ruby_typer//third_party/ruby:ldflags.patch", + "@com_stripe_ruby_typer//third_party/ruby:10151_no_hash_allocate_static_kwargs_3_3_only.patch", + "@com_stripe_ruby_typer//third_party/ruby:10306_no_hash_allocate_static_kwargs_3_3_only.patch", + "@com_stripe_ruby_typer//third_party/ruby:reinit_native_sched_lock.patch", + "@com_stripe_ruby_typer//third_party/ruby:10899-avoid-unnecessary-writes-in-gc-marking.patch", + ], ) def _rubygems_urls(gem): @@ -108,21 +88,3 @@ def _rubygems_urls(gem): "https://rubygems.org/downloads/{}".format(gem), "https://artifactory-content.stripe.build/artifactory/gems/gems/{}".format(gem), ] - -def _ruby_urls(path): - """ - Produce a url list that works both with ruby-lang.org, and stripe's internal artifact cache. - """ - return [ - "https://cache.ruby-lang.org/pub/ruby/{}".format(path), - "https://artifactory-content.stripe.build/artifactory/ruby-lang-cache/pub/ruby/{}".format(path), - ] - -def _github_public_urls(path): - """ - Produce a url list that works both with github, and stripe's internal artifact cache. - """ - return [ - "https://github.com/{}".format(path), - "https://artifactory-content.stripe.build/artifactory/github-archives/{}".format(path), - ] diff --git a/tools/BUILD b/tools/BUILD index 6e1bcd3fb8..df79f8a16a 100644 --- a/tools/BUILD +++ b/tools/BUILD @@ -6,8 +6,6 @@ clang_tool("clang-format") clang_tool("clang-tidy") -clang_tool("opt") - compilation_database( name = "compdb", testonly = True, @@ -39,18 +37,6 @@ compilation_database( "//common/strings:strings", "//common/timers:timers", "//common/web_tracer_framework:tracing", - # "//compiler:sorbet", - # "//compiler/Core:Core", - # "//compiler/Errors:Errors", - # "//compiler/IREmitter:IREmitter", - # "//compiler/IREmitter/Payload:Payload", - # "//compiler/IREmitter/Payload:generate_load_payload", - # "//compiler/IREmitter/Payload:postprocess_payload", - # "//compiler/IREmitter/Payload:pseudo_lib", - # "//compiler/IREmitter/Payload:vm_payload_for_compile_commands", - # "//compiler/Linker:Linker", - # "//compiler/ObjectFileEmitter:ObjectFileEmitter", - # "//compiler/Passes:Passes", "//core:core", "//core:core_test", "//core:generate_names", @@ -59,8 +45,10 @@ compilation_database( "//core/serialize:serialize", "//core/serialize:serialize_test", "//core/sig_finder:sig_finder", + "//core/source_generator:source_generator", "//definition_validator:definition_validator", - "//emscripten:main", + "//emscripten:sorbet-wasm", + "//emscripten:sorbet-wasm.d", "//gems/sorbet-runtime:generate_call_validation", "//hashing:hashing", "//hashing:hashing-orig", @@ -110,11 +98,8 @@ compilation_database( "//payload/text:generate_payload", "//payload/text:some", "//payload/text:text", - "//plugin_injector:plugin_injector", "//scip_indexer:scip_utils", "//scip_indexer:scip_indexer", - "//proto:proto", - "//proto/pay-server:pay-server", "//rbi:generate_procs", "//resolver:resolver", "//resolver/type_syntax:type_syntax", @@ -130,11 +115,11 @@ compilation_database( "//test:scip_test_runner", "//test:pkg-autocorrects-test", "//test:print_document_symbols", + "//test:single_package_runner_cc", "//test/fuzz:fuzz_dash_e", "//test/fuzz:fuzz_dash_e_impl", "//test/fuzz:fuzz_doc_symbols", "//test/fuzz:fuzz_hover", - "//test/fuzz:proto", "//test/helpers:helpers", "//test/lsp:cache_protocol_test_corpus", "//test/lsp:multithreaded_protocol_test_corpus", diff --git a/tools/buildstamp/get_workspace_status b/tools/buildstamp/get_workspace_status index f967d87a23..a561fb0a37 100755 --- a/tools/buildstamp/get_workspace_status +++ b/tools/buildstamp/get_workspace_status @@ -38,7 +38,7 @@ git_commit_count=$(git rev-list --count HEAD) echo "STABLE_BUILD_SCM_REVISION ${git_rev}" echo "STABLE_BUILD_SCM_COMMIT_COUNT ${git_commit_count}" -# Check whether there are any uncommited changes +# Check whether there are any uncommitted changes if git diff-index --quiet HEAD --; then clean="1" else diff --git a/tools/clang.bzl b/tools/clang.bzl index 1b0e856ae7..6596a1d466 100644 --- a/tools/clang.bzl +++ b/tools/clang.bzl @@ -30,6 +30,6 @@ _clang_tool = rule( def clang_tool(name): _clang_tool( name = name, - tool = "@llvm_toolchain_12_0_0//:bin/" + name, + tool = "@llvm_toolchain_15_0_6//:bin/" + name, visibility = ["//visibility:public"], ) diff --git a/tools/config/BUILD b/tools/config/BUILD index 61d9209a0b..54bc5a109b 100644 --- a/tools/config/BUILD +++ b/tools/config/BUILD @@ -46,23 +46,11 @@ config_setting( }, ) -config_setting( - name = "webasm", - values = { - "cpu": "webasm", - }, -) - -config_setting( - name = "webasm_opt", - values = { - "cpu": "webasm", - "compilation_mode": "opt", - }, -) - config_setting( name = "linkshared", + constraint_values = [ + "@platforms//os:linux", + ], values = { "define": "linkshared=true", }, diff --git a/tools/scripts/build_compilation_db.sh b/tools/scripts/build_compilation_db.sh index d6eceec286..6bc3ea3490 100755 --- a/tools/scripts/build_compilation_db.sh +++ b/tools/scripts/build_compilation_db.sh @@ -4,12 +4,6 @@ set -e cd "$(dirname "$0")/../.." -if [ "$(uname -s)-$(uname -m)" = "Darwin-arm64" ]; then - >&2 echo "Note: building the full compile_commands.json file is not yet supported on Apple Silicon machines." - >&2 echo "For rudimentary support, comment out all //compiler: targets in tools/BUILD and re-run this script." - >&2 echo -fi - ./bazel build //tools:compdb --config=dbg if command -v jq &> /dev/null; then diff --git a/tools/scripts/fuzz.sh b/tools/scripts/fuzz.sh index b83f22476b..76dfc0485c 100755 --- a/tools/scripts/fuzz.sh +++ b/tools/scripts/fuzz.sh @@ -28,7 +28,7 @@ echo "building $target" ./bazel build "//test/fuzz:$target" --config=fuzz -c opt # we want the bazel build command to run before this check so that bazel can download itself. -export PATH="$PATH:$PWD/bazel-sorbet/external/llvm_toolchain_12_0_0/bin" +export PATH="$PATH:$PWD/bazel-sorbet/external/llvm_toolchain_15_0_7/bin" if ! command -v llvm-symbolizer >/dev/null; then echo "fatal: command not found: llvm-symbolizer" exit 1 diff --git a/tools/scripts/fuzz_minimize_crash.sh b/tools/scripts/fuzz_minimize_crash.sh index 74513e796b..84231cee50 100755 --- a/tools/scripts/fuzz_minimize_crash.sh +++ b/tools/scripts/fuzz_minimize_crash.sh @@ -72,7 +72,7 @@ handle_TERM() { trap handle_INT SIGINT trap handle_TERM SIGTERM -export PATH="$PATH:$PWD/bazel-sorbet/external/llvm_toolchain_12_0_0/bin" +export PATH="$PATH:$PWD/bazel-sorbet/external/llvm_toolchain_15_0_7/bin" if ! command -v llvm-symbolizer >/dev/null; then echo "fatal: command not found: llvm-symbolizer" exit 1 diff --git a/tools/scripts/ll-view.sh b/tools/scripts/ll-view.sh deleted file mode 100755 index 95ff35c0fb..0000000000 --- a/tools/scripts/ll-view.sh +++ /dev/null @@ -1,121 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -green=$'\x1b[0;32m' -cyan=$'\x1b[0;36m' -cnone=$'\x1b[0m' - -__use_color= -if [ -t 1 ]; then - __use_color=1 -fi - -# Detects whether we can add colors or not -in_color() { - local color="$1" - shift - - if [ -z "$__use_color" ]; then - echo "$*" - else - echo "$color$*$cnone" - fi -} - -usage() { - cat < - -Arguments: - The file with the LLVM IR. -EOF -} - -# ----- option parsing -------------------------------------------------------- - -llvm_ir_file= -while [[ $# -gt 0 ]]; do - case $1 in - -h|--help) - usage - exit - ;; - -*) - 1>&2 echo "error: Unrecoginzed argument: $1" - 1>&1 usage - exit 1 - ;; - *) - if [ "$llvm_ir_file" != "" ]; then - 1>&2 echo "error: Can only pass a single file" - 1>&2 usage - exit 1 - fi - llvm_ir_file="$1" - shift - ;; - esac -done - -if [ "$llvm_ir_file" = "" ]; then - 1>&2 echo "error: Missing " - 1>&2 usage - exit 1 -fi - -if ! command -v realpath &> /dev/null; then - 1>&2 echo "error: ll-view.sh requires GNU realpath" - exit 1 -fi - -if ! command -v dot &> /dev/null; then - 1>&2 echo "error: ll-view.sh requires dot (from graphviz)" - exit 1 -fi - -project_root="$(realpath "$( dirname "${BASH_SOURCE[0]}" )/../..")" - -# ----- main ------------------------------------------------------------------ - -out_folder="$project_root/ll-view.out" -rm -rf "$out_folder" -mkdir -p "$out_folder" - -input_file="$out_folder/input.ll" -cp "$llvm_ir_file" "$input_file" - -# We could use bazel run here, but it's more convenient to use whatever happens -# to have been built last, so that we don't have to potentially rebuild LLVM. -llvm_opt="$project_root/bazel-sorbet/external/llvm_toolchain_12_0_0/bin/opt" - -if ! [ -x "$llvm_opt" ]; then - # ... but if nothing was built yet, at least build something. - bazel build -c opt //tools:opt -fi - -( - cd "$out_folder" - # /dev/null silences the LLVM bitcode output warning. - # We could put it in a file, but we don't care about it. - "$llvm_opt" -dot-cfg "$input_file" > /dev/null -) - -i=0 -for dot_file in "$out_folder"/.*.dot; do - dot_basename="$(basename "$dot_file" ".dot")" - # .foo -> foo - svg_basename="${dot_basename:1}" - svg_file="$out_folder/$svg_basename.svg" - dot -Tsvg "$dot_file" > "$svg_file" - i=$((i+1)) -done - -echo -echo "Created $i *.dot and *.svg files in:" -in_color "$cyan" " ./$(realpath --relative-to "$PWD" "$out_folder")" -in_color "$green" "Done." diff --git a/tools/scripts/make_worktree.sh b/tools/scripts/make_worktree.sh deleted file mode 100755 index c62532f5ef..0000000000 --- a/tools/scripts/make_worktree.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -if [ $# -eq 0 ]; then - echo "usage: $0 " - exit 1 -fi - -worktree_name=$1 -mkdir -p "$HOME/.cache/sorbet/_bazel_$worktree_name" -git worktree add "../$worktree_name" "HEAD@{0}" -cd "../$worktree_name" -echo "startup --output_base=$HOME/.cache/sorbet/_bazel_$worktree_name" >> .bazelrc.local - -echo "Created worktree at ../$worktree_name" -echo "Your first build in this worktree will be a little slow," -echo "but other than that, we're done!" diff --git a/tools/scripts/regen-emscripten-cache.sh b/tools/scripts/regen-emscripten-cache.sh deleted file mode 100755 index 5e21337d49..0000000000 --- a/tools/scripts/regen-emscripten-cache.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -set -e - -cd "$(dirname "$0")/../.." - -./bazel build //tools/toolchain/webasm-darwin:generate_em_cache -cp bazel-genfiles/tools/toolchain/webasm-darwin/em_cache.tar.gz tools/toolchain/webasm-darwin/em_cache_existing.tar.gz diff --git a/tools/scripts/run-benchmarks.rb b/tools/scripts/run-benchmarks.rb deleted file mode 100644 index dc96b17669..0000000000 --- a/tools/scripts/run-benchmarks.rb +++ /dev/null @@ -1,237 +0,0 @@ -# frozen_string_literal: true -# typed: strict - -require 'benchmark' -require 'fileutils' -require 'optparse' -require_relative '../../gems/sorbet-runtime/lib/sorbet-runtime' - -class Module - include T::Sig -end - -class BenchmarkCommand < T::Struct - prop :argv, T::Array[String] - prop :dir, String - prop :env, T::Hash[String, T.untyped], factory: -> {{}} -end - -class BenchmarkResult < T::Struct - const :benchmark, String - const :shortname, String - const :interpreted_time, Float - const :compiled_time, Float -end - -class SuiteResult < T::Struct - const :baseline, T.nilable(BenchmarkResult) - const :benchmarks, T::Array[BenchmarkResult] -end - -module SorbetBenchmark - - sig {params(command: T::Array[String], env: T::Hash[String, T.untyped], - chdir: T.nilable(String), out: T.nilable(String), err: T.nilable(String)).void} - def self.check_call(command, env: {}, chdir: nil, out: nil, err: nil) - opts = {} - if chdir - opts[:chdir] = chdir - end - if out - opts[:out] = out - end - if err - opts[:err] = err - end - - pid = Process.spawn(env, *command, opts) - Process.wait(pid) - status = $? - if status.to_i != 0 - raise "#{command.join(" ")} exited with #{status.to_i}" - end - end - - sig {params(benchmark: String).void} - def self.compile(benchmark) - FileUtils.rm_rf(Dir.glob('tmp/bench/*')) - - target = 'tmp/bench/target.rb' - FileUtils.cp(benchmark, target) - check_call(['bash', 'test/run_sorbet.sh', target], env: {'llvmir' => '.', 'compiled_out_dir' => '.'}, out: "/dev/null", err: "/dev/null") - end - - sig {params(ruby: String, sorbet: String).returns(T::Array[String])} - def self.basic_ruby(ruby, sorbet) - return [ruby, "--disable=gems", "--disable=did_you_mean", - "-r", "rubygems", - "-r", "#{sorbet}/gems/sorbet-runtime/lib/sorbet-runtime.rb"] - end - - sig {params(ruby: String, sorbet: String).returns(BenchmarkCommand)} - def self.startup_command(ruby, sorbet) - BenchmarkCommand.new( - argv: [*basic_ruby(ruby, sorbet), "-e", "1"], - dir: 'tmp/bench', - ) - end - - sig {params(ruby: String, sorbet: String).returns(BenchmarkCommand)} - def self.interpreted_command(ruby, sorbet) - BenchmarkCommand.new( - argv: [*basic_ruby(ruby, sorbet), "./target.rb"], - dir: 'tmp/bench', - ) - end - - sig {params(topdir: String, ruby: String, sorbet: String).returns(BenchmarkCommand)} - def self.compiled_command(topdir, ruby, sorbet) - BenchmarkCommand.new( - argv: [*basic_ruby(ruby, sorbet), - "-r", "#{topdir}/test/patch_require.rb", - "-e", "$__sorbet_ruby_realpath='target.rb'; require './target.rb.so'"], - dir: 'tmp/bench', - env: {'llvmir' => '.'}, - ) - end - - sig {params(command: BenchmarkCommand).returns(Float)} - def self.measure(command) - Benchmark.realtime do - check_call(command.argv, env: command.env, chdir: command.dir, - out: "/dev/null", err: "/dev/null") - end - end - - sig {params(command: BenchmarkCommand, n_runs: Integer).returns(Float)} - def self.average_runtime(command, n_runs: 10) - n_runs.times.map do |_| - measure(command) - end.reduce(0.0, :+) / n_runs - end - - sig {params(benchmark: String, interpreted_time: Float, compiled_time: Float, delimiter: String).void} - def self.report(benchmark, interpreted_time, compiled_time, delimiter) - puts "%s#{delimiter}%.3f#{delimiter}%.3f".%( - benchmark: benchmark, - interpreted: interpreted_time, - compiled: compiled_time, - ) - end - - sig {params(rootdir: String, benchmark: String, ruby: String, sorbet: String).returns(BenchmarkResult)} - def self.compare_interpreted_vs_compiled(rootdir, benchmark, ruby, sorbet) - compile(benchmark) - interpreted = average_runtime(interpreted_command(ruby, sorbet)) - compiled = average_runtime(compiled_command(rootdir, ruby, sorbet)) - BenchmarkResult.new( - benchmark: benchmark, - shortname: benchmark.sub(%r{^test/testdata/ruby_benchmark/}, ''), - interpreted_time: interpreted, - compiled_time: compiled, - ) - end - - sig {params(topdir: String, benchmarks: T::Array[String], baseline: T.nilable(String), verbose: T::Boolean).returns(SuiteResult)} - def self.gather_results(topdir, benchmarks, baseline, verbose) - Dir.chdir(topdir) - - sorbet_ruby_target = "@sorbet_ruby_2_7_for_compiler//:ruby" - - check_call(["./bazel", "build", "//compiler:sorbet", sorbet_ruby_target, "-c", "opt"]) - check_call(["./bazel", "run", sorbet_ruby_target, "-c", "opt", "--", "--version"]) - - FileUtils.mkdir_p('tmp/bench') - - pwd = Dir.pwd - - ruby = "#{pwd}/bazel-bin/external/sorbet_ruby_2_7_for_compiler/ruby" - sorbet = "#{pwd}" - - startup_time = average_runtime(startup_command(ruby, sorbet)) - puts "ruby vm startup time: %.3f" % startup_time - - baseline_result = nil - - if baseline - baseline_result = compare_interpreted_vs_compiled(pwd, baseline, ruby, sorbet) - end - - benchmark_results = benchmarks.map do |b| - compare_interpreted_vs_compiled(pwd, b, ruby, sorbet) - end - - SuiteResult.new( - baseline: baseline_result, - benchmarks: benchmark_results, - ) - end - - sig {params(topdir: String, benchmarks: T::Array[String], delimiter: String, baseline: T.nilable(String), verbose: T::Boolean).void} - def self.run(topdir:, benchmarks:, delimiter:, baseline:, verbose:) - suite = gather_results(topdir, benchmarks, baseline, verbose) - - baseline_result = suite.baseline - baseline_interpreted = 0.0 - baseline_compiled = 0.0 - - if baseline_result - baseline_interpreted = baseline_result.interpreted_time - baseline_compiled = baseline_result.compiled_time - report(baseline_result.shortname, baseline_interpreted, baseline_compiled, delimiter) - end - - suite.benchmarks.each do |result| - report(result.shortname, result.interpreted_time, result.compiled_time, delimiter) - - if baseline_result - report("#{result.shortname} - baseline", - result.interpreted_time - baseline_interpreted, - result.compiled_time - baseline_compiled, delimiter) - end - end - end -end - -if __FILE__ == $0 - topdir = File.dirname($0) + '/../..' - - baseline = nil - delimiter = "\t" - benchmarks = [] - verbose = false - - OptionParser.new do |opts| - - opts.banner = "Usage: run-benchmarks.rb [options] -f [benchmark1] -f [benchmark2] ..." - - opts.on '-b PATH', '--baseline=PATH', 'Path to a baseline Ruby benchmark to subtract out' do |path| - baseline = path - end - - opts.on '-d STRING', '--delimiter=STRING', 'Delimiter character' do |d| - delimiter = d - end - - opts.on '-f PATH', '--file=PATH', 'Path to a Ruby benchmark' do |path| - benchmarks.push(path) - end - - opts.on '-v' do - verbose = true - end - - end.parse! - - if benchmarks.empty? - if baseline - raise "-b is not supported with no provided benchmarks!" - end - - benchmarks = Dir.glob('test/testdata/ruby_benchmark/**/*.rb').filter do |filename| - filename.include?('disabled') || filename.include?('too_slow') - end.sort - end - - SorbetBenchmark.run(topdir: topdir, benchmarks: benchmarks, delimiter: delimiter, baseline: baseline, verbose: verbose) -end diff --git a/tools/scripts/run_benchmarks.sh b/tools/scripts/run_benchmarks.sh deleted file mode 100755 index d0118853a9..0000000000 --- a/tools/scripts/run_benchmarks.sh +++ /dev/null @@ -1,210 +0,0 @@ -#!/bin/bash - -set -euo pipefail - -usage() { - cat <.rb] -f .rb ...] - -Options: - -f .rb Run benchmark specified in .rb via the - interpreter and via the compiled output. May be repeated - to run multiple benchmarks. If omitted, runs all files in - test/testdata/ruby_benchmark/**/*.rb, except those that - are disabled or too slow. - - -b .rb Run benchmark .rb, and treat it like the - baseline (subtract its time from all other benchmarks) - - -d Use to separate columns of output. - Suggestions are ',' or '|' or $'\\n'. [default: $'\\t'] - - -v Verbose mode. Shows commands that are run, verbatim. - Useful for running the last (or only) benchmark elsewhere, - like under 'perf record --' or 'lldb --'. - (Looks best with with -d $'\\n', see above.) -EOF -} - -verbose= -delim=$'\t' -baseline="" -benchmarks=() -while getopts 'vd:b:f:' optname; do - case $optname in - v) - verbose=1 - ;; - - d) - delim="$OPTARG" - ;; - - b) - # Used to subtract the "unimportant" parts out of the comparisons - baseline="$OPTARG" - ;; - - f) - benchmarks+=( "$OPTARG" ) - ;; - - *) - usage - exit 1 - ;; - esac -done - -cd "$(dirname "$0")" -cd ../.. -# we are now at the repo root. -repo_root="$PWD" - -rm -rf tmp/bench -mkdir -p tmp/bench - -# TODO(jez) Careful! These must use the same configuration! -# (Alternatively: be sure to compile the right one right before it's used.) -./bazel build //compiler:sorbet @sorbet_ruby_2_7_for_compiler//:ruby -c opt -./bazel run @sorbet_ruby_2_7_for_compiler//:ruby -c opt -- --version - -if [ "${#benchmarks[@]}" -eq 0 ]; then - if [ "$baseline" != "" ]; then - echo "-b is not supported with no -f " - exit 1 - fi - paths=(test/testdata/ruby_benchmark) - - while IFS='' read -r line; do - benchmarks+=( "$line" ) - done < <(find "${paths[@]}" -name '*.rb' | grep -v -E '(disabled|too_slow)' | sort) -fi - - -ruby="${repo_root}/bazel-bin/external/sorbet_ruby_2_7_for_compiler/ruby" - -command=() - -set_startup() { - unset llvmir - command=( - "${ruby}" \ - "--disable=gems" "--disable=did_you_mean" \ - -r "rubygems" \ - -r "${repo_root}/gems/sorbet-runtime/lib/sorbet-runtime.rb" \ - -e 1 \ - ) -} - -set_interpreted() { - unset llvmir - command=( - "${ruby}" \ - "--disable=gems" "--disable=did_you_mean" \ - -r "rubygems" \ - -r "${repo_root}/gems/sorbet-runtime/lib/sorbet-runtime.rb" \ - ./target.rb \ - ) -} - -set_compiled() { - export llvmir=. - command=( \ - "${ruby}" \ - "--disable=gems" "--disable=did_you_mean" \ - -r "rubygems" \ - -r "${repo_root}/gems/sorbet-runtime/lib/sorbet-runtime.rb" \ - -r "${repo_root}/test/patch_require.rb" \ - -e "\$__sorbet_ruby_realpath='target.rb'; require './target.rb.so'" \ - ) -} - -measure() { - pushd tmp/bench &>/dev/null - (time for _ in {1..10}; do "${command[@]}"; done) 2>&1|grep real | cut -d$'\t' -f 2 > measurement - minutes=$(cut -d "m" -f1 < measurement) - seconds=$(cut -d "m" -f2 < measurement | cut -d "s" -f 1) - popd &>/dev/null - - time="$(echo "scale=3;(${minutes} * 60 + ${seconds})/10" | bc)" - echo -en "$time" -} - -compile_benchmark() { - benchmark="$1" - - rm tmp/bench/* - cp "$benchmark" tmp/bench/target.rb - llvmir=. compiled_out_dir=. test/run_sorbet.sh tmp/bench/target.rb &>/dev/null -} - - -# We require GNU printf for the fancy %q functionality. -__system_printf_has_q= -if env printf ' %q' 'test' &> /dev/null; then - __system_printf_has_q=1 -fi - -echo_command() { - # (The `env` avoids the bash built-in printf, which doesn't have %q) - if [ "$__system_printf_has_q" != "" ]; then - env printf ' %q' "${command[@]}" - else - # Won't be quoted, but at least it'll print something. - # User will have to add their own quotes. - env printf ' %s' "${command[@]}" - fi - printf '\n' -} - -echo "ruby vm startup time: $(set_startup; measure)" - -baseline_interpreted= -baseline_compiled= - -echo -e "source${delim}interpreted${delim}compiled" -for benchmark in "$baseline" "${benchmarks[@]}"; do - if [ "$benchmark" = "" ]; then - # No baseline, skip this iteration - continue - fi - - - echo -en "${benchmark#test/testdata/ruby_benchmark/}$delim" - - compile_benchmark "$benchmark" - - set_interpreted - if [ -n "$verbose" ]; then - >&2 printf '(in tmp/bench/) interpreted:' - >&2 echo_command - fi - time_interpreted="$(measure)" - echo -en "$time_interpreted$delim" - if [ "$benchmark" = "$baseline" ]; then - baseline_interpreted="$time_interpreted" - fi - - set_compiled - if [ -n "$verbose" ]; then - >&2 printf '(in tmp/bench/) compiled: llvmir=.' - >&2 echo_command - fi - time_compiled="$(measure)" - echo "$time_compiled" - if [ "$benchmark" = "$baseline" ]; then - baseline_compiled="$time_compiled" - fi - - if [ "$baseline" != "" ] && [ "$benchmark" != "$baseline" ]; then - echo -en "${benchmark#test/testdata/ruby_benchmark/} - baseline$delim" - echo -en "$(echo "$time_interpreted - $baseline_interpreted" | bc)$delim" - echo "$time_compiled - $baseline_compiled" | bc - fi - -done - diff --git a/tools/scripts/update-sorbet.run.sh b/tools/scripts/update-sorbet.run.sh deleted file mode 100755 index 74ba35e649..0000000000 --- a/tools/scripts/update-sorbet.run.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -set -e - -cd "$(dirname "$0")/../.." - -./bazel build //emscripten:sorbet-wasm.tar --config=webasm-darwin --strip=always - -echo -echo "./bazel-bin/emscripten:sorbet-wasm.tar should contain \".wasm\" and \".js\" file" -echo "Please copy them to your clone of sorbet.run repo and make a commit" -echo " tar -C ../sorbet.run/docs/ -xvf ./bazel-bin/emscripten/sorbet-wasm.tar sorbet-wasm.wasm sorbet-wasm.js" -echo " git rev-parse HEAD > ../sorbet.run/docs/sha.html" diff --git a/tools/scripts/update_compiler_exp.sh b/tools/scripts/update_compiler_exp.sh deleted file mode 100755 index 61383150b0..0000000000 --- a/tools/scripts/update_compiler_exp.sh +++ /dev/null @@ -1,90 +0,0 @@ -#!/bin/bash - -set -e - -cd "$(dirname "$0")" -cd ../.. -# we are now at the repo root. - -if ! command -v parallel &> /dev/null; then - echo "This script requires GNU parallel to be installed" - exit 1 -fi - -if [ "$(uname -s)" != Linux ] ; then - echo "This script can only be run on Linux" - exit 1 -fi - -COMMAND_FILE="$(mktemp)" -trap 'rm -f "$COMMAND_FILE"' EXIT - -./bazel build //compiler:sorbet - -if [ $# -eq 0 ]; then - paths=(test/testdata) -else - paths=("$@") -fi - -rb_src=() -while IFS='' read -r line; do - rb_src+=("$line") -done < <(find "${paths[@]}" -name '*.rb*' | sort) - -basename= -srcs=() -exp_extensions="opt.ll ll stderr" - -syncback=() - -for this_src in "${rb_src[@]}" DUMMY; do - this_base="${this_src%__*}" - if [ "$this_base" = "$basename" ]; then - srcs=("${srcs[@]}" "$this_src") - continue - fi - - # Don't update exp files for validate exp tests, as they are hand edited to - # produce llvm failures - if [[ "$this_base" =~ "/disabled/validate_exp_test" ]]; then - continue - fi - - dir="$(dirname "$this_base")" - basename="$this_base" - srcs=("$this_src") - - for ext in $exp_extensions; do - exp=${basename%.rb}.$ext.exp - if [ -f "${basename%.rb}.$ext.exp" ]; then - llvmir=$(mktemp -d) - syncback+=("$exp") - echo \ - bazel-bin/compiler/sorbet \ - --silence-dev-message \ - --no-error-count \ - --suppress-non-critical \ - --compiled-out-dir \ - "$llvmir" \ - --llvm-ir-dir \ - "$llvmir" \ - "${srcs[@]}" \ - 2\> "$llvmir/update_testdata_exp.stderr"\; \ - \< "$llvmir/$dir/*.$ext" sed -e \'/^target triple =/d\' \> "$exp" \ - >> "$COMMAND_FILE" - fi - done -done - -if ! parallel --joblog - < "$COMMAND_FILE"; then - echo 'WARN: parallel exiited non-zero' -fi - -if [ "${EMIT_SYNCBACK:-}" != "" ]; then - echo '### BEGIN SYNCBACK ###' - for file in "${syncback[@]}"; do - echo "$file" - done - echo '### END SYNCBACK ###' -fi diff --git a/tools/scripts/update_exp_files.sh b/tools/scripts/update_exp_files.sh index a926b09081..df0b2b8e98 100755 --- a/tools/scripts/update_exp_files.sh +++ b/tools/scripts/update_exp_files.sh @@ -14,4 +14,3 @@ cd "$(dirname "${BASH_SOURCE[0]}")/../.." //test/cli:update //test/lsp:update //test/scip:update -c opt "$@" tools/scripts/update_testdata_exp.sh -test/cli/compiler/update_cli_exp_files.sh diff --git a/tools/scripts/update_sorbet_ruby_patch_for_compiler.sh b/tools/scripts/update_sorbet_ruby_patch_for_compiler.sh deleted file mode 100755 index 6a5eb3e514..0000000000 --- a/tools/scripts/update_sorbet_ruby_patch_for_compiler.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -set -euo pipefail - -sorbet_repo_root="$(cd "$(dirname "$0")"/../.. ; pwd)" - -# Base tag to use as the base for the patch. -base_tag="v2_7_2" - -# Branch to use to generate the patch. -patch_branch="sorbet_ruby_2_7" - -# Filename for the patch in the repo. -patch_filename="sorbet_ruby_2_7_for_compiler.patch" - -# We'll let GitHub generate the patch for us. -patch_source_url="https://github.com/sorbet/ruby/compare/${base_tag}...${patch_branch}.patch" - -echo "Fetching new patch file from GitHub..." -curl -o "$sorbet_repo_root/third_party/ruby/${patch_filename}" "$patch_source_url" -echo "Done!" diff --git a/tools/scripts/update_testdata_exp.sh b/tools/scripts/update_testdata_exp.sh index a54e37a9c8..05e8fcd7ee 100755 --- a/tools/scripts/update_testdata_exp.sh +++ b/tools/scripts/update_testdata_exp.sh @@ -115,6 +115,19 @@ for this_src in "${rb_src[@]}" DUMMY; do if grep -q '^# enable-experimental-requires-ancestor: true' "${srcs[@]}"; then needs_requires_ancestor=true fi + + if grep -q '^# typed-super: false' "${srcs[@]}"; then + needs_typed_super_false=true + else + needs_typed_super_false=false + fi + + if grep -q '^# enable-suggest-unsafe: true' "${srcs[@]}"; then + needs_suggest_unsafe=true + else + needs_suggest_unsafe=false + fi + for pass in "${passes[@]}"; do candidate="$basename.$pass.exp" # Document symbols is weird, because it's (currently) the only exp-style @@ -164,6 +177,16 @@ for this_src in "${rb_src[@]}" DUMMY; do else args=() fi + if $needs_typed_super_false; then + args+=("--typed-super=false") + else + args+=() + fi + if $needs_suggest_unsafe; then + args+=("--suggest-unsafe") + else + args+=() + fi if [ "$pass" = "autogen" ]; then args=("--stop-after=namer") elif [ "$pass" = "minimized-rbi" ]; then diff --git a/tools/toolchain/webasm-darwin/BUILD b/tools/toolchain/webasm-darwin/BUILD deleted file mode 100644 index 8d4fa215e3..0000000000 --- a/tools/toolchain/webasm-darwin/BUILD +++ /dev/null @@ -1,94 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -cc_toolchain_suite( - name = "webasm-darwin", - tags = ["manual"], - toolchains = { - "webasm": ":webasm_toolchain_darwin", - "webasm-darwin": ":webasm_toolchain_darwin", - "webasm|emscripten": ":webasm_toolchain_darwin", - "webasm|webasm": ":webasm_toolchain_darwin", - }, -) - -filegroup(name = "empty") - -GENERATE_EM_CACHE_COMMAND = """ -export EM_CACHE=$$(mktemp -d) -export PATH="$${PATH}:/usr/local/bin/" -EM_CONFIG="LLVM_ROOT='$${PWD}/external/emscripten_clang_darwin/';" -EM_CONFIG+="EMSCRIPTEN_NATIVE_OPTIMIZER='$${PWD}/external/external/emscripten_clang_darwin/optimizer';" -EM_CONFIG+="BINARYEN_ROOT='$${PWD}/external/emscripten_clang_darwin/binaryen';" -EM_CONFIG+="NODE_JS='$${PWD}/external/nodejs_darwin_amd64/bin/node';" -EM_CONFIG+="EMSCRIPTEN_ROOT='$${PWD}/external/emscripten_toolchain';" -EM_CONFIG+="SPIDERMONKEY_ENGINE='';" -EM_CONFIG+="V8_ENGINE='';" -EM_CONFIG+="COMPILER_ENGINE=NODE_JS;" -EM_CONFIG+="JS_ENGINES=[NODE_JS];" -export EM_EXCLUSIVE_CACHE_ACCESS=1 -export EMCC_SKIP_SANITY_CHECK=1 -export EMCC_WASM_BACKEND=0 - - -export EM_EXCLUSIVE_CACHE_ACCESS=1 -export EMCC_WASM_BACKEND=0 - -mkdir -p "tmp" -TMPDIR=$$(realpath "tmp") -export TMPDIR -BC_RENAME_PREFIX=$$(mktemp -dt "renaming_links-XXXX") -export BC_RENAME_PREFIX -OUT_DIR=$$(mktemp -dt "emscripten_out-XXXX") -export OUT_DIR -EMCC_TEMP_DIR=$$(mktemp -dt "emscripten_tmp-XXXX") -export EMCC_TEMP_DIR -EM_CONFIG+="TEMP_DIR='$${EMCC_TEMP_DIR}';" -export EM_CONFIG -python $${PWD}/external/emscripten_toolchain/embuilder.py build dlmalloc libc++_noexcept libc++abi libc libc-wasm libc-extras -tar -czf $(location em_cache.tar.gz) -C "$$EM_CACHE" . -""" - -genrule( - name = "generate_em_cache", - srcs = [ - "@emscripten_clang_darwin//:all", - "@emscripten_toolchain//:all", - "@nodejs_darwin_amd64//:node", - ], - outs = ["em_cache.tar.gz"], - cmd = GENERATE_EM_CACHE_COMMAND, - tags = ["manual"], -) - -filegroup( - name = "all-darwin", - srcs = [ - "em_cache_existing.tar.gz", - "emcc.sh", - "@emscripten_clang_darwin//:all", - "@emscripten_toolchain//:all", - "@nodejs_darwin_amd64//:node", - ], - tags = ["manual"], -) - -cc_toolchain( - name = "webasm_toolchain_darwin", - all_files = ":all-darwin", - compiler_files = ":all-darwin", - dwp_files = ":empty", - linker_files = ":all-darwin", - objcopy_files = ":empty", - strip_files = ":empty", - supports_param_files = 0, - tags = ["manual"], - toolchain_config = "darwin-wasm", - toolchain_identifier = "webasm-toolchain", -) - -load(":cc_toolchain_config.bzl", "cc_toolchain_config") - -cc_toolchain_config( - name = "darwin-wasm", - cpu = "webasm", -) diff --git a/tools/toolchain/webasm-darwin/ar.sh b/tools/toolchain/webasm-darwin/ar.sh deleted file mode 100755 index 2b0f8042d7..0000000000 --- a/tools/toolchain/webasm-darwin/ar.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/bash -set -euo pipefail - -command -v realpath > /dev/null 2>&1 || { echo 'You need to install the "realpath" command system-wide.' ; exit 1; } - -ABSOLUTE_PREFIX=$(realpath "${PWD}") # bazel replaces PWD with /proc/self/cwd which is unstable under "cd", meaning that reffering to a path under relative name fails - -EM_CONFIG="LLVM_ROOT='${ABSOLUTE_PREFIX}/external/emscripten_clang_darwin/';" -EM_CONFIG+="EMSCRIPTEN_NATIVE_OPTIMIZER='${ABSOLUTE_PREFIX}/external/external/emscripten_clang_darwin/optimizer';" -EM_CONFIG+="BINARYEN_ROOT='${ABSOLUTE_PREFIX}/external/emscripten_clang_darwin/binaryen';" -EM_CONFIG+="NODE_JS='${ABSOLUTE_PREFIX}/external/nodejs_darwin_amd64/bin/node';" -EM_CONFIG+="EMSCRIPTEN_ROOT='${ABSOLUTE_PREFIX}/external/emscripten_toolchain';" -EM_CONFIG+="SPIDERMONKEY_ENGINE='';" -EM_CONFIG+="V8_ENGINE='';" -EM_CONFIG+="TEMP_DIR='${ABSOLUTE_PREFIX}/tmp/emscripten_tmp';" -EM_CONFIG+="COMPILER_ENGINE=NODE_JS;" -EM_CONFIG+="JS_ENGINES=[NODE_JS];" -export EM_CONFIG - -# Try to find Python on the system. -# Our version of emscripten uses Python 2, but upstream has already switched to -# Python 3. For now we prefer finding python2 if available, and fall back to -# trying Python 3 in case no Python 2 was found (which will chunder warnings -# but still work). -if command -v python &> /dev/null; then - PYTHON="python" -elif command -v python2 &> /dev/null; then - PYTHON="python2" -elif command -v python3 &> /dev/null; then - PYTHON="python3" -else - export PATH="${PATH}:/usr/local/bin/" - PYTHON="python" -fi - -export EM_EXCLUSIVE_CACHE_ACCESS=1 -export EMCC_SKIP_SANITY_CHECK=1 -# export EMCC_DEBUG=2 -export EMCC_WASM_BACKEND=0 -mkdir -p "tmp/emscripten_tmp" - -"$PYTHON" external/emscripten_toolchain/emar.py "$@" diff --git a/tools/toolchain/webasm-darwin/cc_toolchain_config.bzl b/tools/toolchain/webasm-darwin/cc_toolchain_config.bzl deleted file mode 100644 index 1835627015..0000000000 --- a/tools/toolchain/webasm-darwin/cc_toolchain_config.bzl +++ /dev/null @@ -1,182 +0,0 @@ -load( - "@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl", - "action_config", - "artifact_name_pattern", - "env_entry", - "env_set", - "feature", - "feature_set", - "flag_group", - "flag_set", - "make_variable", - "tool", - "tool_path", - "variable_with_value", - "with_feature_set", -) -load("@bazel_tools//tools/build_defs/cc:action_names.bzl", "ACTION_NAMES") - -def _impl(ctx): - toolchain_identifier = "webasm-toolchain" - - host_system_name = "i686-darwin-gnu" - - target_system_name = "webasm-darwin-emscripten" - - target_cpu = "webasm" - - target_libc = "unknown" - - compiler = "emscripten" - - abi_version = "unknown" - - abi_libc_version = "unknown" - - cc_target_os = None - - builtin_sysroot = None - - all_compile_actions = [ - ACTION_NAMES.c_compile, - ACTION_NAMES.cpp_compile, - ACTION_NAMES.linkstamp_compile, - ACTION_NAMES.assemble, - ACTION_NAMES.preprocess_assemble, - ACTION_NAMES.cpp_header_parsing, - ACTION_NAMES.cpp_module_compile, - ACTION_NAMES.cpp_module_codegen, - ACTION_NAMES.clif_match, - ACTION_NAMES.lto_backend, - ] - - all_cpp_compile_actions = [ - ACTION_NAMES.cpp_compile, - ACTION_NAMES.linkstamp_compile, - ACTION_NAMES.cpp_header_parsing, - ACTION_NAMES.cpp_module_compile, - ACTION_NAMES.cpp_module_codegen, - ACTION_NAMES.clif_match, - ] - - preprocessor_compile_actions = [ - ACTION_NAMES.c_compile, - ACTION_NAMES.cpp_compile, - ACTION_NAMES.linkstamp_compile, - ACTION_NAMES.preprocess_assemble, - ACTION_NAMES.cpp_header_parsing, - ACTION_NAMES.cpp_module_compile, - ACTION_NAMES.clif_match, - ] - - codegen_compile_actions = [ - ACTION_NAMES.c_compile, - ACTION_NAMES.cpp_compile, - ACTION_NAMES.linkstamp_compile, - ACTION_NAMES.assemble, - ACTION_NAMES.preprocess_assemble, - ACTION_NAMES.cpp_module_codegen, - ACTION_NAMES.lto_backend, - ] - - all_link_actions = [ - ACTION_NAMES.cpp_link_executable, - ACTION_NAMES.cpp_link_dynamic_library, - ACTION_NAMES.cpp_link_nodeps_dynamic_library, - ] - - action_configs = [] - - default_compile_flags_feature = feature( - name = "default_compile_flags", - enabled = True, - flag_sets = [ - flag_set( - actions = [ - ACTION_NAMES.assemble, - ACTION_NAMES.preprocess_assemble, - ACTION_NAMES.linkstamp_compile, - ACTION_NAMES.c_compile, - ACTION_NAMES.cpp_compile, - ACTION_NAMES.cpp_header_parsing, - ACTION_NAMES.cpp_module_compile, - ACTION_NAMES.cpp_module_codegen, - ACTION_NAMES.lto_backend, - ACTION_NAMES.clif_match, - ], - flag_groups = [ - flag_group( - flags = [ - "-isystem", - "external/emscripten_toolchain/system/include/compat", - "-isystem", - "external/emscripten_toolchain/system/include/libcxx", - "-isystem", - "external/emscripten_toolchain/system/include/libc", - "-isystem", - "external/emscripten_toolchain/system/lib/libcxxabi/include", - "-isystem", - "external/emscripten_toolchain/system/include", - "-isystem", - "external/emscripten_toolchain/system/include/SSE", - ], - ), - ], - ), - ], - ) - - features = [default_compile_flags_feature] - - cxx_builtin_include_directories = [] - - artifact_name_patterns = [] - - make_variables = [] - - tool_paths = [ - tool_path(name = "gcc", path = "emcc.sh"), - tool_path(name = "ld", path = "emcc.sh"), - tool_path(name = "ar", path = "ar.sh"), - tool_path(name = "cpp", path = "/usr/bin/false2"), - tool_path(name = "gcov", path = "/usr/bin/false3"), - tool_path(name = "nm", path = "/usr/bin/false4"), - tool_path(name = "objdump", path = "/usr/bin/false5"), - tool_path(name = "strip", path = "/usr/bin/false6"), - ] - - out = ctx.actions.declare_file(ctx.label.name) - ctx.actions.write(out, "Fake executable") - return [ - cc_common.create_cc_toolchain_config_info( - ctx = ctx, - features = features, - action_configs = action_configs, - artifact_name_patterns = artifact_name_patterns, - cxx_builtin_include_directories = cxx_builtin_include_directories, - toolchain_identifier = toolchain_identifier, - host_system_name = host_system_name, - target_system_name = target_system_name, - target_cpu = target_cpu, - target_libc = target_libc, - compiler = compiler, - abi_version = abi_version, - abi_libc_version = abi_libc_version, - tool_paths = tool_paths, - make_variables = make_variables, - builtin_sysroot = builtin_sysroot, - cc_target_os = cc_target_os, - ), - DefaultInfo( - executable = out, - ), - ] - -cc_toolchain_config = rule( - implementation = _impl, - attrs = { - "cpu": attr.string(mandatory = True, values = ["webasm"]), - }, - provides = [CcToolchainConfigInfo], - executable = True, -) diff --git a/tools/toolchain/webasm-darwin/em_cache_existing.tar.gz b/tools/toolchain/webasm-darwin/em_cache_existing.tar.gz deleted file mode 100755 index 3b313632e7..0000000000 Binary files a/tools/toolchain/webasm-darwin/em_cache_existing.tar.gz and /dev/null differ diff --git a/tools/toolchain/webasm-darwin/emcc.sh b/tools/toolchain/webasm-darwin/emcc.sh deleted file mode 100755 index 6120be01c0..0000000000 --- a/tools/toolchain/webasm-darwin/emcc.sh +++ /dev/null @@ -1,114 +0,0 @@ -#!/bin/bash -set -eo pipefail - -EM_CACHE_ARCHIVE="tools/toolchain/webasm-darwin/em_cache_existing.tar.gz" - -if [ ! -f "$EM_CACHE_ARCHIVE" ]; then - echo "can't find stdlib compilation cache"; -fi - -command -v realpath > /dev/null 2>&1 || { echo 'You need to install the "realpath" command system-wide.' ; exit 1; } - -# Try to find Python on the system. -# Our version of emscripten uses Python 2, but upstream has already switched to -# Python 3. For now we prefer finding python2 if available, and fall back to -# trying Python 3 in case no Python 2 was found (which will chunder warnings -# but still work). -if command -v python &> /dev/null; then - PYTHON="python" -elif command -v python2 &> /dev/null; then - PYTHON="python2" -elif command -v python3 &> /dev/null; then - PYTHON="python3" -else - export PATH="${PATH}:/usr/local/bin/" - PYTHON="python" -fi - -ABSOLUTE_PREFIX=$(realpath "${PWD}") # bazel replaces PWD with /proc/self/cwd which is unstable under "cd", meaning that reffering to a path under relative name fails - -EM_CONFIG="LLVM_ROOT='${ABSOLUTE_PREFIX}/external/emscripten_clang_darwin/';" -EM_CONFIG+="EMSCRIPTEN_NATIVE_OPTIMIZER='${ABSOLUTE_PREFIX}/external/external/emscripten_clang_darwin/optimizer';" -EM_CONFIG+="BINARYEN_ROOT='${ABSOLUTE_PREFIX}/external/emscripten_clang_darwin/binaryen';" -EM_CONFIG+="NODE_JS='${ABSOLUTE_PREFIX}/external/nodejs_darwin_amd64/bin/node';" -EM_CONFIG+="EMSCRIPTEN_ROOT='${ABSOLUTE_PREFIX}/external/emscripten_toolchain';" -EM_CONFIG+="SPIDERMONKEY_ENGINE='';" -EM_CONFIG+="V8_ENGINE='';" -EM_CONFIG+="COMPILER_ENGINE=NODE_JS;" -EM_CONFIG+="JS_ENGINES=[NODE_JS];" - - -export EM_EXCLUSIVE_CACHE_ACCESS=1 -export EMCC_SKIP_SANITY_CHECK=1 -# export EMCC_DEBUG=2 -export EMCC_WASM_BACKEND=0 - - -export EM_EXCLUSIVE_CACHE_ACCESS=1 -#export EMCC_SKIP_SANITY_CHECK=1 -export EMCC_WASM_BACKEND=0 - -mkdir -p "tmp" -TMPDIR=$(realpath "tmp") -export TMPDIR -BC_RENAME_PREFIX=$(mktemp -dt "renaming_links-XXXX") -export BC_RENAME_PREFIX -EM_CACHE=$(mktemp -dt "emscripten_cache-XXXX") -export EM_CACHE -OUT_DIR=$(mktemp -dt "emscripten_out-XXXX") -export OUT_DIR -EMCC_TEMP_DIR=$(mktemp -dt "emscripten_tmp-XXXX") -export EMCC_TEMP_DIR -trap 'rm -rf "$EMCC_TEMP_DIR" "$OUT_DIR" "$EM_CACHE" "$BC_RENAME_PREFIX"' EXIT -# shellcheck disable=SC2089 -# ^^^^ complains that we have literal ' in string, that we _intended_ to have there -# I didn't find a way to structure it so that it stops complaining. -EM_CONFIG+="TEMP_DIR='${EMCC_TEMP_DIR}';" -# shellcheck disable=SC2090 -# ^^^^ false positive due to false positive above -export EM_CONFIG -args=() -tar_name= -# bazel hardcodes `.o` files and `.a` files as outputs of tasks (WHY???). -# LLVM is happy to read&write bitcode to `.o` files -# but explicitly prohibits this for `.a` files. Fool it by creating symbolic links. -for i in "$@"; do - if [[ "$i" =~ ^(.*)\.tar$ ]]; then - tar_name="${BASH_REMATCH[1]}" - folder_name=$(dirname "$i") - mkdir -p "${OUT_DIR}/${folder_name}" - out_name="${OUT_DIR}/${tar_name}.html" - args=("${args[@]}" "$out_name") - elif [[ "$i" =~ ^(.*)\.a$ ]]; then - core_name="${BASH_REMATCH[1]}" - folder_name=$(dirname "$i") - mkdir -p "$BC_RENAME_PREFIX/$folder_name" - link_name="$BC_RENAME_PREFIX/${core_name}.bc" - cp "$i" "$link_name" - args=("${args[@]}" "$link_name") - elif [[ "$i" =~ ^-march=(.*)$ ]]; then - # nothing - continue - elif [[ "$i" == "-framework" ]] || [[ "$i" == "Foundation" ]]; then - # workaround https://github.com/abseil/abseil-cpp/issues/308 - continue - else - args=("${args[@]}" "$i") - fi -done - -if [ -n "$tar_name" ]; then - tar -xf "$EM_CACHE_ARCHIVE" -C "$EM_CACHE" -fi -# Run emscripten to compile and link -#echo "original_args" "$@" -#echo "transformed_args" "${args[@]}" -"$PYTHON" external/emscripten_toolchain/emcc.py "${args[@]}" - -if [ -n "${tar_name}" ]; then - full_tar_path=$(realpath "${tar_name}.tar") - folder_prefix=$(dirname "${OUT_DIR}/${tar_name}") - file_name=$(basename "${OUT_DIR}/${tar_name}") - cd "${folder_prefix}" - tar cf "${full_tar_path}" ./"${file_name}"* -fi diff --git a/tools/toolchain/webasm-linux/BUILD b/tools/toolchain/webasm-linux/BUILD deleted file mode 100644 index 6e77e3e4a0..0000000000 --- a/tools/toolchain/webasm-linux/BUILD +++ /dev/null @@ -1,94 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -cc_toolchain_suite( - name = "webasm-linux", - tags = ["manual"], - toolchains = { - "webasm": ":webasm_toolchain_linux", - "webasm-linux": ":webasm_toolchain_linux", - "webasm|emscripten": ":webasm_toolchain_linux", - "webasm|webasm": ":webasm_toolchain_linux", - }, -) - -filegroup(name = "empty") - -GENERATE_EM_CACHE_COMMAND = """ -export EM_CACHE=$$(mktemp -d) -export PATH="$${PATH}:/usr/local/bin/" -EM_CONFIG="LLVM_ROOT='$${PWD}/external/emscripten_clang_linux/';" -EM_CONFIG+="EMSCRIPTEN_NATIVE_OPTIMIZER='$${PWD}/external/external/emscripten_clang_linux/optimizer';" -EM_CONFIG+="BINARYEN_ROOT='$${PWD}/external/emscripten_clang_linux/binaryen';" -EM_CONFIG+="NODE_JS='$${PWD}/external/nodejs_linux_amd64/bin/node';" -EM_CONFIG+="EMSCRIPTEN_ROOT='$${PWD}/external/emscripten_toolchain';" -EM_CONFIG+="SPIDERMONKEY_ENGINE='';" -EM_CONFIG+="V8_ENGINE='';" -EM_CONFIG+="COMPILER_ENGINE=NODE_JS;" -EM_CONFIG+="JS_ENGINES=[NODE_JS];" -export EM_EXCLUSIVE_CACHE_ACCESS=1 -export EMCC_SKIP_SANITY_CHECK=1 -export EMCC_WASM_BACKEND=0 - - -export EM_EXCLUSIVE_CACHE_ACCESS=1 -export EMCC_WASM_BACKEND=0 - -mkdir -p "tmp" -TMPDIR=$$(realpath "tmp") -export TMPDIR -BC_RENAME_PREFIX=$$(mktemp -dt "renaming_links-XXXX") -export BC_RENAME_PREFIX -OUT_DIR=$$(mktemp -dt "emscripten_out-XXXX") -export OUT_DIR -EMCC_TEMP_DIR=$$(mktemp -dt "emscripten_tmp-XXXX") -export EMCC_TEMP_DIR -EM_CONFIG+="TEMP_DIR='$${EMCC_TEMP_DIR}';" -export EM_CONFIG -python $${PWD}/external/emscripten_toolchain/embuilder.py build dlmalloc libc++_noexcept libc++abi libc libc-wasm libc-extras -tar -czf $(location em_cache.tar.gz) -C "$$EM_CACHE" . -""" - -genrule( - name = "generate_em_cache", - srcs = [ - "@emscripten_clang_linux//:all", - "@emscripten_toolchain//:all", - "@nodejs_linux_amd64//:node", - ], - outs = ["em_cache.tar.gz"], - cmd = GENERATE_EM_CACHE_COMMAND, - tags = ["manual"], -) - -filegroup( - name = "all-linux", - srcs = [ - "em_cache_existing.tar.gz", - "emcc.sh", - "@emscripten_clang_linux//:all", - "@emscripten_toolchain//:all", - "@nodejs_linux_amd64//:node", - ], - tags = ["manual"], -) - -cc_toolchain( - name = "webasm_toolchain_linux", - all_files = ":all-linux", - compiler_files = ":all-linux", - dwp_files = ":empty", - linker_files = ":all-linux", - objcopy_files = ":empty", - strip_files = ":empty", - supports_param_files = 0, - tags = ["manual"], - toolchain_config = "wasm-linux", - toolchain_identifier = "webasm-toolchain", -) - -load(":cc_toolchain_config.bzl", "cc_toolchain_config") - -cc_toolchain_config( - name = "wasm-linux", - cpu = "webasm", -) diff --git a/tools/toolchain/webasm-linux/ar.sh b/tools/toolchain/webasm-linux/ar.sh deleted file mode 100755 index 7742abb6a1..0000000000 --- a/tools/toolchain/webasm-linux/ar.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/bash -set -euo pipefail - -command -v realpath > /dev/null 2>&1 || { echo 'You need to install the "realpath" command system-wide.' ; exit 1; } - -ABSOLUTE_PREFIX=$(realpath "${PWD}") # bazel replaces PWD with /proc/self/cwd which is unstable under "cd", meaning that reffering to a path under relative name fails - -EM_CONFIG="LLVM_ROOT='${ABSOLUTE_PREFIX}/external/emscripten_clang_linux/';" -EM_CONFIG+="EMSCRIPTEN_NATIVE_OPTIMIZER='${ABSOLUTE_PREFIX}/external/external/emscripten_clang_linux/optimizer';" -EM_CONFIG+="BINARYEN_ROOT='${ABSOLUTE_PREFIX}/external/emscripten_clang_linux/binaryen';" -EM_CONFIG+="NODE_JS='${ABSOLUTE_PREFIX}/external/nodejs_linux_amd64/bin/node';" -EM_CONFIG+="EMSCRIPTEN_ROOT='${ABSOLUTE_PREFIX}/external/emscripten_toolchain';" -EM_CONFIG+="SPIDERMONKEY_ENGINE='';" -EM_CONFIG+="V8_ENGINE='';" -EM_CONFIG+="TEMP_DIR='${ABSOLUTE_PREFIX}/tmp/emscripten_tmp';" -EM_CONFIG+="COMPILER_ENGINE=NODE_JS;" -EM_CONFIG+="JS_ENGINES=[NODE_JS];" -export EM_CONFIG - -# Try to find Python on the system. -# Our version of emscripten uses Python 2, but upstream has already switched to -# Python 3. For now we prefer finding python2 if available, and fall back to -# trying Python 3 in case no Python 2 was found (which will chunder warnings -# but still work). -if command -v python &> /dev/null; then - PYTHON="python" -elif command -v python2 &> /dev/null; then - PYTHON="python2" -elif command -v python3 &> /dev/null; then - PYTHON="python3" -else - export PATH="${PATH}:/usr/local/bin/" - PYTHON="python" -fi - -export EM_EXCLUSIVE_CACHE_ACCESS=1 -export EMCC_SKIP_SANITY_CHECK=1 -# export EMCC_DEBUG=2 -export EMCC_WASM_BACKEND=0 -mkdir -p "tmp/emscripten_tmp" - -"$PYTHON" external/emscripten_toolchain/emar.py "$@" diff --git a/tools/toolchain/webasm-linux/cc_toolchain_config.bzl b/tools/toolchain/webasm-linux/cc_toolchain_config.bzl deleted file mode 100644 index a8ed8ae64d..0000000000 --- a/tools/toolchain/webasm-linux/cc_toolchain_config.bzl +++ /dev/null @@ -1,182 +0,0 @@ -load( - "@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl", - "action_config", - "artifact_name_pattern", - "env_entry", - "env_set", - "feature", - "feature_set", - "flag_group", - "flag_set", - "make_variable", - "tool", - "tool_path", - "variable_with_value", - "with_feature_set", -) -load("@bazel_tools//tools/build_defs/cc:action_names.bzl", "ACTION_NAMES") - -def _impl(ctx): - toolchain_identifier = "webasm-toolchain" - - host_system_name = "i686-linux-gnu" - - target_system_name = "webasm-linux-emscripten" - - target_cpu = "webasm" - - target_libc = "unknown" - - compiler = "emscripten" - - abi_version = "unknown" - - abi_libc_version = "unknown" - - cc_target_os = None - - builtin_sysroot = None - - all_compile_actions = [ - ACTION_NAMES.c_compile, - ACTION_NAMES.cpp_compile, - ACTION_NAMES.linkstamp_compile, - ACTION_NAMES.assemble, - ACTION_NAMES.preprocess_assemble, - ACTION_NAMES.cpp_header_parsing, - ACTION_NAMES.cpp_module_compile, - ACTION_NAMES.cpp_module_codegen, - ACTION_NAMES.clif_match, - ACTION_NAMES.lto_backend, - ] - - all_cpp_compile_actions = [ - ACTION_NAMES.cpp_compile, - ACTION_NAMES.linkstamp_compile, - ACTION_NAMES.cpp_header_parsing, - ACTION_NAMES.cpp_module_compile, - ACTION_NAMES.cpp_module_codegen, - ACTION_NAMES.clif_match, - ] - - preprocessor_compile_actions = [ - ACTION_NAMES.c_compile, - ACTION_NAMES.cpp_compile, - ACTION_NAMES.linkstamp_compile, - ACTION_NAMES.preprocess_assemble, - ACTION_NAMES.cpp_header_parsing, - ACTION_NAMES.cpp_module_compile, - ACTION_NAMES.clif_match, - ] - - codegen_compile_actions = [ - ACTION_NAMES.c_compile, - ACTION_NAMES.cpp_compile, - ACTION_NAMES.linkstamp_compile, - ACTION_NAMES.assemble, - ACTION_NAMES.preprocess_assemble, - ACTION_NAMES.cpp_module_codegen, - ACTION_NAMES.lto_backend, - ] - - all_link_actions = [ - ACTION_NAMES.cpp_link_executable, - ACTION_NAMES.cpp_link_dynamic_library, - ACTION_NAMES.cpp_link_nodeps_dynamic_library, - ] - - action_configs = [] - - default_compile_flags_feature = feature( - name = "default_compile_flags", - enabled = True, - flag_sets = [ - flag_set( - actions = [ - ACTION_NAMES.assemble, - ACTION_NAMES.preprocess_assemble, - ACTION_NAMES.linkstamp_compile, - ACTION_NAMES.c_compile, - ACTION_NAMES.cpp_compile, - ACTION_NAMES.cpp_header_parsing, - ACTION_NAMES.cpp_module_compile, - ACTION_NAMES.cpp_module_codegen, - ACTION_NAMES.lto_backend, - ACTION_NAMES.clif_match, - ], - flag_groups = [ - flag_group( - flags = [ - "-isystem", - "external/emscripten_toolchain/system/include/compat", - "-isystem", - "external/emscripten_toolchain/system/include/libcxx", - "-isystem", - "external/emscripten_toolchain/system/include/libc", - "-isystem", - "external/emscripten_toolchain/system/lib/libcxxabi/include", - "-isystem", - "external/emscripten_toolchain/system/include", - "-isystem", - "external/emscripten_toolchain/system/include/SSE", - ], - ), - ], - ), - ], - ) - - features = [default_compile_flags_feature] - - cxx_builtin_include_directories = [] - - artifact_name_patterns = [] - - make_variables = [] - - tool_paths = [ - tool_path(name = "gcc", path = "emcc.sh"), - tool_path(name = "ld", path = "emcc.sh"), - tool_path(name = "ar", path = "ar.sh"), - tool_path(name = "cpp", path = "/usr/bin/false2"), - tool_path(name = "gcov", path = "/usr/bin/false3"), - tool_path(name = "nm", path = "/usr/bin/false4"), - tool_path(name = "objdump", path = "/usr/bin/false5"), - tool_path(name = "strip", path = "/usr/bin/false6"), - ] - - out = ctx.actions.declare_file(ctx.label.name) - ctx.actions.write(out, "Fake executable") - return [ - cc_common.create_cc_toolchain_config_info( - ctx = ctx, - features = features, - action_configs = action_configs, - artifact_name_patterns = artifact_name_patterns, - cxx_builtin_include_directories = cxx_builtin_include_directories, - toolchain_identifier = toolchain_identifier, - host_system_name = host_system_name, - target_system_name = target_system_name, - target_cpu = target_cpu, - target_libc = target_libc, - compiler = compiler, - abi_version = abi_version, - abi_libc_version = abi_libc_version, - tool_paths = tool_paths, - make_variables = make_variables, - builtin_sysroot = builtin_sysroot, - cc_target_os = cc_target_os, - ), - DefaultInfo( - executable = out, - ), - ] - -cc_toolchain_config = rule( - implementation = _impl, - attrs = { - "cpu": attr.string(mandatory = True, values = ["webasm"]), - }, - provides = [CcToolchainConfigInfo], - executable = True, -) diff --git a/tools/toolchain/webasm-linux/em_cache_existing.tar.gz b/tools/toolchain/webasm-linux/em_cache_existing.tar.gz deleted file mode 100755 index 48ba7340bd..0000000000 Binary files a/tools/toolchain/webasm-linux/em_cache_existing.tar.gz and /dev/null differ diff --git a/tools/toolchain/webasm-linux/emcc.sh b/tools/toolchain/webasm-linux/emcc.sh deleted file mode 100755 index e2c1247ea2..0000000000 --- a/tools/toolchain/webasm-linux/emcc.sh +++ /dev/null @@ -1,112 +0,0 @@ -#!/bin/bash -set -eo pipefail - -EM_CACHE_ARCHIVE="tools/toolchain/webasm-linux/em_cache_existing.tar.gz" - -if [ ! -f "$EM_CACHE_ARCHIVE" ]; then - echo "can't find stdlib compilation cache"; -fi - -command -v realpath > /dev/null 2>&1 || { echo 'You need to install the "realpath" command system-wide.' ; exit 1; } - -# Try to find Python on the system. -# Our version of emscripten uses Python 2, but upstream has already switched to -# Python 3. For now we prefer finding python2 if available, and fall back to -# trying Python 3 in case no Python 2 was found (which will chunder warnings -# but still work). -if command -v python &> /dev/null; then - PYTHON="python" -elif command -v python2 &> /dev/null; then - PYTHON="python2" -elif command -v python3 &> /dev/null; then - PYTHON="python3" -else - export PATH="${PATH}:/usr/local/bin/" - PYTHON="python" -fi - -ABSOLUTE_PREFIX=$(realpath "${PWD}") # bazel replaces PWD with /proc/self/cwd which is unstable under "cd", meaning that reffering to a path under relative name fails - -EM_CONFIG="LLVM_ROOT='${ABSOLUTE_PREFIX}/external/emscripten_clang_linux/';" -EM_CONFIG+="EMSCRIPTEN_NATIVE_OPTIMIZER='${ABSOLUTE_PREFIX}/external/emscripten_clang_linux/optimizer';" -EM_CONFIG+="BINARYEN_ROOT='${ABSOLUTE_PREFIX}/external/emscripten_clang_linux/binaryen';" -EM_CONFIG+="NODE_JS='${ABSOLUTE_PREFIX}/external/nodejs_linux_amd64/bin/node';" -EM_CONFIG+="EMSCRIPTEN_ROOT='${ABSOLUTE_PREFIX}/external/emscripten_toolchain';" -EM_CONFIG+="SPIDERMONKEY_ENGINE='';" -EM_CONFIG+="V8_ENGINE='';" -EM_CONFIG+="COMPILER_ENGINE=NODE_JS;" -EM_CONFIG+="JS_ENGINES=[NODE_JS];" - - -export EM_EXCLUSIVE_CACHE_ACCESS=1 -export EMCC_SKIP_SANITY_CHECK=1 -#export EMCC_DEBUG=2 -export EMCC_WASM_BACKEND=0 - - -export EM_EXCLUSIVE_CACHE_ACCESS=1 -#export EMCC_SKIP_SANITY_CHECK=1 -export EMCC_WASM_BACKEND=0 - -mkdir -p "tmp" -TMPDIR=$(realpath "tmp") -export TMPDIR -BC_RENAME_PREFIX=$(mktemp -dt "renaming_links-XXXX") -export BC_RENAME_PREFIX -EM_CACHE=$(mktemp -dt "emscripten_cache-XXXX") -export EM_CACHE -OUT_DIR=$(mktemp -dt "emscripten_out-XXXX") -export OUT_DIR -EMCC_TEMP_DIR=$(mktemp -dt "emscripten_tmp-XXXX") -export EMCC_TEMP_DIR -trap 'rm -rf "$EMCC_TEMP_DIR" "$OUT_DIR" "$EM_CACHE" "$BC_RENAME_PREFIX"' EXIT -# shellcheck disable=SC2089 -# ^^^^ complains that we have literal ' in string, that we _intended_ to have there -# I didn't find a way to structure it so that it stops complaining. -EM_CONFIG+="TEMP_DIR='${EMCC_TEMP_DIR}';" -# shellcheck disable=SC2090 -# ^^^^ false positive due to false positive above -export EM_CONFIG -args=() -tar_name= -# bazel hardcodes `.o` files and `.a` files as outputs of tasks (WHY???). -# LLVM is happy to read&write bitcode to `.o` files -# but explicitly prohibits this for `.a` files. Fool it by creating symbolic links. -for i in "$@"; do - if [[ "$i" =~ ^(.*)\.tar$ ]]; then - tar_name="${BASH_REMATCH[1]}" - folder_name=$(dirname "$i") - mkdir -p "${OUT_DIR}/${folder_name}" - out_name="${OUT_DIR}/${tar_name}.html" - args=("${args[@]}" "$out_name") - - elif [[ "$i" =~ ^(.*)\.a$ ]]; then - core_name="${BASH_REMATCH[1]}" - folder_name=$(dirname "$i") - mkdir -p "$BC_RENAME_PREFIX/$folder_name" - link_name="$BC_RENAME_PREFIX/${core_name}.bc" - cp "$i" "$link_name" - args=("${args[@]}" "$link_name") - elif [[ "$i" =~ ^-march=(.*)$ ]]; then - # nothing - continue - else - args=("${args[@]}" "$i") - fi -done - -if [ -n "$tar_name" ]; then - tar -xf "$EM_CACHE_ARCHIVE" -C "$EM_CACHE" -fi -# Run emscripten to compile and link -#echo "original_args" "$@" -#echo "transformed_args" "${args[@]}" -"$PYTHON" external/emscripten_toolchain/emcc.py "${args[@]}" - -if [ -n "${tar_name}" ]; then - full_tar_path=$(realpath "${tar_name}.tar") - folder_prefix=$(dirname "${OUT_DIR}/${tar_name}") - file_name=$(basename "${OUT_DIR}/${tar_name}") - cd "${folder_prefix}" - tar cf "${full_tar_path}" ./"${file_name}"* -fi diff --git a/vscode_extension/.eslintrc.yaml b/vscode_extension/.eslintrc.yaml index 0b504593f9..26056f8c8d 100644 --- a/vscode_extension/.eslintrc.yaml +++ b/vscode_extension/.eslintrc.yaml @@ -23,6 +23,10 @@ rules: no-plusplus: 0 # This rule generates false positives with typescript enums no-shadow: "off" + quotes: + - error + - double + - avoidEscape: true # This requires way too much space otherwise between fields. lines-between-class-members: - 2 @@ -127,15 +131,23 @@ rules: # Disallow use before definition 'no-use-before-define': [2, {functions: false, classes: false}] + "@typescript-eslint/await-thenable": error + "@typescript-eslint/ban-types": error + extends: - 'eslint-config-airbnb/rules/react' - 'eslint-config-airbnb-base' - prettier plugins: + - "@typescript-eslint" - eslint-comments - prettier - import parser: "@typescript-eslint/parser" +overrides: + - files: '*.ts' + parserOptions: + project: './tsconfig.json' env: es6: true parserOptions: diff --git a/vscode_extension/CHANGELOG.md b/vscode_extension/CHANGELOG.md index 18db110fa3..acb86abe38 100644 --- a/vscode_extension/CHANGELOG.md +++ b/vscode_extension/CHANGELOG.md @@ -1,6 +1,18 @@ # Version history + +## 0.3.26 +- Add option to toggle the auto-complete nudge in `typed: false` files + +## 0.3.25 +- Toggling highlighting of untyped code does not require a restart now + - Sends workspace/didChangeConfiguration notification to LSP server instead + ## 0.3.24 -- Disable the `Copy Symbol to Clipboard` command when there is a text selection. + +- `Copy Symbol to Clipboard` + - Disable command when there is a text selection. + - Show a progress dialog when Sorbet is not ready to process commands. +- Auto-save `__package.rb` files when edited by a quickfix ## 0.3.23 - Fix: Sorbet extension fails when opening a project containing Ruby code but no active configuration. @@ -14,7 +26,7 @@ ## 0.3.20 - `Sorbet` status bar item shows a quick-pick drop down instead of a notification dialog when clicked. -- `Sorbet: Set Log Level…` command allows to control what messages are logged to the `Sorbet` output pane. +- `Sorbet: Set Log Level…` command allows to control what messages are logged to the `Sorbet` output pane. - Defaults to `info` and does not persist between sessions. - The `VSCODE_SORBETEXT_LOG_LEVEL` environment variable, if defined, changes the initial value. Use one of these values: `trace`, `debug`, `info`, `warning`, `error` (case-insensitive). diff --git a/vscode_extension/README.md b/vscode_extension/README.md index 7ef6eca14e..e53c46ae27 100644 --- a/vscode_extension/README.md +++ b/vscode_extension/README.md @@ -44,17 +44,5 @@ for further information. ## Developing on this Extension -``` -cd vscode_extension -code --new-window . -``` - -To test out the changes: - -- Find the "Run and Debug" window -- Ensure that "Launch Extension" is selected in the dropdown -- Click the green triangle, which will launch a new VS Code instance with the - local copy of the extension loaded -- Navigate to the project you want to test Sorbet in - -Or in one command: ⇧⌘P > `Debug: Select and Start Debugging` > `Launch Extension` +See [docs/internals/lsp-dev-guide.md](../docs/internals/lsp-dev-guide.md) for +information on how to get started with LSP and VS Code extension development. diff --git a/vscode_extension/icon.png b/vscode_extension/icon.png deleted file mode 120000 index d000d301a1..0000000000 --- a/vscode_extension/icon.png +++ /dev/null @@ -1 +0,0 @@ -../docs/logo/sorbet-logo@2x.png \ No newline at end of file diff --git a/vscode_extension/icon.png b/vscode_extension/icon.png new file mode 100644 index 0000000000..10748c28e3 Binary files /dev/null and b/vscode_extension/icon.png differ diff --git a/vscode_extension/package.json b/vscode_extension/package.json index 3f5776c59a..12bf02e8ab 100644 --- a/vscode_extension/package.json +++ b/vscode_extension/package.json @@ -4,7 +4,7 @@ "description": "Ruby IDE features, powered by Sorbet.", "author": "Stripe Inc.", "license": "Apache-2.0", - "version": "0.3.23", + "version": "0.3.35", "publisher": "sorbet", "icon": "icon.png", "repository": { @@ -24,12 +24,14 @@ ], "activationEvents": [ "onCommand:sorbet.configure", + "onCommand:sorbet.configureHighlightUntyped", "onCommand:sorbet.disable", "onCommand:sorbet.enable", "onCommand:sorbet.restart", "onCommand:sorbet.setLogLevel", "onCommand:sorbet.showOutput", "onCommand:sorbet.toggleHighlightUntyped", + "onCommand:sorbet.toggleTypedFalseCompletionNudges", "onLanguage:ruby", "workspaceContains:sorbet/*" ], @@ -54,6 +56,12 @@ "title": "Configure", "category": "Sorbet" }, + { + "command": "sorbet.configureHighlightUntyped", + "title": "Configure untyped code highlighting", + "category": "Sorbet", + "enablement": "workbenchState != empty" + }, { "command": "sorbet.copySymbolToClipboard", "title": "Copy Symbol to Clipboard", @@ -70,12 +78,6 @@ "title": "Enable", "category": "Sorbet" }, - { - "command": "sorbet.rename", - "title": "Rename Symbol", - "category": "Sorbet", - "enablement": "editorLangId == ruby" - }, { "command": "sorbet.restart", "title": "Restart", @@ -96,6 +98,12 @@ "title": "Toggle highlighting untyped code", "category": "Sorbet", "enablement": "workbenchState != empty" + }, + { + "command": "sorbet.toggleTypedFalseCompletionNudges", + "title": "Toggle the auto-complete nudge in `typed: false` files", + "category": "Sorbet", + "enablement": "workbenchState != empty" } ], "configuration": { @@ -222,6 +230,15 @@ "format": "uri-reference", "default": "${workspaceFolder}" }, + "env": { + "description": "Environment variables to set when launching sorbet", + "type": "object", + "minItems": 1, + "additionalProperties": { + "type": "string" + }, + "default": {} + }, "command": { "description": "Full command line to invoke sorbet", "type": "array", @@ -236,6 +253,9 @@ "name": "My Custom Sorbet Configuration", "description": "A longer description of this Sorbet Configuration for use in hover text", "cwd": "${workspaceFolder}", + "env": { + "MY_ENV_VAR": "my-value" + }, "command": [ "bundle", "exec", @@ -254,9 +274,18 @@ "default": false }, "sorbet.highlightUntyped": { - "type": "boolean", + "enum": [ + "nowhere", + "everywhere-but-tests", + "everywhere" + ], "description": "Shows warning for untyped values.", - "default": false + "default": "nowhere" + }, + "sorbet.typedFalseCompletionNudges": { + "type": "boolean", + "description": "Displays an auto-complete nudge in `typed: false` files.", + "default": true }, "sorbet.configFilePatterns": { "type": "array", @@ -296,7 +325,7 @@ "test": "yarn compile && ./scripts/run_tests.sh .", "test:ci": "node --preserve-symlinks-main ./out/scripts/runTests.js .", "generate-package": "yarn install && yarn compile && yarn vsce package --out sorbet.vsix", - "vscode-install": "yarn generate-package && code --force --install-extension sorbet.vsix" + "launch": "VSCODE_SORBETEXT_LOG_LEVEL=trace code --extensionDevelopmentPath=$PWD" }, "dependencies": { "async": "^2.6.4", @@ -311,6 +340,7 @@ "@types/node": "^10.11.7", "@types/sinon": "^7.5.0", "@types/vscode": "~1.65", + "@typescript-eslint/eslint-plugin": "^5.17.0", "@typescript-eslint/parser": "^5.17.0", "eslint": "^7.32.0", "eslint-config-airbnb": "^17.1.0", diff --git a/vscode_extension/src/commandIds.ts b/vscode_extension/src/commandIds.ts index d3c923c5a0..2a8950a6d6 100644 --- a/vscode_extension/src/commandIds.ts +++ b/vscode_extension/src/commandIds.ts @@ -1,12 +1,13 @@ /** - * Copy Symbol to Clipboard. + * Configure highlighting of untyped code. */ -export const COPY_SYMBOL_COMMAND_ID = "sorbet.copySymbolToClipboard"; +export const CONFIGURE_HIGHLIGHT_UNTYPED_COMMAND_ID = + "sorbet.configureHighlightUntyped"; /** - * Rename Symbol at a given document position. + * Copy Symbol to Clipboard. */ -export const RENAME_SYMBOL_COMMAND_ID = "sorbet.rename"; +export const COPY_SYMBOL_COMMAND_ID = "sorbet.copySymbolToClipboard"; /** * Set log level available actions. @@ -43,8 +44,18 @@ export const SORBET_ENABLE_COMMAND_ID = "sorbet.enable"; */ export const SORBET_RESTART_COMMAND_ID = "sorbet.restart"; +/** + * Save all open __package.rb files. + */ +export const SORBET_SAVE_PACKAGE_FILES = "sorbet.savePackageFiles"; + /** * Toggle highlighting of untyped code. */ export const TOGGLE_HIGHLIGHT_UNTYPED_COMMAND_ID = "sorbet.toggleHighlightUntyped"; +/** + * Toggle the auto-complete nudge in `typed: false` files. + */ +export const TOGGLE_TYPED_FALSE_COMPLETION_NUDGES_COMMAND_ID = + "sorbet.toggleTypedFalseCompletionNudges"; diff --git a/vscode_extension/src/commands/copySymbolToClipboard.ts b/vscode_extension/src/commands/copySymbolToClipboard.ts index bde2fafd01..c8d21cb8cc 100644 --- a/vscode_extension/src/commands/copySymbolToClipboard.ts +++ b/vscode_extension/src/commands/copySymbolToClipboard.ts @@ -1,4 +1,4 @@ -import { env, window } from "vscode"; +import { env, ProgressLocation, window } from "vscode"; import { SymbolInformation, TextDocumentPositionParams, @@ -14,29 +14,35 @@ export async function copySymbolToClipboard( context: SorbetExtensionContext, ): Promise { const { activeLanguageClient: client } = context.statusProvider; - if (client?.status !== ServerStatus.RUNNING) { - context.log.warning("Sorbet LSP client is not ready."); + + if (!client) { + context.log.warning("CopySymbol: No active Sorbet LSP."); return; } if (!client.capabilities?.sorbetShowSymbolProvider) { context.log.warning( - 'Sorbet LSP client does not support "show symbol" capability.', + "CopySymbol: Sorbet LSP does not support 'showSymbol' capability.", ); return; } const editor = window.activeTextEditor; if (!editor) { - context.log.debug("No active editor to copy symbol from."); + context.log.debug("CopySymbol: No active editor, no target symbol."); return; } if (!editor.selection.isEmpty) { context.log.debug( - "Cannot determine target symbol from a non-empty selection.", + "CopySymbol: Non-empty selection, cannot determine target symbol.", ); - return; // something is selected, abort + return; + } + + if (client.status !== ServerStatus.RUNNING) { + context.log.warning("CopySymbol: Sorbet LSP is not ready."); + return; } const position = editor.selection.active; @@ -46,13 +52,42 @@ export async function copySymbolToClipboard( }, position, }; - const response = await client.sendRequest( - "sorbet/showSymbol", - params, - ); - - await env.clipboard.writeText(response.name); - context.log.debug( - `Copied symbol name to the clipboard. Name:${response.name}`, - ); + + let response: SymbolInformation | undefined; + if (context.statusProvider.operations.length) { + response = await window.withProgress( + { + cancellable: true, + location: ProgressLocation.Notification, + }, + async (progress, token) => { + progress.report({ message: "Querying Sorbet …" }); + const r = await client.sendRequest( + "sorbet/showSymbol", + params, + ); + + if (token.isCancellationRequested) { + context.log.debug( + `CopySymbol: Ignored canceled operation result. Symbol:${r.name}`, + ); + return undefined; + } else { + return r; + } + }, + ); + } else { + response = await client.sendRequest( + "sorbet/showSymbol", + params, + ); + } + + if (response) { + await env.clipboard.writeText(response.name); + context.log.debug( + `CopySymbol: Copied symbol name. Symbol:${response.name}`, + ); + } } diff --git a/vscode_extension/src/commands/renameSymbol.ts b/vscode_extension/src/commands/renameSymbol.ts deleted file mode 100644 index c9ede2b390..0000000000 --- a/vscode_extension/src/commands/renameSymbol.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { commands, Position, Uri } from "vscode"; -import { TextDocumentPositionParams } from "vscode-languageclient"; -import { SorbetExtensionContext } from "../sorbetExtensionContext"; -import { ServerStatus } from "../types"; - -/** - * Rename symbol at {@link TextDocumentPositionParams position}. - * - * Unfortunately, we need this command as a wrapper around `editor.action.rename`, - * because VSCode doesn't allow calling it from the JSON RPC - * https://github.com/microsoft/vscode/issues/146767 - * - * @param context Sorbet extension context. - * @param params Document position. - */ -export async function renameSymbol( - context: SorbetExtensionContext, - params: TextDocumentPositionParams, -): Promise { - if (context.statusProvider.serverStatus !== ServerStatus.RUNNING) { - context.log.warning("Sorbet LSP client is not ready."); - return; - } - - const { - textDocument: { uri }, - position: { line, character }, - } = params; - - try { - commands.executeCommand("editor.action.rename", [ - Uri.parse(uri), - new Position(line, character), - ]); - } catch (error) { - context.log.error( - `Failed to rename symbol at ${uri}:${line}:${character}`, - error instanceof Error ? error : undefined, - ); - } -} diff --git a/vscode_extension/src/commands/savePackageFiles.ts b/vscode_extension/src/commands/savePackageFiles.ts new file mode 100644 index 0000000000..f8b58d471e --- /dev/null +++ b/vscode_extension/src/commands/savePackageFiles.ts @@ -0,0 +1,31 @@ +import { workspace } from "vscode"; +import { basename } from "path"; +import { SorbetExtensionContext } from "../sorbetExtensionContext"; + +/** + * Save all __package.rb files with changes. + * + * @param context Sorbet extension context. + * @return `true` if all the files were successfully saved. + */ +export async function savePackageFiles( + context: SorbetExtensionContext, +): Promise { + const packageDocuments = workspace.textDocuments.filter( + (document) => + document.isDirty && basename(document.fileName) === "__package.rb", + ); + if (!packageDocuments.length) { + context.log.trace("savePackageFiles: nothing to save"); + return true; + } + + const allSaved = await Promise.all( + packageDocuments.map((document) => { + context.log.trace(`savePackageFiles: saving ${document.fileName}`); + return document.save(); + }), + ); + + return allSaved.every((x) => x); +} diff --git a/vscode_extension/src/commands/showSorbetConfigurationPicker.ts b/vscode_extension/src/commands/showSorbetConfigurationPicker.ts index 233d1dd66e..d6137ba766 100644 --- a/vscode_extension/src/commands/showSorbetConfigurationPicker.ts +++ b/vscode_extension/src/commands/showSorbetConfigurationPicker.ts @@ -1,6 +1,6 @@ import { QuickPickItem, window } from "vscode"; -import { SorbetLspConfig } from "../config"; import { SorbetExtensionContext } from "../sorbetExtensionContext"; +import { SorbetLspConfig } from "../sorbetLspConfig"; export interface LspConfigQuickPickItem extends QuickPickItem { lspConfig?: SorbetLspConfig; diff --git a/vscode_extension/src/commands/toggleTypedFalseCompletionNudges.ts b/vscode_extension/src/commands/toggleTypedFalseCompletionNudges.ts new file mode 100644 index 0000000000..61b07c997e --- /dev/null +++ b/vscode_extension/src/commands/toggleTypedFalseCompletionNudges.ts @@ -0,0 +1,20 @@ +import { SorbetExtensionContext } from "../sorbetExtensionContext"; +import { RestartReason } from "../types"; + +/** + * Toggle the auto-complete nudge in `typed: false` files. + * @param context Sorbet extension context. + * @returns `true` if the nudge is enabled, `false` otherwise. + */ +export async function toggleTypedFalseCompletionNudges( + context: SorbetExtensionContext, +): Promise { + const targetState = !context.configuration.typedFalseCompletionNudges; + await context.configuration.setTypedFalseCompletionNudges(targetState); + context.log.info( + `Untyped file auto-complete nudge: ${targetState ? "enabled" : "disabled"}`, + ); + + await context.statusProvider.restartSorbet(RestartReason.CONFIG_CHANGE); + return context.configuration.typedFalseCompletionNudges; +} diff --git a/vscode_extension/src/commands/toggleUntypedCodeHighlighting.ts b/vscode_extension/src/commands/toggleUntypedCodeHighlighting.ts index 07a285fa0f..3fee7ede9c 100644 --- a/vscode_extension/src/commands/toggleUntypedCodeHighlighting.ts +++ b/vscode_extension/src/commands/toggleUntypedCodeHighlighting.ts @@ -1,20 +1,114 @@ +import { QuickPickItem, window } from "vscode"; +import { + TrackUntyped, + ALL_TRACK_UNTYPED, + labelForTrackUntypedSetting, + backwardsCompatibleTrackUntyped, + SorbetExtensionConfig, +} from "../config"; +import { Log } from "../log"; import { SorbetExtensionContext } from "../sorbetExtensionContext"; -import { RestartReason } from "../types"; + +export interface TrackUntypedQuickPickItem extends QuickPickItem { + trackWhere: TrackUntyped; +} + +function toggle(log: Log, configuration: SorbetExtensionConfig): TrackUntyped { + if (configuration.oldHighlightUntyped != null) { + return configuration.oldHighlightUntyped; + } + + switch (configuration.highlightUntyped) { + case "nowhere": + return "everywhere"; + case "everywhere-but-tests": + return "nowhere"; + case "everywhere": + return "nowhere"; + default: + const exhaustiveCheck: never = configuration.highlightUntyped; + log.warning(`Got unexpected state: ${exhaustiveCheck}`); + return "nowhere"; + } +} + +async function cycleStates( + context: SorbetExtensionContext, + targetState: TrackUntyped, + forCommand: string, +) { + const oldHighlightUntyped = context.configuration.highlightUntyped; + context.configuration.oldHighlightUntyped = oldHighlightUntyped; + + await context.configuration.setHighlightUntyped(targetState); + context.log.info(`${forCommand}: Untyped code highlighting: ${targetState}`); +} /** * Toggle highlighting of untyped code. * @param context Sorbet extension context. - * @returns `true` if highlighting is now enabled, `false` otherwise. + * @returns The new TrackUntyped setting */ export async function toggleUntypedCodeHighlighting( context: SorbetExtensionContext, -): Promise { - const targetState = !context.configuration.highlightUntyped; - await context.configuration.setHighlightUntyped(targetState); - context.log.info( - `Untyped code highlighting: ${targetState ? "enabled" : "disabled"}`, +): Promise { + const targetState = toggle(context.log, context.configuration); + await cycleStates(context, targetState, "ToggleUntyped"); + + const { activeLanguageClient: client } = context.statusProvider; + if (client) { + client.sendNotification("workspace/didChangeConfiguration", { + settings: { + highlightUntyped: backwardsCompatibleTrackUntyped( + context.log, + targetState, + ), + }, + }); + } else { + context.log.debug("ToggleUntyped: No active Sorbet LSP to notify."); + } + + return targetState; +} + +/** + * Set highlighting of untyped code to specific setting. + * @param context Sorbet extension context. + */ +export async function configureUntypedCodeHighlighting( + context: SorbetExtensionContext, +): Promise { + const items: TrackUntypedQuickPickItem[] = ALL_TRACK_UNTYPED.map( + (trackWhere) => { + return { + label: labelForTrackUntypedSetting(trackWhere), + trackWhere, + }; + }, ); - await context.statusProvider.restartSorbet(RestartReason.CONFIG_CHANGE); - return context.configuration.highlightUntyped; + const selectedItem = await window.showQuickPick(items, { + placeHolder: "Select where to highlight untyped code", + }); + + if (selectedItem) { + const targetState = selectedItem.trackWhere; + await cycleStates(context, targetState, "ConfigureUntyped"); + + const { activeLanguageClient: client } = context.statusProvider; + if (client) { + client.sendNotification("workspace/didChangeConfiguration", { + settings: { + highlightUntyped: targetState, + }, + }); + } else { + context.log.debug("ConfigureUntyped: No active Sorbet LSP to notify."); + } + + return targetState; + } + + return null; } diff --git a/vscode_extension/src/config.ts b/vscode_extension/src/config.ts index 877745249f..ed2066a739 100644 --- a/vscode_extension/src/config.ts +++ b/vscode_extension/src/config.ts @@ -7,77 +7,70 @@ import { FileSystemWatcher, Memento, workspace, - WorkspaceFolder, } from "vscode"; import * as fs from "fs"; - -/** - * Compare two `string` arrays for deep, in-order equality. - */ -function deepEqual(a: ReadonlyArray, b: ReadonlyArray) { - return a.length === b.length && a.every((itemA, index) => itemA === b[index]); -} - -interface ISorbetLspConfig { - readonly id: string; - /** Display name suitable for short-form fields like menu items or status fields. */ - readonly name: string; - /** Human-readable long-form description suitable for hover text or help. */ - readonly description: string; - readonly cwd: string; - readonly command: ReadonlyArray; -} - -export class SorbetLspConfig { - public readonly id: string; - public readonly name: string; - public readonly description: string; - public readonly cwd: string; - public readonly command: ReadonlyArray; - - constructor({ id, name, description, cwd, command }: ISorbetLspConfig) { - this.id = id; - this.name = name; - this.description = description; - this.cwd = cwd; - this.command = [...command]; +import { Log } from "./log"; +import { SorbetLspConfig, SorbetLspConfigData } from "./sorbetLspConfig"; +import { deepEqual } from "./utils"; + +export type TrackUntyped = "nowhere" | "everywhere-but-tests" | "everywhere"; + +export const ALL_TRACK_UNTYPED: TrackUntyped[] = [ + "nowhere", + "everywhere-but-tests", + "everywhere", +]; + +function coerceTrackUntypedSetting(value: boolean | string): TrackUntyped { + switch (value) { + case true: + return "everywhere"; + case false: + return "nowhere"; + case "nowhere": + case "everywhere-but-tests": + case "everywhere": + return value; + default: + return "nowhere"; } +} - public toString(): string { - return `${this.name}: ${this.description} [cmd: "${this.command.join( - " ", - )}"]`; +export function labelForTrackUntypedSetting(value: TrackUntyped): string { + switch (value) { + case "nowhere": + return "Nowhere"; + case "everywhere-but-tests": + return "Everywhere but tests"; + case "everywhere": + return "Everywhere"; + default: + const unexpected: never = value; + throw new Error(`Unexpected value: ${unexpected}`); } +} - /** Deep equality. */ - public isEqualTo(other: any): boolean { - if ( - this !== other && - (!(other instanceof SorbetLspConfig) || - this.id !== other.id || - this.name !== other.name || - this.description !== other.description || - this.cwd !== other.cwd || - !deepEqual(this.command, other.command)) - ) { +export function backwardsCompatibleTrackUntyped( + log: Log, + trackWhere: TrackUntyped, +): boolean | TrackUntyped { + switch (trackWhere) { + case "nowhere": + return false; + case "everywhere": + return true; + case "everywhere-but-tests": + return trackWhere; + default: + const exhaustiveCheck: never = trackWhere; + log.warning(`Got unexpected state: ${exhaustiveCheck}`); return false; - } - - return true; - } - - /** Deep equality, suitable for use when left and/or right may be null or undefined. */ - public static areEqual( - left: SorbetLspConfig | undefined | null, - right: SorbetLspConfig | undefined | null, - ) { - return left ? left.isEqualTo(right) : left === right; } } -export class SorbetLspConfigChangeEvent { - public readonly oldLspConfig: SorbetLspConfig | null | undefined; - public readonly newLspConfig: SorbetLspConfig | null | undefined; +export interface SorbetLspConfigChangeEvent { + readonly oldLspConfig?: SorbetLspConfig; + readonly newLspConfig?: SorbetLspConfig; } /** @@ -87,18 +80,29 @@ export class SorbetLspConfigChangeEvent { * to make it easier to stub out behavior in tests. */ export interface ISorbetWorkspaceContext extends Disposable { - /** See `vscode.Memento.get`. */ - get(section: string, defaultValue: T): T; + /** + * Get value from {@link ExtensionContext.workspaceState}, if not defined + * fallback to {@link workspace.getConfiguration}, otherwise `defaultValue`. + * @param name Setting name (do not include 'sorbet' prefix). + * @param defaultValue Value to use if no datastore defines the value. + */ + get(name: string, defaultValue: T): T; - /** See `vscode.Memento.update`. */ - update(section: string, value: any): Thenable; + /** + * Set value. Value is saved to {@link extensionContext.workspaceState} unless it + * matches state in {@link workspace.getConfiguration} in which case it is removed + * (effectively setting `value` due to {@link get}'s fallback logic). One caveat is + * that using `undefined` only resets {@link extensionContext.workspaceState}. + * @param name Setting name (do not include 'sorbet' prefix). + * @param value Setting value. + */ + update(name: string, value: any): Promise; - /** See `vscode.workspace.onDidChangeConfiguration` */ + /** + * An event emitted when configuration has changed. + */ onDidChangeConfiguration: Event; - /** See `vscode.workspace.workspaceFolders` */ - workspaceFolders(): ReadonlyArray | undefined; - initializeEnabled(enabled: boolean): void; } @@ -113,12 +117,11 @@ export class DefaultSorbetWorkspaceContext implements ISorbetWorkspaceContext { constructor(extensionContext: ExtensionContext) { this.cachedSorbetConfiguration = workspace.getConfiguration("sorbet"); - this.onDidChangeConfigurationEmitter = new EventEmitter< - ConfigurationChangeEvent - >(); + this.onDidChangeConfigurationEmitter = new EventEmitter(); this.workspaceState = extensionContext.workspaceState; this.disposables = [ + this.onDidChangeConfigurationEmitter, workspace.onDidChangeConfiguration((e) => { if (e.affectsConfiguration("sorbet")) { // update the cached configuration before firing @@ -129,26 +132,33 @@ export class DefaultSorbetWorkspaceContext implements ISorbetWorkspaceContext { ]; } - /** - * Dispose and free associated resources. - */ public dispose() { Disposable.from(...this.disposables).dispose(); } public get(section: string, defaultValue: T): T { - const workspaceStateValue = this.workspaceState.get(`sorbet.${section}`); - if (workspaceStateValue !== undefined) { - return workspaceStateValue; - } - return this.cachedSorbetConfiguration.get(section, defaultValue); + const stateKey = `sorbet.${section}`; + return ( + this.workspaceState.get(stateKey) ?? + this.cachedSorbetConfiguration.get(section, defaultValue) + ); } public async update(section: string, value: any): Promise { - const key = `sorbet.${section}`; - await this.workspaceState.update(key, value); + const stateKey = `sorbet.${section}`; + + const configValue = this.cachedSorbetConfiguration.get(section, value); + if (configValue === value) { + // Remove value from state since configuration's is enough. + await this.workspaceState.update(stateKey, undefined); + } else { + // Save to state since it is being customized. + await this.workspaceState.update(stateKey, value); + } + this.onDidChangeConfigurationEmitter.fire({ - affectsConfiguration: () => true, + affectsConfiguration: (section: string) => + /"^sorbet($|\.)"/.test(section), }); } @@ -156,10 +166,6 @@ export class DefaultSorbetWorkspaceContext implements ISorbetWorkspaceContext { return this.onDidChangeConfigurationEmitter.event; } - public workspaceFolders(): readonly WorkspaceFolder[] | undefined { - return workspace.workspaceFolders; - } - /** * This function is a workaround to make it possible to enable Sorbet on first launch. * @@ -209,7 +215,8 @@ export class SorbetExtensionConfig implements Disposable { /** "Custom" LSP configs that override/supplement "standard" LSP configs. */ private userLspConfigs: ReadonlyArray; private wrappedEnabled: boolean; - private wrappedHighlightUntyped: boolean; + private wrappedHighlightUntyped: TrackUntyped; + private wrappedTypedFalseCompletionNudges: boolean; private wrappedRevealOutputOnError: boolean; constructor(sorbetWorkspaceContext: ISorbetWorkspaceContext) { @@ -221,13 +228,17 @@ export class SorbetExtensionConfig implements Disposable { this.sorbetWorkspaceContext = sorbetWorkspaceContext; this.standardLspConfigs = []; this.userLspConfigs = []; - this.wrappedHighlightUntyped = false; + this.wrappedHighlightUntyped = "nowhere"; + this.wrappedTypedFalseCompletionNudges = true; this.wrappedRevealOutputOnError = false; - const workspaceFolders = this.sorbetWorkspaceContext.workspaceFolders(); - this.wrappedEnabled = workspaceFolders?.length - ? fs.existsSync(`${workspaceFolders[0].uri.fsPath}/sorbet/config`) - : false; + // Any workspace with a `…/sorbet/config` file is considered Sorbet-enabled + // by default. This implementation does not work in the general case with + // multi-root workspaces. + const { workspaceFolders } = workspace; + this.wrappedEnabled = + !!workspaceFolders?.length && + fs.existsSync(`${workspaceFolders[0].uri.fsPath}/sorbet/config`); this.disposables = [ this.onLspConfigChangeEmitter, @@ -270,10 +281,17 @@ export class SorbetExtensionConfig implements Disposable { "revealOutputOnError", this.revealOutputOnError, ); - this.wrappedHighlightUntyped = this.sorbetWorkspaceContext.get( + const highlightUntyped = this.sorbetWorkspaceContext.get( "highlightUntyped", this.highlightUntyped, ); + // Always store the setting as a TrackUntyped enum value internally. + // We'll convert it to legacy-style boolean options (potentially) at the call sites. + this.wrappedHighlightUntyped = coerceTrackUntypedSetting(highlightUntyped); + this.wrappedTypedFalseCompletionNudges = this.sorbetWorkspaceContext.get( + "typedFalseCompletionNudges", + this.typedFalseCompletionNudges, + ); Disposable.from(...this.configFileWatchers).dispose(); this.configFileWatchers = this.configFilePatterns.map((pattern) => { @@ -292,11 +310,11 @@ export class SorbetExtensionConfig implements Disposable { }); this.standardLspConfigs = this.sorbetWorkspaceContext - .get("lspConfigs", []) + .get("lspConfigs", []) .map((c) => new SorbetLspConfig(c)); this.userLspConfigs = this.sorbetWorkspaceContext - .get("userLspConfigs", []) + .get("userLspConfigs", []) .map((c) => new SorbetLspConfig(c)); this.selectedLspConfigId = this.sorbetWorkspaceContext.get< @@ -327,12 +345,32 @@ export class SorbetExtensionConfig implements Disposable { return this.onLspConfigChangeEmitter.event; } + /** + * Get the active {@link SorbetLspConfig LSP config}. + * + * A {@link selectedLspConfig selected} config is only active when {@link enabled} + * is `true`. + */ + public get activeLspConfig(): SorbetLspConfig | undefined { + return this.enabled ? this.selectedLspConfig : undefined; + } + + public get enabled(): boolean { + return this.wrappedEnabled; + } + + public get highlightUntyped(): TrackUntyped { + return this.wrappedHighlightUntyped; + } + + public oldHighlightUntyped: TrackUntyped | undefined = undefined; + /** * Returns a copy of the current SorbetLspConfig objects. */ public get lspConfigs(): ReadonlyArray { const results: Array = []; - const resultIds = new Set(); + const resultIds = new Set(); [...this.userLspConfigs, ...this.standardLspConfigs].forEach((c) => { if (!resultIds.has(c.id)) { results.push(c); @@ -342,70 +380,74 @@ export class SorbetExtensionConfig implements Disposable { return results; } - /** - * Returns the active `SorbetLspConfig`. - * - * If the Sorbet extension is disabled, returns `null`, otherwise - * returns a `SorbetLspConfig` or `undefined` as per `selectedLspConfig`. - */ - public get activeLspConfig(): SorbetLspConfig | null | undefined { - return this.enabled ? this.selectedLspConfig : null; + public get revealOutputOnError(): boolean { + return this.wrappedRevealOutputOnError; } /** - * Returns the selected `SorbetLspConfig`, even if the extension is disabled. + * Get the currently selected {@link SorbetLspConfig LSP config}. * - * If the configuration does not specify a `selectedLspConfigId`, or if - * the `id` refers to a `SorbetLspConfig` that does not exist, return `undefined`. + * Returns `undefined` if {@link selectedLspConfigId} has not been set or if + * its value does not map to a config in {@link lspConfigs}. */ public get selectedLspConfig(): SorbetLspConfig | undefined { return this.lspConfigs.find((c) => c.id === this.selectedLspConfigId); } - /** - * Select the given `SorbetLspConfig`. - * - * (Note that if the extension is disabled, this does not *enable* the - * configuration.) - */ - public async setSelectedLspConfigId(id: string): Promise { - await this.sorbetWorkspaceContext.update("selectedLspConfigId", id); - this.refresh(); + public get typedFalseCompletionNudges(): boolean { + return this.wrappedTypedFalseCompletionNudges; } /** - * Select the given `SorbetLspConfig` and enable the extension, if - * the extension is disabled. + * Set active {@link SorbetLspConfig LSP config}. * - * This is equivalent to calling `selectedLspConfigId = id; enabled=true`. + * If {@link enabled} is `false`, this will change it to `true`. */ public async setActiveLspConfigId(id: string): Promise { - await Promise.all([ - this.sorbetWorkspaceContext.update("selectedLspConfigId", id), - this.sorbetWorkspaceContext.update("enabled", true), - ]); - this.refresh(); - } + const updates: Array> = []; - public get revealOutputOnError(): boolean { - return this.wrappedRevealOutputOnError; - } + if (this.activeLspConfig?.id !== id) { + updates.push( + this.sorbetWorkspaceContext.update("selectedLspConfigId", id), + ); + } + if (!this.enabled) { + updates.push(this.sorbetWorkspaceContext.update("enabled", true)); + } - public get highlightUntyped(): boolean { - return this.wrappedHighlightUntyped; + if (updates.length) { + await Promise.all(updates); + this.refresh(); + } } - public get enabled(): boolean { - return this.wrappedEnabled; + public async setEnabled(enabled: boolean): Promise { + await this.sorbetWorkspaceContext.update("enabled", enabled); + this.refresh(); } - public async setEnabled(b: boolean): Promise { - await this.sorbetWorkspaceContext.update("enabled", b); + public async setHighlightUntyped(trackWhere: TrackUntyped): Promise { + await this.sorbetWorkspaceContext.update("highlightUntyped", trackWhere); this.refresh(); } - public async setHighlightUntyped(b: boolean): Promise { - await this.sorbetWorkspaceContext.update("highlightUntyped", b); + /** + * Set selected {@link SorbetLspConfig LSP config}. + * + * This does not change {@link enabled} state. + */ + public async setSelectedLspConfigId(id: string): Promise { + if (this.selectedLspConfigId !== id) { + await this.sorbetWorkspaceContext.update("selectedLspConfigId", id); + this.refresh(); + } + } + + public async setTypedFalseCompletionNudges(enabled: boolean): Promise { + await this.sorbetWorkspaceContext.update( + "typedFalseCompletionNudges", + enabled, + ); this.refresh(); } } diff --git a/vscode_extension/src/extension.ts b/vscode_extension/src/extension.ts index f519c649f7..fd71325f36 100644 --- a/vscode_extension/src/extension.ts +++ b/vscode_extension/src/extension.ts @@ -1,12 +1,15 @@ import { commands, ExtensionContext, workspace } from "vscode"; -import { TextDocumentPositionParams } from "vscode-languageclient"; import * as cmdIds from "./commandIds"; import { copySymbolToClipboard } from "./commands/copySymbolToClipboard"; -import { renameSymbol } from "./commands/renameSymbol"; +import { savePackageFiles } from "./commands/savePackageFiles"; import { setLogLevel } from "./commands/setLogLevel"; import { showSorbetActions } from "./commands/showSorbetActions"; import { showSorbetConfigurationPicker } from "./commands/showSorbetConfigurationPicker"; -import { toggleUntypedCodeHighlighting } from "./commands/toggleUntypedCodeHighlighting"; +import { + toggleUntypedCodeHighlighting, + configureUntypedCodeHighlighting, +} from "./commands/toggleUntypedCodeHighlighting"; +import { toggleTypedFalseCompletionNudges } from "./commands/toggleTypedFalseCompletionNudges"; import { getLogLevelFromEnvironment, LogLevel } from "./log"; import { SorbetContentProvider, SORBET_SCHEME } from "./sorbetContentProvider"; import { SorbetExtensionContext } from "./sorbetExtensionContext"; @@ -53,11 +56,6 @@ export function activate(context: ExtensionContext) { commands.registerCommand(cmdIds.COPY_SYMBOL_COMMAND_ID, () => copySymbolToClipboard(sorbetExtensionContext), ), - commands.registerCommand( - cmdIds.RENAME_SYMBOL_COMMAND_ID, - (params: TextDocumentPositionParams) => - renameSymbol(sorbetExtensionContext, params), - ), commands.registerCommand( cmdIds.SET_LOGLEVEL_COMMAND_ID, (level?: LogLevel) => setLogLevel(sorbetExtensionContext, level), @@ -82,9 +80,20 @@ export function activate(context: ExtensionContext) { (reason: RestartReason = RestartReason.COMMAND) => sorbetExtensionContext.statusProvider.restartSorbet(reason), ), + commands.registerCommand(cmdIds.SORBET_SAVE_PACKAGE_FILES, () => + savePackageFiles(sorbetExtensionContext), + ), commands.registerCommand(cmdIds.TOGGLE_HIGHLIGHT_UNTYPED_COMMAND_ID, () => toggleUntypedCodeHighlighting(sorbetExtensionContext), ), + commands.registerCommand( + cmdIds.CONFIGURE_HIGHLIGHT_UNTYPED_COMMAND_ID, + () => configureUntypedCodeHighlighting(sorbetExtensionContext), + ), + commands.registerCommand( + cmdIds.TOGGLE_TYPED_FALSE_COMPLETION_NUDGES_COMMAND_ID, + () => toggleTypedFalseCompletionNudges(sorbetExtensionContext), + ), ); // Start the extension. diff --git a/vscode_extension/src/languageClient.ts b/vscode_extension/src/languageClient.ts index 5a9958c39c..71c9ea72f5 100644 --- a/vscode_extension/src/languageClient.ts +++ b/vscode_extension/src/languageClient.ts @@ -1,11 +1,5 @@ import { ChildProcess, spawn } from "child_process"; -import { - CancellationToken, - Disposable, - Event, - EventEmitter, - workspace, -} from "vscode"; +import { Disposable, Event, EventEmitter, workspace } from "vscode"; import { CloseAction, ErrorAction, @@ -21,6 +15,7 @@ import { stopProcess } from "./connections"; import { Tags } from "./metricsClient"; import { SorbetExtensionContext } from "./sorbetExtensionContext"; import { ServerStatus, RestartReason } from "./types"; +import { backwardsCompatibleTrackUntyped } from "./config"; const VALID_STATE_TRANSITIONS: ReadonlyMap< ServerStatus, @@ -52,6 +47,25 @@ function createClient( serverOptions: ServerOptions, errorHandler: ErrorHandler, ) { + const initializationOptions = { + // Opt in to sorbet/showOperation notifications. + supportsOperationNotifications: true, + // Let Sorbet know that we can handle sorbet:// URIs for generated files. + supportsSorbetURIs: true, + highlightUntyped: backwardsCompatibleTrackUntyped( + context.log, + context.configuration.highlightUntyped, + ), + enableTypedFalseCompletionNudges: + context.configuration.typedFalseCompletionNudges, + }; + + context.log.debug( + `Initializing with initializationOptions=${JSON.stringify( + initializationOptions, + )}`, + ); + const client = new LanguageClient("ruby", "Sorbet", serverOptions, { documentSelector: [ { language: "ruby", scheme: "file" }, @@ -59,13 +73,7 @@ function createClient( { language: "ruby", scheme: "sorbet" }, ], outputChannel: context.logOutputChannel, - initializationOptions: { - // Opt in to sorbet/showOperation notifications. - supportsOperationNotifications: true, - // Let Sorbet know that we can handle sorbet:// URIs for generated files. - supportsSorbetURIs: true, - highlightUntyped: context.configuration.highlightUntyped, - }, + initializationOptions, errorHandler, revealOutputChannelOn: context.configuration.revealOutputOnError ? RevealOutputChannelOn.Error @@ -239,13 +247,15 @@ export class SorbetLanguageClient implements Disposable, ErrorHandler { public sendRequest( method: string, param: any, - token?: CancellationToken, ): Promise { - // Do not pass `token` if undefined, otherwise `param` ends up being passed - // as `[...param, undefined]` instead of `param`. - return token - ? this.languageClient.sendRequest(method, param, token) - : this.languageClient.sendRequest(method, param); + return this.languageClient.sendRequest(method, param); + } + + /** + * Send a notification to language server. See {@link LanguageClient.sendNotification}. + */ + public sendNotification(method: string, param: any): void { + this.languageClient.sendNotification(method, param); } /** @@ -277,8 +287,8 @@ export class SorbetLanguageClient implements Disposable, ErrorHandler { private startSorbetProcess(): Promise { this.status = ServerStatus.INITIALIZING; this.context.log.info("Running Sorbet LSP."); - const [command, ...args] = - this.context.configuration.activeLspConfig?.command ?? []; + const activeConfig = this.context.configuration.activeLspConfig; + const [command, ...args] = activeConfig?.command ?? []; if (!command) { const msg = `Missing command-line data to start Sorbet. ConfigId:${this.context.configuration.activeLspConfig?.id}`; this.context.log.error(msg); @@ -288,6 +298,7 @@ export class SorbetLanguageClient implements Disposable, ErrorHandler { this.context.log.debug(` > ${command} ${args.join(" ")}`); this.sorbetProcess = spawn(command, args, { cwd: workspace.rootPath, + env: { ...process.env, ...activeConfig?.env }, }); // N.B.: 'exit' is sometimes not invoked if the process exits with an error/fails to start, as per the Node.js docs. // So, we need to handle both events. ¯\_(ツ)_/¯ diff --git a/vscode_extension/src/metricsClient.ts b/vscode_extension/src/metricsClient.ts index 95efdb3336..db592ce237 100644 --- a/vscode_extension/src/metricsClient.ts +++ b/vscode_extension/src/metricsClient.ts @@ -69,9 +69,12 @@ export class MetricClient { constructor(context: SorbetExtensionContext, api?: Api) { this.apiPromise = api ? Promise.resolve(api) : this.initSorbetMetricsApi(); this.context = context; - const sorbetExtension = extensions.getExtension("sorbet-vscode-extension"); + const sorbetExtension = extensions.getExtension( + "sorbet.sorbet-vscode-extension", + ); this.sorbetExtensionVersion = sorbetExtension?.packageJSON.version ?? "unknown"; + this.emitCountMetric("metrics_client_initialized", 1); } /** @@ -98,6 +101,7 @@ export class MetricClient { sorbetMetricsApi = api as Api; if (!sorbetMetricsApi.metricsEmitter.timing) { this.context.log.info("Timer metrics disabled (unsupported API)."); + sorbetMetricsApi = NoOpApi.INSTANCE; } } else { this.context.log.info("Metrics-gathering disabled (no API)"); diff --git a/vscode_extension/src/sorbetContentProvider.ts b/vscode_extension/src/sorbetContentProvider.ts index d1143a1db3..69868d4127 100644 --- a/vscode_extension/src/sorbetContentProvider.ts +++ b/vscode_extension/src/sorbetContentProvider.ts @@ -22,7 +22,7 @@ export class SorbetContentProvider implements TextDocumentContentProvider { */ public async provideTextDocumentContent( uri: Uri, - token?: CancellationToken, + _token?: CancellationToken, ): Promise { let content: string; const { activeLanguageClient: client } = this.context.statusProvider; @@ -33,7 +33,6 @@ export class SorbetContentProvider implements TextDocumentContentProvider { { uri: uri.toString(), }, - token, ); content = response.text; } else { diff --git a/vscode_extension/src/sorbetLspConfig.ts b/vscode_extension/src/sorbetLspConfig.ts new file mode 100644 index 0000000000..4fc3e53250 --- /dev/null +++ b/vscode_extension/src/sorbetLspConfig.ts @@ -0,0 +1,141 @@ +import { deepEqual, deepEqualEnv } from "./utils"; + +/** + * Sorbet LSP configuration (data-only). + */ +export interface SorbetLspConfigData { + /** + * Configuration Id. + */ + readonly id: string; + /** + * Display name suitable for short-form fields like menu items or status fields. + */ + readonly name: string; + /** + * Human-readable long-form description suitable for hover text or help. + */ + readonly description: string; + /** + * Working directory for {@link command}. + */ + readonly cwd: string; + /** + * Environment variables to set when executing {@link command}. + */ + readonly env: NodeJS.ProcessEnv; + /** + * Command and arguments to execute, e.g. `["srb", "typecheck", "--lsp"]`. + */ + readonly command: ReadonlyArray; +} + +/** + * Sorbet LSP configuration. + */ +export class SorbetLspConfig implements SorbetLspConfigData { + /** + * Configuration Id. + */ + public readonly id: string; + /** + * Display name suitable for short-form fields like menu items or status fields. + */ + public readonly name: string; + /** + * Human-readable long-form description suitable for hover text or help. + */ + public readonly description: string; + /** + * Working directory for {@link command}. + */ + public readonly cwd: string; + /** + * Environment variables to set when executing {@link command}. + */ + public readonly env: NodeJS.ProcessEnv; + /** + * Command and arguments to execute, e.g. `["bundle", "exec", "srb", "typecheck", "--lsp"]`. + */ + public readonly command: ReadonlyArray; + + constructor(data: SorbetLspConfigData); + + constructor(id: string, name: string); + constructor(id: string, name: string, description: string); + constructor(id: string, name: string, description: string, cwd: string); + constructor( + id: string, + name: string, + description: string, + cwd: string, + env: NodeJS.ProcessEnv, + ); + + constructor( + id: string, + name: string, + description: string, + cwd: string, + env: NodeJS.ProcessEnv, + command: ReadonlyArray, + ); + + constructor( + idOrData: string | SorbetLspConfigData, + name: string = "", + description: string = "", + cwd: string = "", + env: NodeJS.ProcessEnv = {}, + command: ReadonlyArray = [], + ) { + if (typeof idOrData === "string") { + this.id = idOrData; + this.name = name; + this.description = description; + this.cwd = cwd; + this.env = { ...env }; + this.command = command; + } else { + this.id = idOrData.id; + this.name = idOrData.name; + this.description = idOrData.description; + this.cwd = idOrData.cwd; + this.env = { ...idOrData.env }; + this.command = [...idOrData.command]; + } + } + + public toString(): string { + return `${this.name}: ${this.description} [cmd: "${this.command.join( + " ", + )}"]`; + } + + /** + * Deep equality. + */ + public isEqualTo(other: any): boolean { + if (this === other) return true; + if (!(other instanceof SorbetLspConfig)) return false; + + return ( + this.id === other.id && + this.name === other.name && + this.description === other.description && + this.cwd === other.cwd && + deepEqualEnv(this.env, other.env) && + deepEqual(this.command, other.command) + ); + } + + /** + * Deep equality, suitable for use when left and/or right may be null or undefined. + */ + public static areEqual( + left: SorbetLspConfig | undefined | null, + right: SorbetLspConfig | undefined | null, + ) { + return left ? left.isEqualTo(right) : left === right; + } +} diff --git a/vscode_extension/src/sorbetStatusBarEntry.ts b/vscode_extension/src/sorbetStatusBarEntry.ts index 3fb5cb71e6..f4b7b76d98 100644 --- a/vscode_extension/src/sorbetStatusBarEntry.ts +++ b/vscode_extension/src/sorbetStatusBarEntry.ts @@ -67,7 +67,7 @@ export class SorbetStatusBarEntry implements Disposable { private render() { const { operations } = this.context.statusProvider; - const { activeLspConfig, highlightUntyped } = this.context.configuration; + const { activeLspConfig } = this.context.configuration; const sorbetName = activeLspConfig?.name ?? "Sorbet"; let text: string; @@ -80,7 +80,7 @@ export class SorbetStatusBarEntry implements Disposable { ) { const latestOp = operations[operations.length - 1]; text = `${sorbetName}: ${latestOp.description} ${this.getSpinner()}`; - tooltip = getRunningTooltip(); + tooltip = "The Sorbet server is currently running."; } else { switch (this.serverStatus) { case ServerStatus.DISABLED: @@ -105,7 +105,7 @@ export class SorbetStatusBarEntry implements Disposable { break; case ServerStatus.RUNNING: text = `${sorbetName}: Idle`; - tooltip = getRunningTooltip(); + tooltip = "The Sorbet server is currently running."; break; default: this.context.log.error(`Invalid ServerStatus: ${this.serverStatus}`); @@ -117,13 +117,5 @@ export class SorbetStatusBarEntry implements Disposable { this.statusBarItem.text = text; this.statusBarItem.tooltip = tooltip; - - function getRunningTooltip() { - let txt = "The Sorbet server is currently running."; - if (highlightUntyped) { - txt += "\n - Highlight untyped code"; - } - return txt; - } } } diff --git a/vscode_extension/src/test/commands/copySymbolToClipboard.test.ts b/vscode_extension/src/test/commands/copySymbolToClipboard.test.ts index 1b554f624a..89d43e190c 100644 --- a/vscode_extension/src/test/commands/copySymbolToClipboard.test.ts +++ b/vscode_extension/src/test/commands/copySymbolToClipboard.test.ts @@ -1,6 +1,5 @@ import * as vscode from "vscode"; import * as vsclc from "vscode-languageclient/node"; -import * as assert from "assert"; import * as path from "path"; import * as sinon from "sinon"; @@ -10,7 +9,7 @@ import { SorbetLanguageClient } from "../../languageClient"; import { LogLevel } from "../../log"; import { SorbetExtensionContext } from "../../sorbetExtensionContext"; import { SorbetStatusProvider } from "../../sorbetStatusProvider"; -import { ServerStatus } from "../../types"; +import { ServerStatus, ShowOperationParams } from "../../types"; suite(`Test Suite: ${path.basename(__filename, ".test.js")}`, () => { let testRestorables: { restore: () => void }[]; @@ -24,7 +23,7 @@ suite(`Test Suite: ${path.basename(__filename, ".test.js")}`, () => { }); test("copySymbolToClipboard: does nothing if client is not present", async () => { - const writeTextSpy = sinon.spy(() => assert.fail()); + const writeTextSpy = sinon.spy(); const envClipboardStub = sinon.stub(vscode, "env").value({ clipboard: { writeText: writeTextSpy, @@ -45,7 +44,7 @@ suite(`Test Suite: ${path.basename(__filename, ".test.js")}`, () => { }); test("copySymbolToClipboard: does nothing if client is not ready", async () => { - const writeTextSpy = sinon.spy(() => assert.fail()); + const writeTextSpy = sinon.spy(); const envClipboardStub = sinon.stub(vscode, "env").value({ clipboard: { writeText: writeTextSpy, @@ -68,7 +67,7 @@ suite(`Test Suite: ${path.basename(__filename, ".test.js")}`, () => { }); test("copySymbolToClipboard: does nothing if client does not support `sorbetShowSymbolProvider`", async () => { - const writeTextSpy = sinon.spy(() => assert.fail()); + const writeTextSpy = sinon.spy(); const envClipboardStub = sinon.stub(vscode, "env").value({ clipboard: { writeText: writeTextSpy, @@ -94,13 +93,11 @@ suite(`Test Suite: ${path.basename(__filename, ".test.js")}`, () => { sinon.assert.notCalled(writeTextSpy); }); - test("copySymbolToClipboard: copies symbol to clipboard whne there is a valid selection", async () => { + test("copySymbolToClipboard: copies symbol to clipboard when there is a valid selection", async () => { const expectedUri = vscode.Uri.parse("file://workspace/test.rb"); const expectedSymbolName = "test_symbol_name"; - const writeTextSpy = sinon.spy((value: String) => - assert.strictEqual(value, expectedSymbolName), - ); + const writeTextSpy = sinon.spy(); const envClipboardStub = sinon.stub(vscode, "env").value({ clipboard: { writeText: writeTextSpy, @@ -118,7 +115,6 @@ suite(`Test Suite: ${path.basename(__filename, ".test.js")}`, () => { }, ); testRestorables.push(activeTextEditorStub); - testRestorables.push(activeTextEditorStub); const sendRequestSpy = sinon.spy( (_method: string, _param: vsclc.TextDocumentPositionParams) => { @@ -134,6 +130,68 @@ suite(`Test Suite: ${path.basename(__filename, ".test.js")}`, () => { sendRequest: sendRequestSpy, status: ServerStatus.RUNNING, }, + operations: >>[], + }; + const context = { + log: createLogStub(LogLevel.Info), + statusProvider, + }; + await copySymbolToClipboard(context); + + sinon.assert.calledOnce(writeTextSpy); + sinon.assert.calledWith(writeTextSpy, expectedSymbolName); + sinon.assert.calledOnce(sendRequestSpy); + sinon.assert.calledWith( + sendRequestSpy, + "sorbet/showSymbol", + sinon.match.object, + ); + }); + + test("copySymbolToClipboard: shows progress dialog when Sorbet is not ready", async () => { + const expectedUri = vscode.Uri.parse("file://workspace/test.rb"); + const expectedSymbolName = "test_symbol_name"; + + const writeTextSpy = sinon.spy(); + const envClipboardStub = sinon.stub(vscode, "env").value({ + clipboard: { + writeText: writeTextSpy, + }, + }); + testRestorables.push(envClipboardStub); + + const activeTextEditorStub = sinon + .stub(vscode.window, "activeTextEditor") + .get( + () => + { + document: { uri: expectedUri }, + selection: new vscode.Selection(1, 1, 1, 1), + }, + ); + testRestorables.push(activeTextEditorStub); + + const progressStub = sinon.stub(vscode.window, "withProgress").resolves(< + vsclc.SymbolInformation + >{ + name: expectedSymbolName, + }); + testRestorables.push(progressStub); + + const statusProvider = { + activeLanguageClient: { + capabilities: { + sorbetShowSymbolProvider: true, + }, + status: ServerStatus.RUNNING, + }, + operations: >>[ + { + description: "Test operation", + operationName: "TestOperation", + status: "start", + }, + ], }; const context = { log: createLogStub(LogLevel.Info), @@ -143,5 +201,58 @@ suite(`Test Suite: ${path.basename(__filename, ".test.js")}`, () => { sinon.assert.calledOnce(writeTextSpy); sinon.assert.calledWith(writeTextSpy, expectedSymbolName); + sinon.assert.calledOnce(progressStub); + }); + + test("copySymbolToClipboard: exits gracefully when cancelled", async () => { + const expectedUri = vscode.Uri.parse("file://workspace/test.rb"); + + const writeTextSpy = sinon.spy(); + const envClipboardStub = sinon.stub(vscode, "env").value({ + clipboard: { + writeText: writeTextSpy, + }, + }); + testRestorables.push(envClipboardStub); + + const activeTextEditorStub = sinon + .stub(vscode.window, "activeTextEditor") + .get( + () => + { + document: { uri: expectedUri }, + selection: new vscode.Selection(1, 1, 1, 1), + }, + ); + testRestorables.push(activeTextEditorStub); + + const progressStub = sinon + .stub(vscode.window, "withProgress") + .resolves(undefined); // Canceled + testRestorables.push(progressStub); + + const statusProvider = { + activeLanguageClient: { + capabilities: { + sorbetShowSymbolProvider: true, + }, + status: ServerStatus.RUNNING, + }, + operations: >>[ + { + description: "Test operation", + operationName: "TestOperation", + status: "start", + }, + ], + }; + const context = { + log: createLogStub(LogLevel.Info), + statusProvider, + }; + await copySymbolToClipboard(context); + + sinon.assert.calledOnce(progressStub); + sinon.assert.notCalled(writeTextSpy); }); }); diff --git a/vscode_extension/src/test/commands/renameSymbol.test.ts b/vscode_extension/src/test/commands/renameSymbol.test.ts deleted file mode 100644 index 3d7590f159..0000000000 --- a/vscode_extension/src/test/commands/renameSymbol.test.ts +++ /dev/null @@ -1,70 +0,0 @@ -import * as vscode from "vscode"; -import * as vsclc from "vscode-languageclient"; -import * as assert from "assert"; -import * as path from "path"; -import * as sinon from "sinon"; - -import { createLogStub } from "../testUtils"; -import { renameSymbol } from "../../commands/renameSymbol"; -import { LogLevel } from "../../log"; -import { SorbetExtensionContext } from "../../sorbetExtensionContext"; -import { SorbetStatusProvider } from "../../sorbetStatusProvider"; -import { ServerStatus } from "../../types"; - -suite(`Test Suite: ${path.basename(__filename, ".test.js")}`, () => { - let testRestorables: { restore: () => void }[]; - - setup(() => { - testRestorables = []; - }); - - teardown(() => { - testRestorables.forEach((r) => r.restore()); - }); - - test("renameSymbol: does nothing if client is not ready", async () => { - const executeStub = sinon.stub(vscode.commands, "executeCommand"); - testRestorables.push(executeStub); - - const statusProvider = { - serverStatus: ServerStatus.ERROR, - }; - const context = { - log: createLogStub(LogLevel.Info), - statusProvider, - }; - await renameSymbol(context, {}); - - sinon.assert.notCalled(executeStub); - }); - - test("renameSymbol: invokes `editor.action.rename`", async () => { - const expectedLine = 77; - const expectedCharacter = 99; - const expectedUri = vscode.Uri.parse("file://workspace/test.rb"); - - const executeStub = sinon - .stub(vscode.commands, "executeCommand") - .resolves(); - testRestorables.push(executeStub); - - const statusProvider = { - serverStatus: ServerStatus.RUNNING, - }; - const context = { - log: createLogStub(LogLevel.Info), - statusProvider, - }; - await renameSymbol(context, { - textDocument: { uri: expectedUri.toString() }, - position: { line: expectedLine, character: expectedCharacter }, - }); - - sinon.assert.calledOnce(executeStub); - const [commandName, [uri, position]] = executeStub.firstCall.args; - assert.strictEqual(commandName, "editor.action.rename"); - assert.strictEqual(uri.toString(), expectedUri.toString()); - assert.strictEqual(position.line, expectedLine); - assert.strictEqual(position.character, expectedCharacter); - }); -}); diff --git a/vscode_extension/src/test/commands/showSorbetActions.test.ts b/vscode_extension/src/test/commands/showSorbetActions.test.ts index dda2c9fa40..3b6b1ead13 100644 --- a/vscode_extension/src/test/commands/showSorbetActions.test.ts +++ b/vscode_extension/src/test/commands/showSorbetActions.test.ts @@ -74,13 +74,13 @@ suite(`Test Suite: ${path.basename(__filename, ".test.js")}`, () => { await assert.doesNotReject(showSorbetActions(context)); sinon.assert.calledOnce(showQuickPickSingleStub); - assert.deepStrictEqual(await showQuickPickSingleStub.firstCall.args[0], [ + assert.deepStrictEqual(showQuickPickSingleStub.firstCall.args[0], [ Action.ViewOutput, Action.RestartSorbet, Action.DisableSorbet, Action.ConfigureSorbet, ]); - assert.deepStrictEqual(await showQuickPickSingleStub.firstCall.args[1], { + assert.deepStrictEqual(showQuickPickSingleStub.firstCall.args[1], { placeHolder: "Select an action", }); }); diff --git a/vscode_extension/src/test/commands/showSorbetConfigurationPicker.test.ts b/vscode_extension/src/test/commands/showSorbetConfigurationPicker.test.ts index eac6491210..56b8fcb520 100644 --- a/vscode_extension/src/test/commands/showSorbetConfigurationPicker.test.ts +++ b/vscode_extension/src/test/commands/showSorbetConfigurationPicker.test.ts @@ -5,8 +5,9 @@ import * as sinon from "sinon"; import { createLogStub } from "../testUtils"; import { showSorbetConfigurationPicker } from "../../commands/showSorbetConfigurationPicker"; -import { SorbetExtensionConfig, SorbetLspConfig } from "../../config"; +import { SorbetExtensionConfig } from "../../config"; import { SorbetExtensionContext } from "../../sorbetExtensionContext"; +import { SorbetLspConfig } from "../../sorbetLspConfig"; suite(`Test Suite: ${path.basename(__filename, ".test.js")}`, () => { let testRestorables: { restore: () => void }[]; @@ -25,6 +26,7 @@ suite(`Test Suite: ${path.basename(__filename, ".test.js")}`, () => { name: "test-config-id-active", description: "", cwd: "", + env: {}, command: [], }); const otherLspConfig = new SorbetLspConfig({ @@ -32,6 +34,7 @@ suite(`Test Suite: ${path.basename(__filename, ".test.js")}`, () => { name: "test-config-id", description: "", cwd: "", + env: {}, command: [], }); @@ -50,7 +53,7 @@ suite(`Test Suite: ${path.basename(__filename, ".test.js")}`, () => { await assert.doesNotReject(showSorbetConfigurationPicker(context)); sinon.assert.calledOnce(showQuickPickSingleStub); - assert.deepStrictEqual(await showQuickPickSingleStub.firstCall.args[0], [ + assert.deepStrictEqual(showQuickPickSingleStub.firstCall.args[0], [ { label: `• ${activeLspConfig.name}`, description: activeLspConfig.description, @@ -68,7 +71,7 @@ suite(`Test Suite: ${path.basename(__filename, ".test.js")}`, () => { description: "Disable the Sorbet extension", }, ]); - assert.deepStrictEqual(await showQuickPickSingleStub.firstCall.args[1], { + assert.deepStrictEqual(showQuickPickSingleStub.firstCall.args[1], { placeHolder: "Select a Sorbet configuration", }); }); diff --git a/vscode_extension/src/test/commands/toggleTypedFalseCompletionNudges.test.ts b/vscode_extension/src/test/commands/toggleTypedFalseCompletionNudges.test.ts new file mode 100644 index 0000000000..8a483f5626 --- /dev/null +++ b/vscode_extension/src/test/commands/toggleTypedFalseCompletionNudges.test.ts @@ -0,0 +1,67 @@ +import * as assert from "assert"; +import * as path from "path"; +import * as sinon from "sinon"; + +import { createLogStub } from "../testUtils"; +import { toggleTypedFalseCompletionNudges } from "../../commands/toggleTypedFalseCompletionNudges"; +import { SorbetExtensionConfig } from "../../config"; +import { SorbetExtensionContext } from "../../sorbetExtensionContext"; +import { SorbetStatusProvider } from "../../sorbetStatusProvider"; +import { RestartReason } from "../../types"; + +suite(`Test Suite: ${path.basename(__filename, ".test.js")}`, () => { + let testRestorables: { restore: () => void }[]; + + setup(() => { + testRestorables = []; + }); + + teardown(() => { + testRestorables.forEach((r) => r.restore()); + }); + + test("toggleTypedFalseCompletionNudges", async () => { + const initialState = true; + let currentState = initialState; + + const log = createLogStub(); + + const setTypedFalseCompletionNudgesSpy = sinon.spy((value: boolean) => { + currentState = value; + }); + const configuration = ({ + get typedFalseCompletionNudges() { + return currentState; + }, + setTypedFalseCompletionNudges: setTypedFalseCompletionNudgesSpy, + }); + + const restartSorbetSpy = sinon.spy((_reason: RestartReason) => {}); + const statusProvider = ({ + restartSorbet: restartSorbetSpy, + }); + + const context = { + log, + configuration, + statusProvider, + }; + + assert.strictEqual( + await toggleTypedFalseCompletionNudges(context), + !initialState, + ); + + sinon.assert.calledOnce(setTypedFalseCompletionNudgesSpy); + sinon.assert.calledWithExactly( + setTypedFalseCompletionNudgesSpy, + !initialState, + ); + + sinon.assert.calledOnce(restartSorbetSpy); + sinon.assert.calledWithExactly( + restartSorbetSpy, + RestartReason.CONFIG_CHANGE, + ); + }); +}); diff --git a/vscode_extension/src/test/commands/toggleUntypedCodeHighlighting.test.ts b/vscode_extension/src/test/commands/toggleUntypedCodeHighlighting.test.ts index aac8611148..596521052c 100644 --- a/vscode_extension/src/test/commands/toggleUntypedCodeHighlighting.test.ts +++ b/vscode_extension/src/test/commands/toggleUntypedCodeHighlighting.test.ts @@ -1,10 +1,19 @@ import * as assert from "assert"; +import * as vscode from "vscode"; import * as path from "path"; import * as sinon from "sinon"; import { createLogStub } from "../testUtils"; -import { toggleUntypedCodeHighlighting } from "../../commands/toggleUntypedCodeHighlighting"; -import { SorbetExtensionConfig } from "../../config"; +import { + toggleUntypedCodeHighlighting, + configureUntypedCodeHighlighting, + TrackUntypedQuickPickItem, +} from "../../commands/toggleUntypedCodeHighlighting"; +import { + SorbetExtensionConfig, + TrackUntyped, + labelForTrackUntypedSetting, +} from "../../config"; import { SorbetExtensionContext } from "../../sorbetExtensionContext"; import { SorbetStatusProvider } from "../../sorbetStatusProvider"; import { RestartReason } from "../../types"; @@ -21,12 +30,12 @@ suite(`Test Suite: ${path.basename(__filename, ".test.js")}`, () => { }); test("toggleUntypedCodeHighlighting", async () => { - const initialState = true; + const initialState = "everywhere"; let currentState = initialState; const log = createLogStub(); - const setHighlightUntypedSpy = sinon.spy((value: boolean) => { + const setHighlightUntypedSpy = sinon.spy((value: TrackUntyped) => { currentState = value; }); const configuration = ({ @@ -47,18 +56,97 @@ suite(`Test Suite: ${path.basename(__filename, ".test.js")}`, () => { statusProvider, }; + assert.strictEqual(await toggleUntypedCodeHighlighting(context), "nowhere"); + + sinon.assert.calledOnce(setHighlightUntypedSpy); + sinon.assert.calledWithExactly(setHighlightUntypedSpy, "nowhere"); + }); + + test("configureUntypedCodeHighlighting", async () => { + const initialState = "everywhere"; + let currentState = initialState; + + const log = createLogStub(); + + const setHighlightUntypedSpy = sinon.spy((value: TrackUntyped) => { + currentState = value; + }); + const configuration = ({ + get highlightUntyped() { + return currentState; + }, + setHighlightUntyped: setHighlightUntypedSpy, + }); + + const restartSorbetSpy = sinon.spy((_reason: RestartReason) => {}); + const statusProvider = ({ + restartSorbet: restartSorbetSpy, + }); + + const context = { + log, + configuration, + statusProvider, + }; + + const expectedTrackWhere = "everywhere-but-tests"; + const showQuickPickSingleStub = sinon + .stub(vscode.window, "showQuickPick") + .resolves({ + label: labelForTrackUntypedSetting(expectedTrackWhere), + trackWhere: expectedTrackWhere, + }); + testRestorables.push(showQuickPickSingleStub); + assert.strictEqual( - await toggleUntypedCodeHighlighting(context), - !initialState, + await configureUntypedCodeHighlighting(context), + expectedTrackWhere, ); - sinon.assert.calledOnce(setHighlightUntypedSpy); - sinon.assert.calledWithExactly(setHighlightUntypedSpy, !initialState); + sinon.assert.calledWithExactly(setHighlightUntypedSpy, expectedTrackWhere); + }); + + test("toggle is sticky", async () => { + const initialState = "everywhere-but-tests"; + let currentState = initialState; + + const log = createLogStub(); + + const setHighlightUntypedSpy = sinon.spy((value: TrackUntyped) => { + currentState = value; + }); + const configuration = ({ + get highlightUntyped() { + return currentState; + }, + setHighlightUntyped: setHighlightUntypedSpy, + }); - sinon.assert.calledOnce(restartSorbetSpy); + const restartSorbetSpy = sinon.spy((_reason: RestartReason) => {}); + const statusProvider = ({ + restartSorbet: restartSorbetSpy, + }); + + const context = { + log, + configuration, + statusProvider, + }; + + assert.strictEqual(await toggleUntypedCodeHighlighting(context), "nowhere"); + assert.strictEqual( + await toggleUntypedCodeHighlighting(context), + initialState, + ); + + sinon.assert.calledTwice(setHighlightUntypedSpy); + sinon.assert.calledWithExactly( + setHighlightUntypedSpy.getCall(0), + "nowhere", + ); sinon.assert.calledWithExactly( - restartSorbetSpy, - RestartReason.CONFIG_CHANGE, + setHighlightUntypedSpy.getCall(1), + initialState, ); }); }); diff --git a/vscode_extension/src/test/config.test.ts b/vscode_extension/src/test/config.test.ts index 9e476cbfda..cb04a16f2f 100644 --- a/vscode_extension/src/test/config.test.ts +++ b/vscode_extension/src/test/config.test.ts @@ -2,39 +2,36 @@ import * as assert from "assert"; import * as sinon from "sinon"; import { EventEmitter, + extensions, ConfigurationTarget, ConfigurationChangeEvent, Uri, - WorkspaceFolder, - extensions, + workspace, } from "vscode"; import * as fs from "fs"; -import { - SorbetExtensionConfig, - SorbetLspConfig, - ISorbetWorkspaceContext, -} from "../config"; +import { SorbetExtensionConfig, ISorbetWorkspaceContext } from "../config"; +import { SorbetLspConfig } from "../sorbetLspConfig"; // Helpers /** Imitate the WorkspaceConfiguration. */ class FakeWorkspaceConfiguration implements ISorbetWorkspaceContext { - public readonly backingStore: Map; - public readonly defaults: Map; + public readonly backingStore: Map; + public readonly defaults: Map; private readonly configurationChangeEmitter: EventEmitter< ConfigurationChangeEvent >; - constructor(properties: Iterable<[String, any]> = []) { - this.backingStore = new Map(properties); + constructor(properties: Iterable<[string, any]> = []) { + this.backingStore = new Map(properties); this.configurationChangeEmitter = new EventEmitter< ConfigurationChangeEvent >(); const defaultProperties = extensions.getExtension( "sorbet.sorbet-vscode-extension", )!.packageJSON.contributes.configuration.properties; - const defaultValues: Iterable<[String, any]> = Object.keys( + const defaultValues: Iterable<[string, any]> = Object.keys( defaultProperties, ).map((settingName) => { let value = defaultProperties[settingName].default; @@ -48,7 +45,7 @@ class FakeWorkspaceConfiguration implements ISorbetWorkspaceContext { return [settingName.replace("sorbet.", ""), value]; }); - this.defaults = new Map(defaultValues); + this.defaults = new Map(defaultValues); } dispose() {} @@ -67,7 +64,7 @@ class FakeWorkspaceConfiguration implements ISorbetWorkspaceContext { section: string, value: any, configurationTarget?: boolean | ConfigurationTarget | undefined, - ): Thenable { + ): Promise { if (configurationTarget) { assert.fail( `fake does not (yet) support ConfigurationTarget, given: ${configurationTarget}`, @@ -87,10 +84,6 @@ class FakeWorkspaceConfiguration implements ISorbetWorkspaceContext { return this.configurationChangeEmitter.event; } - workspaceFolders() { - return [{ uri: { fsPath: "/fake/path/to/project" } }] as WorkspaceFolder[]; - } - initializeEnabled(enabled: boolean): void { const stateEnabled = this.backingStore.get("enabled"); @@ -105,6 +98,7 @@ const fooLspConfig = new SorbetLspConfig({ name: "FooFoo", description: "The foo config", cwd: "${workspaceFolder}", // eslint-disable-line no-template-curly-in-string + env: {}, command: ["foo", "on", "you"], }); @@ -113,6 +107,7 @@ const barLspConfig = new SorbetLspConfig({ name: "BarBar", description: "The bar config", cwd: "${workspaceFolder}/bar", // eslint-disable-line no-template-curly-in-string + env: {}, command: ["I", "heart", "bar", "bee", "que"], }); @@ -123,6 +118,7 @@ suite("SorbetLspConfig", () => { name: "two", description: "three", cwd: "four", + env: {}, command: ["five", "six"], }; const lspConfig = new SorbetLspConfig(ctorArg); @@ -164,6 +160,7 @@ suite("SorbetLspConfig", () => { name: "two", description: "three", cwd: "four", + env: {}, command: ["five", "six"], }; const config1 = new SorbetLspConfig(json); @@ -174,8 +171,8 @@ suite("SorbetLspConfig", () => { new SorbetLspConfig({ ...json, description: "different description" }), new SorbetLspConfig({ ...json, cwd: "different cwd" }), new SorbetLspConfig({ ...json, command: ["different", "command"] }), + new SorbetLspConfig({ ...json, env: { different: "value" } }), undefined, - null, ]; test(".isEqualTo(other)", () => { @@ -226,7 +223,7 @@ suite("SorbetExtensionConfig", async () => { ); assert.strictEqual( sorbetConfig.activeLspConfig, - null, + undefined, "should not have an active LSP config", ); }); @@ -234,15 +231,21 @@ suite("SorbetExtensionConfig", async () => { suite("when a sorbet/config file exists", async () => { test("sorbet is enabled", async () => { - sinon + const expectedWorkspacePath = "/fake/path/to/project"; + const existsSyncStub = sinon .stub(fs, "existsSync") - .withArgs("/fake/path/to/project/sorbet/config") + .withArgs(`${expectedWorkspacePath}/sorbet/config`) .returns(true); + sinon + .stub(workspace, "workspaceFolders") + .value([{ uri: { fsPath: expectedWorkspacePath } }]); const workspaceConfig = new FakeWorkspaceConfiguration(); const sorbetConfig = new SorbetExtensionConfig(workspaceConfig); assert.strictEqual(sorbetConfig.enabled, true, "should be enabled"); + + sinon.assert.calledOnce(existsSyncStub); sinon.restore(); }); }); @@ -281,10 +284,14 @@ suite("SorbetExtensionConfig", async () => { suite("when workspace has *some* sorbet settings", async () => { test("when `sorbet.enabled` is missing", async () => { - sinon + const expectedWorkspacePath = "/fake/path/to/project"; + const existsSyncStub = sinon .stub(fs, "existsSync") - .withArgs("/fake/path/to/project/sorbet/config") + .withArgs(`${expectedWorkspacePath}/sorbet/config`) .returns(false); + sinon + .stub(workspace, "workspaceFolders") + .value([{ uri: { fsPath: expectedWorkspacePath } }]); const workspaceConfig = new FakeWorkspaceConfiguration([ ["lspConfigs", [fooLspConfig, barLspConfig]], @@ -304,10 +311,11 @@ suite("SorbetExtensionConfig", async () => { ); assert.strictEqual( sorbetConfig.activeLspConfig, - null, + undefined, "but should not have an active LSP config", ); + sinon.assert.calledOnce(existsSyncStub); sinon.restore(); }); @@ -373,6 +381,30 @@ suite("SorbetExtensionConfig", async () => { ); }); + suite("sorbet.highlightUntyped", async () => { + test("true instead of a string", async () => { + const workspaceConfig = new FakeWorkspaceConfiguration([ + ["highlightUntyped", true], + ]); + const sorbetConfig = new SorbetExtensionConfig(workspaceConfig); + assert.strictEqual(sorbetConfig.highlightUntyped, "everywhere"); + }); + test("false instead of a string", async () => { + const workspaceConfig = new FakeWorkspaceConfiguration([ + ["highlightUntyped", false], + ]); + const sorbetConfig = new SorbetExtensionConfig(workspaceConfig); + assert.strictEqual(sorbetConfig.highlightUntyped, "nowhere"); + }); + test("unrecognized string", async () => { + const workspaceConfig = new FakeWorkspaceConfiguration([ + ["highlightUntyped", "nope"], + ]); + const sorbetConfig = new SorbetExtensionConfig(workspaceConfig); + assert.strictEqual(sorbetConfig.highlightUntyped, "nowhere"); + }); + }); + test("multiple instances of SorbetExtensionConfig stay in sync with each other", async () => { const workspaceConfig = new FakeWorkspaceConfiguration([ ["enabled", true], @@ -470,7 +502,7 @@ suite("SorbetExtensionConfig", async () => { assert.deepStrictEqual( listener.getCall(0).args[0], { - oldLspConfig: null, + oldLspConfig: undefined, newLspConfig: barLspConfig, }, "should have transitioned from no config to bar config", @@ -497,7 +529,7 @@ suite("SorbetExtensionConfig", async () => { listener.getCall(0).args[0], { oldLspConfig: barLspConfig, - newLspConfig: null, + newLspConfig: undefined, }, "should have transitioned from bar config to no config", ); diff --git a/vscode_extension/src/test/languageClient.test.ts b/vscode_extension/src/test/languageClient.test.ts index 37b3363c08..21426a904d 100644 --- a/vscode_extension/src/test/languageClient.test.ts +++ b/vscode_extension/src/test/languageClient.test.ts @@ -112,16 +112,16 @@ suite("LanguageClient", () => { }, position: { line: 1, character: 1 }, }); - assert.equal( + assert.strictEqual( (successResponse as any).contents, TestLanguageServerSpecialURIs.SUCCESS, ); const metrics = metricsEmitter.getAndResetMetrics(); - assert.equal(metrics.length, 1); + assert.strictEqual(metrics.length, 1); const m = metrics[0]; - assert.equal(m[0], MetricType.Timing); - assert.equal(m[1], `latency.textDocument_hover_ms`); - assert.equal(m[3].success, "true"); + assert.strictEqual(m[0], MetricType.Timing); + assert.strictEqual(m[1], "latency.textDocument_hover_ms"); + assert.strictEqual(m[3].success, "true"); } { @@ -134,16 +134,16 @@ suite("LanguageClient", () => { position: { line: 1, character: 1 }, }, ); - assert.equal( + assert.strictEqual( (successResponse as any).contents, TestLanguageServerSpecialURIs.SUCCESS, ); const metrics = metricsEmitter.getAndResetMetrics(); - assert.equal(metrics.length, 1); + assert.strictEqual(metrics.length, 1); const m = metrics[0]; - assert.equal(m[0], MetricType.Timing); - assert.equal(m[1], `latency.textDocument_hover_ms`); - assert.equal(m[3].success, "true"); + assert.strictEqual(m[0], MetricType.Timing); + assert.strictEqual(m[1], "latency.textDocument_hover_ms"); + assert.strictEqual(m[3].success, "true"); } try { @@ -161,11 +161,11 @@ suite("LanguageClient", () => { ) !== -1, ); const metrics = metricsEmitter.getAndResetMetrics(); - assert.equal(metrics.length, 1); + assert.strictEqual(metrics.length, 1); const m = metrics[0]; - assert.equal(m[0], MetricType.Timing); - assert.equal(m[1], `latency.textDocument_hover_ms`); - assert.equal(m[3].success, "false"); + assert.strictEqual(m[0], MetricType.Timing); + assert.strictEqual(m[1], "latency.textDocument_hover_ms"); + assert.strictEqual(m[3].success, "false"); } try { @@ -178,11 +178,11 @@ suite("LanguageClient", () => { assert.fail("Request should have failed."); } catch (e) { const metrics = metricsEmitter.getAndResetMetrics(); - assert.equal(metrics.length, 1); + assert.strictEqual(metrics.length, 1); const m = metrics[0]; - assert.equal(m[0], MetricType.Timing); - assert.equal(m[1], `latency.textDocument_hover_ms`); - assert.equal(m[3].success, "false"); + assert.strictEqual(m[0], MetricType.Timing); + assert.strictEqual(m[1], "latency.textDocument_hover_ms"); + assert.strictEqual(m[3].success, "false"); } }); }); diff --git a/vscode_extension/src/test/metricsClient.test.ts b/vscode_extension/src/test/metricsClient.test.ts index 7b389ec5e6..c237e1e460 100644 --- a/vscode_extension/src/test/metricsClient.test.ts +++ b/vscode_extension/src/test/metricsClient.test.ts @@ -44,9 +44,9 @@ suite(`Test Suite: ${path.basename(__filename, ".test.js")}`, () => { expectedTags, ); - sinon.assert.calledOnce(incrementStub); + sinon.assert.calledTwice(incrementStub); sinon.assert.calledWithMatch( - incrementStub, + incrementStub.secondCall, `${METRIC_PREFIX}${expectedMetricName}`, expectedCount, expectedTags, diff --git a/vscode_extension/src/test/sorbetLspConfig.test.ts b/vscode_extension/src/test/sorbetLspConfig.test.ts new file mode 100644 index 0000000000..0b8fdf6d55 --- /dev/null +++ b/vscode_extension/src/test/sorbetLspConfig.test.ts @@ -0,0 +1,91 @@ +import * as assert from "assert"; +import * as path from "path"; + +import { SorbetLspConfig } from "../sorbetLspConfig"; + +suite(`Test Suite: ${path.basename(__filename, ".test.js")}`, () => { + const config1 = new SorbetLspConfig( + "test_id", + "test_name", + "test_description", + "test_cwd", + {}, + ["test_command", "test_arg_1"], + ); + const config2 = new SorbetLspConfig( + "test_id", + "test_name", + "test_description", + "test_cwd", + {}, + ["test_command", "test_arg_1"], + ); + const differentConfigs = [ + new SorbetLspConfig( + "different_test_id", + "test_name", + "test_description", + "test_cwd", + {}, + ["test_command", "test_arg_1"], + ), + new SorbetLspConfig( + "test_id", + "different_test_name", + "test_description", + "test_cwd", + {}, + ["test_command", "test_arg_1"], + ), + new SorbetLspConfig( + "test_id", + "test_name", + "different_test_description", + "test_cwd", + {}, + ["test_command", "test_arg_1"], + ), + new SorbetLspConfig( + "test_id", + "test_name", + "test_description", + "different_test_cwd", + {}, + ["test_command", "test_arg_1"], + ), + new SorbetLspConfig( + "test_id", + "test_name", + "test_description", + "test_cwd", + {}, + ["different_test_command", "test_arg_1"], + ), + new SorbetLspConfig( + "test_id", + "test_name", + "test_description", + "test_cwd", + { + different_env_key: "different_env_value", + }, + ["test_command", "test_arg_1"], + ), + undefined, + null, + ]; + + test("isEqualTo(other)", () => { + assert.ok(config1.isEqualTo(config2)); + differentConfigs.forEach((c) => + assert.ok(!config1.isEqualTo(c), `Should not equal: ${c}`), + ); + }); + + test("toString", () => { + assert.strictEqual( + config1.toString(), + 'test_name: test_description [cmd: "test_command test_arg_1"]', + ); + }); +}); diff --git a/vscode_extension/src/test/utils.test.ts b/vscode_extension/src/test/utils.test.ts new file mode 100644 index 0000000000..8783142d34 --- /dev/null +++ b/vscode_extension/src/test/utils.test.ts @@ -0,0 +1,15 @@ +import * as assert from "assert"; +import * as path from "path"; + +import { deepEqual } from "../utils"; + +suite(`Test Suite: ${path.basename(__filename, ".test.js")}`, () => { + test("deepEqual", () => { + assert.ok(deepEqual([], []), "Empty"); + assert.ok(deepEqual(["a", "b", "c"], ["a", "b", "c"]), "Simple"); + + assert.ok(!deepEqual(["a", "b", "c"], []), "Prefix"); + assert.ok(!deepEqual(["a", "b", "c"], ["a", "b"]), "Prefix"); + assert.ok(!deepEqual(["a", "b", "c"], ["c", "b", "a"]), "Out-of-order"); + }); +}); diff --git a/vscode_extension/src/utils.ts b/vscode_extension/src/utils.ts new file mode 100644 index 0000000000..a7488f36ad --- /dev/null +++ b/vscode_extension/src/utils.ts @@ -0,0 +1,20 @@ +/** + * Compare two `string` arrays for deep, in-order equality. + */ +export function deepEqual(a: ReadonlyArray, b: ReadonlyArray) { + return a.length === b.length && a.every((itemA, index) => itemA === b[index]); +} + +export function deepEqualEnv( + a: Readonly, + b: Readonly, +) { + const keysA = Object.keys(a); + const keysB = Object.keys(b); + + return ( + keysA.length === keysB.length && + keysA.every((key) => a[key] === b[key]) && + keysB.every((key) => a[key] === b[key]) + ); +} diff --git a/vscode_extension/yarn.lock b/vscode_extension/yarn.lock index ffb1532b73..b73403f896 100644 --- a/vscode_extension/yarn.lock +++ b/vscode_extension/yarn.lock @@ -30,6 +30,18 @@ dependencies: regenerator-runtime "^0.13.11" +"@eslint-community/eslint-utils@^4.2.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/regexpp@^4.4.0": + version "4.8.1" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.8.1.tgz#8c4bb756cc2aa7eaf13cfa5e69c83afb3260c20c" + integrity sha512-PWiOzLIUAjN/w5K17PoF4n6sKBw0gqLHPhywmYHP4t1VFQQVYeb1yWsJwnMVEMl3tUHME7X/SJPZLmtG7XBDxQ== + "@eslint/eslintrc@^0.4.3": version "0.4.3" resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c" @@ -127,6 +139,11 @@ "@types/minimatch" "*" "@types/node" "*" +"@types/json-schema@^7.0.9": + version "7.0.13" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.13.tgz#02c24f4363176d2d18fc8b70b9f3c54aba178a85" + integrity sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ== + "@types/json5@^0.0.29": version "0.0.29" resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" @@ -152,6 +169,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.60.tgz#35f3d6213daed95da7f0f73e75bcc6980e90597b" integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw== +"@types/semver@^7.3.12": + version "7.5.2" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.2.tgz#31f6eec1ed7ec23f4f05608d3a2d381df041f564" + integrity sha512-7aqorHYgdNO4DM36stTiGO3DvKoex9TQRwsJU6vMaFGyqpBA1MNZkz+PG3gaNUPpTAOYhT1WR7M1JyA3fbS9Cw== + "@types/sinon@^7.5.0": version "7.5.2" resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-7.5.2.tgz#5e2f1d120f07b9cda07e5dedd4f3bf8888fccdb9" @@ -162,6 +184,22 @@ resolved "https://registry.yarnpkg.com/@types/vscode/-/vscode-1.65.0.tgz#042dd8d93c32ac62cb826cd0fa12376069d1f448" integrity sha512-wQhExnh2nEzpjDMSKhUvnNmz3ucpd3E+R7wJkOhBNK3No6fG3VUdmVmMOKD0A8NDZDDDiQcLNxe3oGmX5SjJ5w== +"@typescript-eslint/eslint-plugin@^5.17.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz#aeef0328d172b9e37d9bab6dbc13b87ed88977db" + integrity sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag== + dependencies: + "@eslint-community/regexpp" "^4.4.0" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/type-utils" "5.62.0" + "@typescript-eslint/utils" "5.62.0" + debug "^4.3.4" + graphemer "^1.4.0" + ignore "^5.2.0" + natural-compare-lite "^1.4.0" + semver "^7.3.7" + tsutils "^3.21.0" + "@typescript-eslint/parser@^5.17.0": version "5.59.8" resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.59.8.tgz#60cbb00671d86cf746044ab797900b1448188567" @@ -180,11 +218,34 @@ "@typescript-eslint/types" "5.59.8" "@typescript-eslint/visitor-keys" "5.59.8" +"@typescript-eslint/scope-manager@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz#d9457ccc6a0b8d6b37d0eb252a23022478c5460c" + integrity sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w== + dependencies: + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/visitor-keys" "5.62.0" + +"@typescript-eslint/type-utils@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz#286f0389c41681376cdad96b309cedd17d70346a" + integrity sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew== + dependencies: + "@typescript-eslint/typescript-estree" "5.62.0" + "@typescript-eslint/utils" "5.62.0" + debug "^4.3.4" + tsutils "^3.21.0" + "@typescript-eslint/types@5.59.8": version "5.59.8" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.59.8.tgz#212e54414733618f5d0fd50b2da2717f630aebf8" integrity sha512-+uWuOhBTj/L6awoWIg0BlWy0u9TyFpCHrAuQ5bNfxDaZ1Ppb3mx6tUigc74LHcbHpOHuOTOJrBoAnhdHdaea1w== +"@typescript-eslint/types@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f" + integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ== + "@typescript-eslint/typescript-estree@5.59.8": version "5.59.8" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.8.tgz#801a7b1766481629481b3b0878148bd7a1f345d7" @@ -198,6 +259,33 @@ semver "^7.3.7" tsutils "^3.21.0" +"@typescript-eslint/typescript-estree@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz#7d17794b77fabcac615d6a48fb143330d962eb9b" + integrity sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA== + dependencies: + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/visitor-keys" "5.62.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/utils@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.62.0.tgz#141e809c71636e4a75daa39faed2fb5f4b10df86" + integrity sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@types/json-schema" "^7.0.9" + "@types/semver" "^7.3.12" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/typescript-estree" "5.62.0" + eslint-scope "^5.1.1" + semver "^7.3.7" + "@typescript-eslint/visitor-keys@5.59.8": version "5.59.8" resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.8.tgz#aa6a7ef862add919401470c09e1609392ef3cc40" @@ -206,6 +294,14 @@ "@typescript-eslint/types" "5.59.8" eslint-visitor-keys "^3.3.0" +"@typescript-eslint/visitor-keys@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz#2174011917ce582875954ffe2f6912d5931e353e" + integrity sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw== + dependencies: + "@typescript-eslint/types" "5.62.0" + eslint-visitor-keys "^3.3.0" + acorn-es7-plugin@^1.0.12: version "1.1.7" resolved "https://registry.yarnpkg.com/acorn-es7-plugin/-/acorn-es7-plugin-1.1.7.tgz#f2ee1f3228a90eead1245f9ab1922eb2e71d336b" @@ -1431,6 +1527,11 @@ graceful-fs@^4.1.2, graceful-fs@^4.2.2: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== + growl@1.10.5: version "1.10.5" resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" @@ -2044,6 +2145,11 @@ mute-stream@~0.0.4: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== +natural-compare-lite@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz#17b09581988979fddafe0201e931ba933c96cbb4" + integrity sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g== + natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" diff --git a/website/blog/2019-12-20-announcing-sorbet-0.5.md b/website/blog/2019-12-20-announcing-sorbet-0.5.md index a7de2b6118..4a448add3e 100644 --- a/website/blog/2019-12-20-announcing-sorbet-0.5.md +++ b/website/blog/2019-12-20-announcing-sorbet-0.5.md @@ -148,8 +148,8 @@ created by subclassing `T::Enum`, and individual values are instances of that class, created by calling `new`. Because of this, **enums in Sorbet are naturally type-safe**: one enum value cannot be used where some other enum is expected, and vice versa. (By comparison, existing Ruby code often uses symbols -like `:spades` or `:hearts` for enums, but all symbols are interchangable at the -type-level, so they provide no type safety.) +like `:spades` or `:hearts` for enums, but all symbols are interchangeable at +the type-level, so they provide no type safety.) Enums work hand-in-hand with exhaustiveness checks by design: diff --git a/website/docs/abstract.md b/website/docs/abstract.md index da214ee390..db045f0b1a 100644 --- a/website/docs/abstract.md +++ b/website/docs/abstract.md @@ -122,6 +122,152 @@ As the example shows, there are two main steps: Note: if you want to provide functionality in an abstract class or module that **must not** be possible to override in a child, use a [final method](final.md). +## Letting abstract methods be implemented via inheritance + +Sorbet allows abstract methods in modules to be implemented by an **ancestor** +of the class or module they're eventually mixed into. Consider this example: + +```ruby +class Parent + sig { void } + def foo = puts 'Hello!' +end + +module IFoo + extend T::Helpers + abstract! + + sig { abstract.void } + def foo; end +end + +class Child < Parent # ✅ okay + include IFoo +end + +class NotAParent # ❌ Missing definition for `foo` + include IFoo +end +``` + +Breaking down this example: + +- The `IFoo` module declares a single, abstract `foo` method. All classes that + include this module must either be marked abstract or define this method. + +- The `Parent` method does **not** depend on `IFoo`, **but** does happen to + define a method called `foo`. + +- Both `Child` and `NotAParent` have `include IFoo`, but neither define a `foo` + method. + +- Despite this: only `NotAParent` has an error saying that a concrete + implementation of `foo` is missing. Sorbet allows the `foo` method to be + implemented in `Child` because it inherits a `foo` method from `Parent`. + +### Approximating duck types + +This technique is particularly useful as a way to approximate "duck typing," +where you depend on "anything type, so long as it has this method." + +For example: + +```ruby +module ShortName + extend T::Helpers + abstract! + sig { abstract.returns(T.nilable(String)) } + def name; end + + sig { returns(T.nilable(String)) } + def short_name + self.name&.split('::')&.last + end +end +``` + +This module provides a `short_name` method (which computes the "short name" of a +something like a `Module` by splitting the full name into `::`-delimited tokens +and returning the last one. Like `C` for `module A::B::C`). + +The module's implementation depends on the `name` method existing. If we don't +declare it as an `abstract` method, Sorbet reports an error saying "Method +`name` does not exist," which is true--there's no guarantee someone mixes this +module into a context where `name` is defined. + +But by declaring `name` as an abstract method, Sorbet will check this property. +In particular, this has the effect of catching someone who accidentally uses +`include` instead of `extend` when mixing this module into a class: + +```ruby +class A + include ShortName # ❌ error: Must define abstract method `name` +end + +class B + extend ShortName # ✅ +end +``` + +This technique is particularly effective when it's not possible to refactor some +upstream dependency's code to expose an explicit interface. The `Module` class +in the Ruby stdlib doesn't have some sort of public `INameable` interface with +the `name` method. A handful of database model classes in an application might +share a set of related fields, without explicitly implementing some interface. +And yet, using this technique Sorbet allows writing modules which depend on +those implicit interfaces. + +This technique is also quite flexible: the `ShortName` module can be used in +**any** context where a `name` method is available. So for example, if you had +some `T::Struct` that stores a `name`, this `ShortName` mixin could also be +used: + +```ruby +class C < T::Struct + include ShortName + prop :name, String +end + +C.new(name: "Some::Long::Namespace").short_name # => "Namespace" +``` + +### Consequences for runtime signature checking + +Letting abstract methods be implemented by inherited methods relies on the fact +that method signatures are [checked at runtime](runtime.md). To explain why this +feature requires runtime support, let's look at the resolved ancestors of +`Child`: + +``` +irb> Child.ancestors +=> [Child, IFoo, Parent, <...>] +``` + +This shows that Ruby resolves a call like `child.foo` by first checking whether +`Child` defines `foo`, then whether `IFoo` defines `foo`, and then finally +whether `Parent` does. Since it looks in `IFoo` **before** `Parent`, Ruby +actually calls the `IFoo#foo` method. But this method would normally have an +empty method body—it's abstract! + +So at runtime, the `sig` method replaces the implementation of `foo` with a +method that does something like this: + +```ruby +def foo + if defined?(super) + super + else + raise NotImplementedError.new("Call to unimplemented abstract method") + end +end +``` + +This allows `IFoo#foo` to dispatch up the ancestor chain, letting `child.foo` +result in a call to `Parent#foo`. + +If runtime signature checking is disabled, a call like `child.foo` will silently +produce `nil` instead of calling the appropriate method. + ## Abstract singleton methods `abstract` singleton methods on a module are not allowed, as there's no way to @@ -141,6 +287,53 @@ end M.foo # error: `M.foo` can never be implemented ``` +Abstract singleton methods on a class **are** allowed, but are unsound (i.e., +they can lead to runtime, type-related exceptions like `TypeError` and +`NameError` even when there is no `T.untyped` involved): + +```ruby +class AbstractParent + abstract! + sig { abstract.void } # ❌ BAD: abstract singleton class method! + def self.foo; end +end + +class ConcreteChild < AbstractParent + sig { override.void } + def self.foo = puts("hello!") +end + +sig { params(klass: T.class_of(AbstractParent)).void } +def example(klass) + klass.foo +end + +example(ConcreteChild) # ✅ okay +example(AbstractParent) # static: ✅ no errors + # runtime: 💥 call to abstract method foo +``` + +For more information, see this blog post: + +[Abstract singleton class methods are an abomination →](https://blog.jez.io/abstract-singleton-methods) + +The blog post above discusses the problem and three alternatives to avoid using +abstract singleton class methods. To summarize: + +1. Declare an interface or abstract module with abstract instance methods, and + `extend` that module onto a class. + +1. Use the above approach, but with + [`mixes_in_class_methods`](#interfaces-and-the-included-hook), discussed + below. + +1. Make the method `overridable` instead of `abstract`, effectively giving the + method a default implementation. + +There are also some runtime escape hatches to work around this problem. See +[Runtime reflection on abstract classes](#runtime-reflection-on-abstract-classes) +below. + ## Interfaces and the `included` hook A somewhat common pattern in Ruby is to use an `included` hook to mix class @@ -220,6 +413,11 @@ modules that mixin in their own class modules. In these cases, you will need to declare multiple modules in the `mixes_in_class_methods` call or make multiple `mixes_in_class_methods` calls. +For a more comprehensive resource on how `mixes_in_class_methods` builds on +existing Ruby inheritance features, see this blog post: + +[Inheritance in Ruby, in pictures →](https://blog.jez.io/inheritance-in-ruby) + ## Runtime reflection on abstract classes From time to time, it's useful to be able to ask whether a class or module @@ -241,8 +439,9 @@ end Note that in general, having to ask whether a module is abstract is a **code smell**. There is usually a way to reorganize the code such that calling `abstract_module?` isn't needed. In particular, this happens most frequently -from the use of modules with abstract singleton class methods (abstract `self.` -methods), and the fix is to stop using abstract singleton class methods. +from the use of modules with +[abstract singleton class methods](#abstract-singleton-methods) (abstract +`self.` methods), and the fix is to stop using abstract singleton class methods. Here's an example: diff --git a/website/docs/adopting.md b/website/docs/adopting.md index 6d9e797b28..946e1c7a2c 100644 --- a/website/docs/adopting.md +++ b/website/docs/adopting.md @@ -22,7 +22,7 @@ We'll declare them in our Gemfile and install them with Bundler: gem 'sorbet', :group => :development gem 'sorbet-runtime' -gem 'tapioca', require: false, :group => :development +gem 'tapioca', require: false, :group => [:development, :test] ``` ```plaintext @@ -40,7 +40,7 @@ Alternatively we can use the `sorbet-static-and-runtime` gem to install both # -- Gemfile -- gem 'sorbet-static-and-runtime' -gem 'tapioca', require: false, :group => :development +gem 'tapioca', require: false, :group => [:development, :test] ``` Note that this is not the recommended way to add Sorbet to our project if we diff --git a/website/docs/autocompletion.md b/website/docs/autocompletion.md new file mode 100644 index 0000000000..504ca498cc --- /dev/null +++ b/website/docs/autocompletion.md @@ -0,0 +1,202 @@ +--- +id: autocompletion +title: Autocompletion +sidebar_label: Autocompletion +--- + +Sorbet supports autocompletion via LSP. + + + +Some basic features of autocompletion in Sorbet: + +- Sorbet supports completion for method calls, local, instance, and class + variables, Ruby keywords, classes, and constants. + +- Completion items will include documentation for the suggestion, when + available. Sorbet assumes that a comment immediately above the definition of + things like classes, methods, and constants is the documentation for a given + item. + + Different language clients may or may not show this information by default. + +- Completion items are sorted by inheritance. Methods defined lower in the + inheritance hierarchy show up higher in the completion results. (This is not a + perfect heuristic, but it is how Sorbet works.) + +There are some more complicated features worth calling out in their own section. + +## Troubleshooting + +### No completion results + +Support for autocompletion is minimal in `# typed: false` files, +[like most other LSP features](lsp-typed-level.md#support-by-lsp-feature). + +If the file is `# typed: true` and there are still no completion results, it +might be that Sorbet is busy with an ongoing operation. See +[Showing the Language Server Status](server-status.md) to understand what the +server statuses mean, and whether Sorbet is able to respond to completion +requests given a certain status. + +If Sorbet is "Idle" (or "Typechecking in background...") and there are still no +completion results, it might be that Sorbet failed to recover from a syntax +error in the file. See +[Syntax error recovery and completion](#syntax-error-recovery-and-completion). + +If there is no syntax error, it might be that the code path is unreachable. +Sorbet does not show completion results for dead/unreachable code paths. + +### Wrong completion results + +If the completion results are present but look wrong, inspect the kind of +completion item. For example, VS Code uses these icons to show the completion +item kinds: + +![](/img/lsp/vscode-completion-list.png) + +Sorbet will only ever return completion items with the kind `method`, +`variable`, `field`, `class`, `interface`, `module`, `enum`, or `keyword` (and +sometimes `snippet`). + +**Notably**, the "abc" icon (`word`) means the results came either from VS +Code’s `editor.wordBasedSuggestions` setting or some other generic autocomplete +extension. (Or, if not using VS Code, then from some other plugin.) Sorbet +**never** produces `word` completion items. + +In specific circumstances (see +[Completion for method signatures](#completion-for-method-signatures) and +[Completion for YARD snippets](#completion-for-yard-snippets)), Sorbet will also +produce `snippet` items. In all other cases, `snippet` items come from some +other snippet provider, not Sorbet. + +If the completion results still look wrong, please +[report a bug](https://github.com/sorbet/sorbet/issues/new/choose). + +## Syntax error recovery and completion + +One of the biggest reasons why Sorbet fails to produce completion results is +because it failed to recover from a syntax error in the file. + +Sorbet has a custom Ruby syntax parser which attempts to recover from many +common syntax errors, but it is not perfect. For example: + +```ruby +def foo(x) + x. +end +``` + +This Ruby program has a syntax error, and yet Sorbet is able to recover from it, +understanding that this method contains a call on `x` which is missing the +method name. + +To see all the known cases where Sorbet fails to recover from parsing a file +with a syntax error, see +[all issues labeled `parser`](https://github.com/sorbet/sorbet/issues?q=is%3Aissue+is%3Aopen+label%3Aparser). + +The biggest known case where Sorbet fails to recover is with mismatched +parentheses, brackets, and quotes. + +If you think you've found another example that Sorbet should be able to recover +from, please open an issue. The [Sorbet Playground](https://sorbet.run) allows +showing the parse result for a file, which is useful for debugging whether +Sorbet parsed or failed to parse a given syntax error: simply pass [the +`?arg=--print=parse-tree-whitequark` flag][bad-parse] in the query string. + +[bad-parse]: + https://sorbet.run/?arg=--print=parse-tree-whitequark#%23%20typed%3A%20true%0A%23%20Craft%20your%20test%20case%2C%20then%20click%20%22Create%20issue%20with%20example%22%0A%23%20in%20the%20%22Examples%20%E2%98%B0%22%20menu%20above.%0Adef%20foo%0A%20%20puts%20'hello'%0Aend + +## Completion for method signatures + +Sorbet can suggest signatures for methods without signatures. Simply typing +`sig` above a method without a signature will trigger a completion item that +expands to a snippet with one parameter in the `sig` for each parameter in the +method definition immediately following the `sig`. + +For more information, see +[Automatically suggesting method signatures](sig-suggestion.md) + +![](/img/suggest-sig-completion-item-01.png) + +## Pre-declared snippets + +Snippets expand a pre-defined template and allow pieces of the template to be +filled in with values (most language clients allow pressing `TAB` to cycle +through the parts of the template that need to be filled in). + +Sorbet includes some snippet autocompletion results. These show up in addition +to any snippets that editors might already be configured with. Snippets from +Sorbet always show up prefixed with `(sorbet)`: + +![](/img/lsp/struct-snippet.png) + +In VS Code, snippets also show up with a "square with dashed bottom line" icon. +See [Wrong completion results](#wrong-completion-results). + +If a snippet has the "square with dashed bottom line" icon but does not start +with `(sorbet)`, it came from some other extension in your developer environment +and is configured separately. + +These are the snippets Sorbet includes. + +### Snippets for all Ruby keywords. + +For example, typing `case` brings up a completion item which, when accepted, +expands to + +```ruby +case expr +when expr + +else +end +``` + +Other notable keyword snippets include snippets for `def`, `class`, `module`, +and `if`. + +### Snippets for Sorbet-specific constructs. + +You can type `struct` or `enum` and have these auto-expand to +[T::Struct](tstruct.md) and [T::Enum](tenum.md) classes: + + + +The `struct` and `enum` snippet triggers are not methods that exist at runtime: +they're only indicators to Sorbet that you're trying to define a `T::Struct` or +`T::Enum`. + +These completions will only show up if there is no explicit receiver for a +method call (e.g., `x.struct` will not show the `T::Struct` snippet suggestion, +only `struct`). + +### Completion for YARD snippets + +Sorbet can suggest a YARD doc snippet for methods. + + + +It will appear after typing `##` above a Ruby method or signature, and +pre-populate the snippet with one `@param` for each parameter the method has. +Some reminders about these docs: + +- Sorbet recognizes Markdown syntax in these docs, and VS Code will render the + Markdown to rich text when showing the documentation. + + Get creative and show examples of how to use your API! + +- These docs show up on hover and in autocompletion items. + +- Sorbet **does not** read YARD docs for type annotations. + +- See [the YARD docs][available-yard-tags] for a list of available tags. + +[available-yard-tags]: + https://rubydoc.info/gems/yard/file/docs/Tags.md#List_of_Available_Tags diff --git a/website/docs/class-of.md b/website/docs/class-of.md index ae8352bbb7..eb62e337cc 100644 --- a/website/docs/class-of.md +++ b/website/docs/class-of.md @@ -355,7 +355,7 @@ some things that are only possible to represent with `T::Class`. `T::Class[MyInterface]` work. By contrast, it's simply a syntax error to write `T.class_of(T.any(A, B))` - (because this doesn't resolve to a single attached class), and + (because this doesn't resolve to a single singleton class), and `T.class_of(MyInterface)` means something different from what people might otherwise expect it to mean. diff --git a/website/docs/cli.md b/website/docs/cli.md index 012ce7ca6e..53e2bb234c 100644 --- a/website/docs/cli.md +++ b/website/docs/cli.md @@ -308,6 +308,83 @@ foo.rb:3: Expected `String` but found `Symbol(:"symbol")` for argument `arg0` ... ``` +## `--cache-dir`: Caching parse results + +Sorbet can cache the result of parsing files. If only a few files change between +consecutive runs of Sorbet, Sorbet can skip substantial amounts of work creating +abstract syntax trees from files, which speeds up `srb tc` at the command line +and the "Indexing..." operation in editors. + +To enable `--cache-dir`, simply pass `--cache-dir=...` when invoking `srb tc` +(or add this option to the project's [config file](#config-file)). Replace `...` +with a path to where Sorbet should write cached data to disk. This `...` can +either be: + +- a path to a directory that doesn't exist (will be created by Sorbet) +- a path to an empty directory +- a path to a cache directory populated by a previous run of Sorbet + +For example: + +```bash +# Creates or reuses a cache dir at `.sorbet-cache/` in the current dir +srb tc --cache-dir=.sorbet-cache + +# Creates or reuses a cache dir at `/tmp/sorbet-cache/`, within the system's +# `/tmp` folder. +srb tc --cache-dir=/tmp/sorbet-cache +``` + +Under the hood, Sorbet creates two files in this folder (`data.mdb` and +`lock.mdb`). + +We strongly recommend setting `--cache-dir`, especially in medium- to +large-sized codebases. The parsing and AST rewriting phases of Sorbet are some +of the least optimized parts of Sorbet, because historically this caching +strategy has been so effective. + +### What is cached? What is evicted? + +The cache is a simple key/value store. The majority of the cache maps keys that +look like `path/to/file.rb##` to a compact binary representation of +Sorbet's internal abstract syntax tree data structure. This means that if +`srb tc` runs twice on a project, but the contents of `path/to/file.rb` change +between the first and second run, there will be two keys in the cache which +begin with `path/to/file.rb`, one for each version of the file. This also means +that when checking out an old branch which has already had Sorbet run on it, all +tracked and unmodified files will still be in the cache. + +This compact binary representation has no stability guarantees, meaning it is +not forward nor backward compatible with new versions of Sorbet. Instead, Sorbet +completely flushes the cache whenever the Sorbet version string +(`srb tc --version`) changes. + +(This version string is only populated correctly for release builds of +Sorbet—when using a custom source build of Sorbet which doesn't build Sorbet in +release mode, avoid using `--cache-dir`.) + +Apart from evictions when the Sorbet version changes (e.g. upgrading Sorbet in +the Gemfile, or checking out an old commit with an older Sorbet version), Sorbet +never evicts data from this cache. It can grow without bound. If disk space is +limited, consider +[Collecting metrics from Sorbet](metrics.md#collecting-metrics-from-sorbet), +paying attention to these metrics: + +- `cache.used_bytes` +- `cache.used_percent` + +(Note that these metrics are only reported when the cache is modified, not when +it's read.) + +To simplify Sorbet's implementation against the underlying key/value store +library, the max cache size is fixed when Sorbet starts up. The default max +cache size is 4 GiB. The size on disk will only take up as much data as needs to +be cached (i., not a fixed 4 GiB). For projects which need more than this, you +can use the `--max-cache-size-bytes` to set a larger cache size. If you find +yourself needing to pass this option, please reach out to the Sorbet development +team, as your codebase is likely huge (possibly the largest known Sorbet +codebase) and we'd like to talk to you. + ## Is there a way to get errors in JSON format? There is not, intentionally. If you're trying to consume Sorbet's output from a diff --git a/website/docs/code-actions.md b/website/docs/code-actions.md new file mode 100644 index 0000000000..2fed7f869b --- /dev/null +++ b/website/docs/code-actions.md @@ -0,0 +1,138 @@ +--- +id: code-actions +title: Code Actions +sidebar_label: Code Actions +--- + +Sorbet supports code actions via LSP. + +![](/img/suggest-sig-code-action-01.png) + +In VS Code, code actions can be accessed by clicking the 💡 icon on the line +that the cursor is on. (If there are no available code actions at the cursor, +there will be no 💡 icon). + +Sorbet supports a number of code actions. + +## Quick Fix code actions + +Sorbet produces a `quickfix` code action for every correctable error. When there +is a quick fix code action available for an error message, Sorbet includes "(fix +available)" at the end of the error message: + +![](/img/lsp/fix-available.png) + +Accept the code action in VS Code by placing the cursor on the error message and +waiting for the 💡 icon to appear. Other language clients may have keyboard +shortcuts for this. For example in VS Code, use `Cmd` + `.` (or `Ctrl` + `.` on +Linux). + +Quick Fix code actions are one-to-one with command line autocorrects. See +[Accepting autocorrect suggestions](cli.md#accepting-autocorrect-suggestions) +for more information on autocorrects. + +### Apply all fixes for file + +Sorbet implements a custom code action kind which allows applying all `quickfix` +code actions in the current file. This can be useful especially when upgrading a +file from `# typed: false` to `true`, or `true` to `strict`. + +For example, when upgrading a file from `# typed: false` to `# typed: true`, +many of the errors are likely to be errors arising from possibly-`nil` +references. It can be useful to simply silence all these errors at once by +adding `T.must` wherever Sorbet suggests, and then possibly revisit the +autocorrects manually to decide whether any given `T.must` is the best path +forward. + +As another example, Sorbet requires signatures and type annotations for all +definitions in `# typed: strict` files. Sorbet includes autocorrects to insert +these annotations alongside the error message, so applying all fixes for a file +after upgrading to `# typed: strict` is a quick way to get rudimentary type +annotations for a file that previously had none or few. + +## Refactor code actions + +Sorbet has a handful of refactor code actions. These code actions are not +associated with any particular error message. They aim to automate common edits. + +**Note**: Most of the refactor code actions in Sorbet are limited by the +functionality of the LSP specification. In particular, the LSP specification +provides virtually no way to prompt for user input before running a code action. + +### Move method to a new module + +> **Trigger**: Cursor on a singleton class method + +This code action moves a singleton class method to a new module. This can be +useful to split up a module which has accreted multiple unrelated helper +methods: + + + +As the recording above shows, Sorbet will: + +- Create a new module to hold the method, at the top of the current file. + +- Move the method body to that module. + +- Rename all calls to the method to use the new module's name. + +This is an especially useful tool to split dependencies in heavily-tangled code. +In codebases that use an autoloader (most Rails codebases), writing +`Helpers.compute_qux` requires loading the entire `Helpers` file, which might +cause a bunch of other code to load, even if that code isn't used by the +`compute_qux` method. Splitting `compute_qux` into its own module in a separate +file can be a way to optimize code loading. + +Due to limitations in the LSP specification, Sorbet cannot prompt for the name +of the new module to create, nor put it in a separate file. It chooses a new +name based on the name of the method, and inserts the module at the top of the +file. To choose a different name, use Rename Symbol to change the name after +extracting the method to its own module. (The new name can be a fully-qualified +or namespaced name, like `A::B::C`.) + + + +### Convert to singleton class method + +> **Trigger**: Cursor on an instance method + +This code action converts an instance method to a singleton class method on the +same class. + + + +As the recording above shows, Sorbet will: + +- Change the definition from `def method_name` to `def self.method_name` + +- Add a new method parameter to the definition called `this` in both the method + definition and the signature (if the method has one). + +- Refactor all call sites like `x.method_name(...)` to `X.method_name(x, ...)`. + +At the moment, this code action does not update the method body itself to +rewrite method calls on `self` (implicitly or explicitly) to calls on the new +`this` method parameter. + +This can be useful for example to decouple logic from database models. Over time +it can be problematic to have lots of behavior and business logic accumulate on +database models. Moving instance methods to singleton class methods can be a +first step towards factoring those methods into new modules (See also: the +[Move method to a new module](#move-method-to-a-new-module) code action.) + +### Delete `T.unsafe` / `T.must` + +> **Trigger**: Cursor on a call to `T.unsafe` or `T.must` + +This code action deletes a `T.unsafe`. Sometimes it might be interesting to see +the error that a call to `T.unsafe` or `T.must` is silencing (or to check +whether there still are any such errors.) + +This code action simply changes `T.unsafe(expr)` to `expr`. + + diff --git a/website/docs/error-reference.md b/website/docs/error-reference.md index dc04cda668..4071417934 100644 --- a/website/docs/error-reference.md +++ b/website/docs/error-reference.md @@ -63,11 +63,9 @@ There was a Ruby syntax error. Sorbet was unable to parse the source code. If you encounter this error but your code is accepted by Ruby itself, this is a bug in our parser; please [report an issue] to us so we can address it. -The only intentional break with Ruby compatibility is that method names that are -keywords have some limitations with multi-line code, as explained in [#1993], -and should not be reported. - -[#1993]: https://github.com/sorbet/sorbet/pull/1993 +The only intentional break with the Ruby grammar is that method names that are +keywords have some limitations with multi-line code, as explained in +[Unsupported Ruby Features](unsupported.md#multi-line-calls-to-to-keyword-named-methods-with-trailing-). ## 2002 @@ -441,17 +439,20 @@ end ``` For more examples of valid syntax, -[see the tests](https://github.com/sorbet/sorbet/blob/master/test/testdata/rewriter/minitest_tables.rb). +[see the tests](https://github.com/sorbet/sorbet/blob/master/test/testdata/rewriter/minitest_tables.rb), +and +[another test, with describe](https://github.com/sorbet/sorbet/blob/master/test/testdata/rewriter/test_each_describe.rb). There are some limitations on how `test_each` can be used: The block given to `test_each` must accept at least one argument (except when using `test_each_hash`, it must be able to take exactly two arguments). -The body of the `test_each`'s block must contain only `it` blocks, because of -limitations in Sorbet. Sorbet models `it` blocks by translating them to method -definitions under the hood, and method definitions do not have access to -variables outside of their scope. +The body of the `test_each`'s block must contain only `it`, `before`, `after`, +and `describe` blocks, because of limitations in Sorbet. Sorbet models `it`, +`before`, and `after` blocks by translating them to method definitions under the +hood, and method definitions do not have access to variables outside of their +scope. Usually this comes up with variable destructuring: @@ -584,8 +585,6 @@ not exist. > This error is specific to Stripe's custom `--stripe-packages` mode. If you are > at Stripe, please see [go/modularity](http://go/modularity) for more. -### Import/export statements - All `import` and `export` lines in a `__package.rb` file must have constant literals as their argument. Doing arbitrary computation of imports and exports is not allowed in `__package.rb` files. @@ -593,15 +592,6 @@ is not allowed in `__package.rb` files. Also note that all `import` declarations must be unique, with no duplicated imports. -### autoloader_compatibility declarations - -> See [go/pbal](http://go/pbal) for more details. - -`autoloader_compatibility` declarations must take a single String argument. The -only allowed value is `legacy`, otherwise the declaration cannot be present. -These declarations annotate a package as incompatible for path-based autoloading -and are used by our Ruby code loading pipeline. - ## 3707 > This error is specific to Stripe's custom `--stripe-packages` mode. If you are @@ -881,6 +871,9 @@ that all constants in a Sorbet codebase must resolve, even at `# typed: false`. Parsing `include` blocks is required for this, so incorrect usages of `include` are reported when encountered. +To fix, ensure that the `include` or `extend` line is given at least one +argument. + ## 4002 Sorbet requires seeing the complete inheritance hierarchy in a codebase. To do @@ -953,6 +946,8 @@ that all constants in a Sorbet codebase must resolve, even at `# typed: false`. Parsing `include` blocks is required for this, so incorrect usages of `include` are reported when encountered. +To fix, ensure that the `include` or `extend` line is not given a block. + ## 4006 The `super` keyword in Ruby will call the method with the same name on the @@ -1459,10 +1454,69 @@ A class was changed to inherit from a different superclass. class A; end class B; end +if RUBY_VERSION < "3.0" + class C < A; end +else + class C < B; end # error: Parent of class C redefined from A to B +end +``` + +Sorbet requires that a project have a single, unified inheritance hierarchy. A +class cannot sometimes inherit from one class and sometimes inherit from another +class depending on code at runtime. + +If it's simply not possible to refactor the codebase so that the given class +always descends from one superclass, the next path forward is to hide all but +one of the superclasses of this class. For example, using a trick like this with +`# typed: ignore` files: + +```ruby +# -- main.rb -- +class A; end +class B; end + +if RUBY_VERSION < "3.0" + require_relative './c_a.rb' +else + require_relative './c_b.rb' +end + +class C + # ... +end + +# -- c_a.rb -- +# typed: ignore class C < A; end + +# -- c_b.rb -- class C < B; end ``` +In this example, Sorbet will think that `C` always descends from `B` statically, +even though sometimes it may descend from `A`. + +Another trick to hide the superclass involves using `Class.new` to hide the +definition of `C` from Sorbet entirely. + +Neither of these tricks work when one of the class definitions with a +conflicting superclass definition comes from Sorbet's definitions for standard +library classes. For example, this happens when generating an RBI for a newer +version of a gem which is also include in Sorbet's stdlib RBI payload. + +In these cases, Sorbet provides the +`--suppress-payload-superclass-redefinition-for` command line flag, which +suppresses the superclass redefinition error for that class. For example, if the +class `C` above were a class defined in Sorbet's payload, this would silence the +errors: + +``` +srb tc --suppress-payload-superclass-redefinition-for=C +``` + +This flag can be repeated once per payload class with a superclass redefinition +error. + ## 5013 A class or instance variable declaration used `T.cast` when it should use @@ -1542,6 +1596,21 @@ instead of instance methods (`def foo`). For more information, see the docs for [Generics](generics.md). + + +**Why does this apply even to `fixed` types?** Sorbet requires redeclaring even +`fixed` type members and templates. This differs from many other typed, +object-oriented languages that support generic classes. This choice is an +implementation detail which simplifies some logic inside Sorbet, specifically +around responding incrementally to file edits in an editor. It's _not_ +fundamental to Ruby or the type system semantics Sorbet chooses to implement, +which means that one day we could consider changing Sorbet to support this +(which would require adjusting Sorbet's algorithm for handling incremental +updates). + +For a more technical explanation, see +[the Sorbet source code](https://github.com/sorbet/sorbet/blob/233b47bb46fcedb60982fd7f70148f07a147468f/resolver/GlobalPass.cc#L54-L63). + ## 5015 [Variance]() @@ -1713,7 +1782,7 @@ end ## 5020 There are a few constraints around how methods like `include`, `extend`, -`mixes_in_class_methods` [(docs)](abstract.mdd), and `requires_ancestor` +`mixes_in_class_methods` [(docs)](abstract.md), and `requires_ancestor` [(docs)](requires-ancestor.md) work. - `include` and `extend` must be given a constant that Sorbet can _statically_ @@ -1790,6 +1859,9 @@ recursively-defined data structure, see ## 5025 +> **Note**: This error has been relaxed in newer Sorbet versions. It has been +> replaced by [5075](#5075). + Sorbet does not currently support type aliases in generic classes. This limitation was crafted early in the development of Sorbet to entirely @@ -1797,9 +1869,6 @@ sidestep problems that can arise in the design of a type system leading to unsoundness. (Sorbet users curious about type system design may wish to read [What is Type Projection in Scala, and Why is it Unsound?](https://lptk.github.io/programming/2019/09/13/type-projection.html).) -We are likely to reconsider lifting this limitation in the future, but have no -immediate plans yet. - As a workaround, define type aliases somewhere else. Unfortunately this does mean it is not currently possible to define type aliases that reference type members defined by a generic class. @@ -1990,18 +2059,27 @@ intent to create a new type alias.) ## 5035 -A method was marked `override`, but sorbet was unable to find a method in the -class's ancestors that would be overridden. Ensure that the method being -overridden exists in the ancestors of the class defining the `override` method, -or remove `override` from the signature that's raising the error. See -[Override Checking](override-checking.md) for more information about `override`. +There are two cases when Sorbet might produce this error code: + +1. A method was marked `override`, but sorbet was unable to find a method in + the class's ancestors that would be overridden. Ensure that the method being + overridden exists in the ancestors of the class defining the `override` + method, or remove `override` from the signature that's raising the error. + + If the parent method definitely exists at runtime, it might be hidden in a + [`# typed: ignore`](static#file-level-granularity-strictness-levels) file, + or defined via metaprogramming. To fix, either raise the `typed` sigil of + that file above `ignore`, or generate an [RBI file](rbi) that contains + signatures for the classes and methods that file defines. -If the parent method definitely exists at runtime, it might be hidden in a -[`# typed: ignore`](static#file-level-granularity-strictness-levels) file. -Sorbet will not see it and this error will be raised. In that case you will need -to either raise the `typed` sigil of that file above `ignore`, or generate an -[RBI file](rbi) that contains signatures for the classes and methods that file -defines. +1. Sorbet found the method that this method overrides, but the override method + is not valid. An override method must accept at least as many arguments as + the parent method, with types at least as wide as the parent's types, and + return a value that is at most as wide as the parent's return type. This is + in keeping with the + [Liskov substitution principle](https://en.wikipedia.org/wiki/Liskov_substitution_principle). + +See [Override Checking](override-checking.md) for more. ## 5036 @@ -2193,32 +2271,7 @@ If this workaround will not work in your case, the final alternative is to either omit a signature for the method in question (or define an explicit signature where all parameters that cannot be typed accurately use `T.untyped`). -**Why are overloads not well-supported in Sorbet?** - -Consider how overloading works in typed, compiled languages like C++ or Java; -each overload is a separate method. They actually have separate implementations, -are type checked separately, compile (with link-time name mangling) to separate -symbols in the compiled object, and the compiler knows how to resolve each call -site to a specific overload ahead of time, either statically or dynamically via -virtual dispatch. - -Meanwhile, Ruby itself doesn't have overloading—there's only ever one method -registered with a given name in the VM, regardless of what arguments it accepts. -That complicates things. It becomes unclear how Sorbet should typecheck the body -of the method (against all sigs? against one sig? against the component-wise -union of their arguments?). There's no clear answer, and anything we choose will -be bound to confuse or surprise someone. - -Also because Sorbet doesn't control whether the method can be dispatched to, -even if it were going to make a static claim about whether the code type checks, -it doesn't get to control which (fake) overload will get dispatched to at the -call site (again: there's only one version of the method in the VM). - -Finally this choice is somewhat philosophical: codebases that make heavy use of -overloading (even in typed languages where overloading is supported) tend to be -harder for readers to understand at a glance. The above workaround of defining -multiple methods with unique names solves this readability problem, because now -each overload has a descriptive name. +For more, see [Methods with Overloaded Signatures](overloads.md). ## 5041 @@ -2775,6 +2828,17 @@ typechecked. See [`type_member` & `type_template`](generics.md#type_member--type_template) in the generics docs for more. +There are two main cases where this error is reported: + +1. Using a `type_member` in a scope where only a `type_template` is allowed + (and vice versa), but within the correct class. +2. Using either a `type_member` or `type_template` in a completely unrelated + class. + +Neither is allowed, but in each case the solution is different. + +### `type_member` / `type_template` mismatch + A `type_member` is limited in scope to only appear in instance methods of the given class. A `type_template` is limited in scope to only appear in singleton class methods of the given class. For example: @@ -2849,6 +2913,91 @@ class MyClass < AbstractSerializable end ``` +### In an unrelated class + +In this case, the error usually indicates a more fundamental problem with the +design, which means that the way forward is more nuanced. + +Consider a class like this: + +```ruby +class Box + extend T::Generic + Elem = type_member + + sig { params(val: Elem).void } + def initialize(val); @val = val; end + + sig { returns(Elem) } + def val; @val; end +end +``` + +We might want to write a function which gets the element out of an arbitrary +`Box`: + +```ruby +sig do + type_parameters(:Elem) + .params(box: Box[T.type_parameter(:Elem)]) + .returns(Box[T.type_parameter(:Elem)]::Elem) + # ^^^^^^ ❌ +end +def example_bad1(box) + box.val +end +``` + +This code does not work, because we can't access arbitrary type members inside a +class. In this case, the fix is easy enough: just +`returns(T.type_parameter(:Elem))`: + +```ruby +sig do + type_parameters(:Elem) + .params(box: Box[T.type_parameter(:Elem)]) + .returns(T.type_parameter(:Elem)) # ✅ +end +def example_good1(box) + box.val +end +``` + +Another thing that might cause problems: we want to make the `type_member` +fixed, and then access it like a type alias: + +```ruby +class ComplicatedBox < Box + Elem = type_member { {fixed: T.any(Integer, String, Float, Symbol)} } +end + +sig { params(box: ComplicatedBox).returns(ComplicatedBox::Elem) } +# ^^^^^^^^^^^^^^^^^^^^ ❌ +def example_bad2(box) + box.val +end +``` + +Type members are not type aliases. If you want something that works like a type +alias, make a type alias, and then use that both in the `fixed` annotation of +the type member, and in any signature where you'd like to use that type: + +```ruby +class ComplicatedBox < Box + Type = T.type_alias { T.any(Integer, String, Float, Symbol) } + Elem = type_member { {fixed: Type} } # ✅ +end + +sig { params(box: ComplicatedBox).returns(ComplicatedBox::Type) } +# ^^^^^^^^^^^^^^^^^^^^ # ✅ +def example_good2(box) + box.val +end +``` + +For more information on why type members are not type aliases and vice versa, +see [5075](#5075). + ## 5073 Abstract classes cannot be instantiated by definition. See @@ -2886,6 +3035,108 @@ into another module, both modules must declare `has_attached_class!`. For more information, see the docs for [`T.attached_class`](attached-class.md). +## 5075 + +Sorbet does not support type aliases that alias to generic type members (or type +templates, which are just +[type members owned by the singleton class](generics.md#type_member--type_template)). +At the moment, there is no perfect workaround, though in the future Sorbet will +support this by allowing generic type members to be bounded by other generic +type members. The current best option is simply to copy the contents of the type +alias into all the places where you would like to use that type alias. + +Here's why Sorbet does not allow type _aliases_ to alias to generic type +members. Consider this motivating example, of a generic box type which can be +possibly empty: + +```ruby +class PossiblyEmptyBox + extend T::Generic + + class IsEmpty; end + + Elem = type_member + MaybeElem = T.type_alias { T.any(IsEmpty, Elem) } # ❌ NOT allowed + + # ... + + sig { returns(Elem) } + def val!; end + + sig { returns(MaybeElem) } + def val; end +end +``` + +Our class is generic in `Elem`, so that this container can hold an arbitrary +type. But we have a lot of methods that will return [either](union-types.md) the +`Elem` that this box contains, or an instance of some `IsEmpty` class if there +is nothing currently in the box. (Presumably: there are a lot of methods where +`MaybeElem` is used, and we don't want to have to repeat ourselves by writing +`T.any(IsEmpty, Elem)` all over the place.) + +This is not allowed, because the restrictions on where type aliases can be used +from are not the same as those that apply to type members: + +- Type aliases can be used anywhere, regardless of where the type alias is + defined. + +- Type members can only be used **inside** the enclosing class where they were + defined (as if they were declared with `private_constant`) **and** can only be + used inside methods and instance variables of the instance class (or for type + templates: methods and instance variables of the singleton class). + +If Sorbet were to allow type members to appear in type aliases, that type alias +would have to essentially copy all the rules of where that type alias is allowed +to be used from the type members inside it. + +But at that point, the type alias would be no different from defining a second, +[`fixed`](generics.md#bounds-on-type_members-and-type_templates-fixed-upper-lower) +type member on the same class: + +```ruby +class PossiblyEmptyBox + # ... + Elem = type_member + MaybeElem = type_member { + {fixed: T.any(IsEmpty, Elem)} + # ^ should be allowed in the future + } + + sig { returns(Elem) } + def val!; end + + sig { returns(MaybeElem) } + def val; end +end +``` + +The `fixed` annotation makes `MaybeElem` exactly like a type alias with the same +scope as `Elem`. This solution avoids having to solve the problem of "copying +all the scoping rules of a type member onto a type alias." + +There is only one gotcha: at the moment Sorbet does not support using type +members in `fixed`, `upper`, or `lower` bounds of a type member. We expect to +relax this constraint in the future, which means that at the moment there is no +way to define something that acts like a type alias to a generic type member. + +The only way forward is to copy the contents of the type alias into all the +places where you'd like to use that type alias, like this: + +```ruby +class PossiblyEmptyBox + # ... + Elem = type_member + + sig { returns(Elem) } + def val!; end + + # Use `T.any` instead of `MaybeElem` + sig { returns(T.any(IsEmpty, Elem)) } + def val; end +end +``` + ## 6001 Certain Ruby keywords like `break`, `next`, and `retry` can only be used inside @@ -3260,7 +3511,7 @@ x = T.let(1, String) # error: Argument does not have asserted type `String` ``` Because of the way default values are desugared by Sorbet, this error also -occurs when Sorbet finds a mistmatch between the type specified for a parameter +occurs when Sorbet finds a mismatch between the type specified for a parameter in the signature and the default value provided in the method. In this case, the signature states that `category` type is a `Category`, yet we @@ -3536,7 +3787,7 @@ sig {params(xs: Integer).void} def foo(*xs); end xs = Array.new(3) {|i| i} -T.unsafe(self).foo(xs) +T.unsafe(self).foo(*xs) # --------------------------------------------- ``` @@ -3904,7 +4155,7 @@ sig do .void end def example(x) - x.foo # error! + x.foo # ❌ error! end ``` @@ -3935,7 +4186,7 @@ sig do .void end def example(x) - x.foo # error! + x.foo # ✅ no error end ``` @@ -4139,6 +4390,105 @@ the guarantees of the type system. This feature is opt-in. See [VS Code](vscode.md) for instructions on how to turn it on. +## 7048 + +This error is like [7003](#7003), but for calls using `super`. + +That is, the difference is that Sorbet checks whether a method with the same +name as the enclosing method exists on any ancestor of the current method. + +This error is caused by most of the same reasons that cause [7003](#7003), so +it's best to read the docs for that code as well. + +See also: + +- [Why is `super` untyped, even when the parent method has a `sig`?] +- [How can I fix type errors that arise from `super`?] + +[Why is `super` untyped, even when the parent method has a `sig`?]: + /docs/faq#why-is-super-untyped-even-when-the-parent-method-has-a-sig +[How can I fix type errors that arise from `super`?]: + /docs/faq#how-can-i-fix-type-errors-that-arise-from-super + +## 7049 + +See [Methods with Overloaded Signatures](overloads.md) for complete docs on how +to declare overloaded methods correctly. + +## 7050 + +Calling `T.must` with an untyped object is an indication that code that was +considered typed (and `T.must` was ensuring that the value was not `nil`) is not +typed anymore. + +This was separated from [7015](#7015) to maintain this error in +`# typed: strict` and above, but allow for [7015](#7015) to be enforced at lower +strictness levels. + +## 7051 + +When a method's signature is marked `.void`, Sorbet replaces the result value of +the method with a special singleton value, so that callers downstream of the +method cannot depend on whatever value the method happens to return. + +(See +[`returns` & `void`: Annotating return types](sigs.md#returns--void-annotating-return-types)) + +One thing to be aware of is that this special singleton value is truthy (because +the only two values that are falsy in the Ruby VM are `nil` and `false`). + +Thus, branching on this special void singleton value is virtually always a bug. + +If a method **must be able to return anything** in its body, while still +branching on the resulting value, use `.returns(T.anything)` instead: + +```ruby +sig { returns(T.anything) } +def example + if [true, false].sample + '' + else + false + end +end +``` + +See the docs for [T.anything](anything.md) for more. + +This error happens if _any_ component of the type includes `void`. For example: + +```ruby +result = + if [true, false].sample + returns_void + else + false + end + +T.reveal_type(result) # => T.any(Sorbet::Private::Static::Void, FalseClass) + +if result +# ^^^^^^ error + puts +end +``` + +This is expected, and the way to fix it is again either to change `returns_void` +to `returns(T.anything)`, or update the `if` branch to explicitly produce a +value: + +```ruby +result = + if [true, false].sample + returns_void + true + else + false + end + +T.reveal_type(result) # => T::Boolean +``` + [report an issue]: https://github.com/sorbet/sorbet/issues diff --git a/website/docs/faq.md b/website/docs/faq.md index fa51b8e74d..ed5b9f3330 100644 --- a/website/docs/faq.md +++ b/website/docs/faq.md @@ -57,6 +57,33 @@ sig {void} [→ Method Signatures](sigs.md) +## What's the signature for a method that returns multiple values? + +Ruby's `return x, y` syntax is just shorthand for `return [x, y]`. + +For methods using this multiple return shorthand, the return type should be +either: + +- an [Array](stdlib-generics.md) + + ```ruby + sig {return(T::Array[Integer])} + def example + return 1, 2 + end + ``` + +- a [Tuple](tuples.md) (experimental) + + ```ruby + sig {return([Integer, String])} + def example + return 1, '' + end + ``` + +[→ Method Signatures](sigs.md) + ## How should I add types to methods defined with `attr_reader`? Sorbet has special knowledge for `attr_reader`, `attr_writer`, and @@ -127,27 +154,55 @@ to call `.new`, which is defined on every Ruby class. Typing the result as ## How do I override `==`? What signature should I use? -Your method should accept `BasicObject` and return `T::Boolean`. +The same suggestions also apply for overriding `eql?`. `==` is typically used +for custom/structural equality operators. `eql?` is typically used for reference +equality. But from the perspective of the type system, they both have the same +signature. + +Your method should accept `T.anything` and return `T::Boolean`. For more +information, see the docs for [`T.anything`](anything.md) and the blog post +[Problems typing equality in Ruby]. + +[Problems typing equality in Ruby]: + https://blog.jez.io/problems-typing-ruby-equality/ -Unfortunately, not all `BasicObject`s have `is_a?`, so we have to do one extra -step in our `==` function: check whether `Object === other`. (In Ruby, `==` and -`===` are completely unrelated. The latter has to do with [case subsumption]). -The idiomatic way to write `Object === other` in Ruby is to use `case`: +If you want the implementation to branch on the type of the argument, you'll +want to use `case`/`when` (or `Module#===` directly) instead of `is_a?`. + +Example using `case`/`when`: ```ruby -case other -when Object +class A # ... + attr_reader :foo + + sig { params(other: T.anything).returns(T::Boolean) } + def ==(other) + case other + when A + self.foo == other.foo + else + false + end + end end ``` -[case subsumption]: https://stackoverflow.com/questions/3422223/vs-in-ruby +Example using [`===` directly][case subsumption]: -Here's a complete example that uses `case` to implement `==`: +```ruby +class A + # ... + attr_reader :foo + + sig { params(other: T.anything).returns(T::Boolean) } + def ==(other) + (A === other) && (self.foo == other.foo) + end +end +``` - - → View on sorbet.run - +[case subsumption]: https://stackoverflow.com/questions/3422223/vs-in-ruby ## I use `T.must` a lot with arrays and hashes. Is there a way around this? @@ -246,21 +301,148 @@ end ## Why is `super` untyped, even when the parent method has a `sig`? -Sorbet can't know what the "parent method" is 100% of the time. For example, -when calling `super` from a method defined in a module, the `super` method will -be determined only once we know which class or module this module has been mixed -into. That's a lot of words, so here's an example: +By default, Sorbet type checks all calls to `super` that it can, but there are +still cases where it can't statically check calls to `super`. -→ -View on sorbet.run +See below. + +Note that like with most parts of Sorbet, support for typing `super` is expected +to improve over time, which might introduce new type errors upon upgrading to a +newer Sorbet version. + +## When are calls to `super` typed, and when are they untyped? + +Over time, we have improved support for `super` in certain circumstances. In +particular, if all of these things are true, Sorbet will type check the call to +`super`: + +- The usage of `super` must be in a method defined in a `class`, not a module. + + (Why? → Inside a module, the method that `super` dispatches to [is different + each time][module_super] the module is mixed into a different class. This + means there's not necessarily a single way to analyze a call to `super` + statically.) + +- The usage of `super` must not be inside a Ruby block (like `do ... end`) + + (Why? → Sorbet doesn't always know whether the block is being passed to + `define_method`, or otherwise [executed in a context different from the + enclosing context][define_method_super].) -To typecheck this example, Sorbet would have to typecheck `MyModule#foo` -multiple times, once for each place that method might be used from, or place -restrictions on how and where this module can be included. +[module_super]: + https://sorbet.run/#%23%20typed%3A%20true%0Amodule%20MyModule%0A%20%20extend%20T%3A%3ASig%0A%0A%20%20sig%20%7Breturns%28Integer%29%7D%0A%20%20def%20foo%0A%20%20%20%20%23%20Can't%20know%20super%20until%20we%20know%20which%20module%20we're%20mixed%20into%0A%20%20%20%20res%20%3D%20super%0A%20%20%20%20T.reveal_type%28res%29%0A%20%20%20%20res%0A%20%20end%0Aend%0A%0Amodule%20ParentModule1%0A%20%20extend%20T%3A%3ASig%0A%0A%20%20sig%20%7Breturns%28Integer%29%7D%0A%20%20def%20foo%0A%20%20%20%200%0A%20%20end%0Aend%0A%0Amodule%20ParentModule2%0A%20%20extend%20T%3A%3ASig%0A%20%20%0A%20%20sig%20%7Breturns%28String%29%7D%0A%20%20def%20foo%0A%20%20%20%20''%0A%20%20end%0Aend%0A%0Aclass%20MyClass1%0A%20%20include%20ParentModule1%0A%20%20include%20MyModule%0Aend%0A%0Aclass%20MyClass2%0A%20%20include%20ParentModule2%0A%20%20include%20MyModule%0Aend%0A%0AMyClass1.new.foo%0AMyClass2.new.foo%0A +[define_method_super]: + https://sorbet.run/#%23%20typed%3A%20strict%0A%0Aclass%20Parent%0A%20%20extend%20T%3A%3ASig%0A%20%20sig%20%7Breturns%28Integer%29%7D%0A%20%20def%20self.foo%0A%20%20%20%200%0A%20%20end%0A%0A%20%20sig%20%7Breturns%28String%29%7D%0A%20%20def%20self.bar%0A%20%20%20%20''%0A%20%20end%0Aend%0A%0Aclass%20Child%20%3C%20Parent%0A%20%20sig%20%7Breturns%28Integer%29%7D%0A%20%20def%20self.foo%0A%20%20%20%20define_method%28%3Abar%29%20do%0A%20%20%20%20%20%20x%20%3D%20super%0A%20%20%20%20%20%20T.reveal_type%28x%29%0A%20%20%20%20%20%20%23%20-%3E%20should%20reveal%20String%2C%20but%20Sorbet%20doesn't%20know%20that%0A%20%20%20%20end%0A%0A%20%20%20%200%0A%20%20end%0Aend -Sorbet might adopt a more sophisticated approach in the future, but for now it -falls back to treating `super` as a method call that accepts anything and -returns anything. +To opt out of type checking `super`, pass the `--typed-super=false` command line +flag to Sorbet. + +## How can I fix type errors that arise from `super`? + +1. "Method does not exist" type errors can happen when Sorbet does not see that + a method with the same name actually exists on an ancestor of the current + class. Double check whether such a method actually exist. If it does, + metaprogramming is likely hiding that definition from Sorbet. Use + [RBI files](rbi.md) to ensure that Sorbet sees the parent's definition. + +1. Usually type errors from `super` arise because of incompatible overrides, + [like this][super_incompatible]. Sorbet only does + [Override Checking](override-checking.md) when the parent method is declared + with `overridable` or `abstract`, which means that incompatible overrides + can creep into a codebase. + + To fix these errors, correct the signature so that the child signature is a + [valid override](override-checking.md#a-note-on-variance). + +1. Sometimes the type errors happen even when the child is a valid override of + the parent, because the child either promises to accept a wider input or + return a narrower output. In these cases, before and/or after calling + `super`, the child method will need to handle the cases that the parent + method's type is unable to handle. (Or, if possible: adjust the parent's + signature to match the child's signature.) + +1. All `initialize` calls in Sorbet are typed as returning `.void`, regardless + of what they actually return. Usually, calls to `initialize` simply return + `self`. So instead of doing this: + + ```ruby + def initialize + super.foo + end + ``` + + do this: + + ```ruby + def initialize + super + self.foo + end + ``` + +1. Unlike method calls, it's **not possible** to do something like + `T.unsafe(self).super` to silence errors from a single usage of `super` + (`super` is a keyword in Ruby, not a method call). + + This means that there is not a way to silence errors from a single usage of + `super`. The best you can do is silence all errors inside a method using + `T.bind`: + + ```ruby + def example + T.bind(self, T.untyped) # <------ + super + end + ``` + + See the docs for [T.bind](procs.md#casting-the-self-type-with-tbind). Note: + not only does this opt out of typechecking calls to `super`, but it also + opts out of type checking for all method calls on `self` in that method + body. + +1. To silence all type errors from super, use the `--typed-super=false` flag. + This opts out of all `super` type checking. + +[super_incompatible]: + https://sorbet.run/#%23%20typed%3A%20strict%0A%0Aclass%20Parent%0A%20%20extend%20T%3A%3ASig%0A%20%20sig%20%7Breturns%28Integer%29%7D%0A%20%20def%20self.foo%0A%20%20%20%200%0A%20%20end%0Aend%0A%0Aclass%20Child%20%3C%20Parent%0A%20%20sig%20%7Breturns%28String%29%7D%0A%20%20def%20self.foo%0A%20%20%20%20super%0A%20%20end%0Aend + +## Why does `T.untyped && T::Boolean` have type `T.nilable(T::Boolean)`? + +**The short answer**: because `T.untyped` includes `nil`, and `nil && false` is +`nil`. Therefore, the whole expression's type is possibly nilable. + +**The long answer**: given a snippet like this + +```ruby +sig { params(x: T.untyped, y: T::Boolean).void } +def example(x, y) + res = x && y + T.reveal_type(res) # => T.nilable(T::Boolean) +end +``` + +The revealed type is [`T.nilable`](nilable-types.md) despite neither `x` nor `y` +being `T.nilable`. Ruby's `&&` operator is short-circuiting, and `nil` is falsy: + +```ruby +x = nil && anything_else() +p(x) # => nil +``` + +This `&&` expression evaluates to `nil` without evaluating `anything_else()`. + +So thinking about our `res = x && y` expression: + +1. If `x` is `nil`, the whole expression is `nil` and the type must be at least + [`NilClass`](class-types.md#nil). +1. If `x` is `false`, the whole expression's type must be at least + [`FalseClass`](class-types.md#booleans). +1. Otherwise, `x` is truthy (only `nil` and `false` are falsy in Ruby), so the + expression evaluates to the value of `y`, assuming it's type. In this case, + `T::Boolean`. + +Therefore, the type is `T.any(NilClass, FalseClass, T::Boolean)` which +simplifies to `T.nilable(T::Boolean)`. ## Does Sorbet work with Rake and Rakefiles? @@ -322,10 +504,7 @@ To upgrade a patch level (e.g., from 0.4.4314 to 0.4.4358): ``` bundle update sorbet sorbet-runtime -# also update plugins like sorbet-rails, if any - -# For plugins like sorbet-rails, see their docs, eg. -https://github.com/chanzuckerberg/sorbet-rails#initial-setup +# also update plugins, if any # Optional: Suggest new, stronger sigils (per-file strictness # levels) when possible. Currently, the suggestion process is @@ -393,13 +572,8 @@ RUN wget -nv -O /etc/apk/keys/sgerrand.rsa.pub https://alpine-pkgs.sgerrand.com/ Sorbet doesn't support Rails, but [Tapioca can generate RBI files for it](https://github.com/Shopify/tapioca#generating-rbi-files-for-rails-and-other-dsls). -If you're not using Tapioca, you could use [sorbet-rails], a -community-maintained project which can help generate RBI files for certain Rails -constructs. - Also see the [Community] page for more community-maintained projects! -[sorbet-rails]: https://github.com/chanzuckerberg/sorbet-rails [community]: /en/community ## Can I convert from YARD docs to Sorbet signatures? diff --git a/website/docs/flow-sensitive.md b/website/docs/flow-sensitive.md index 7166b3c469..4fa0f35827 100644 --- a/website/docs/flow-sensitive.md +++ b/website/docs/flow-sensitive.md @@ -263,3 +263,100 @@ View full example on sorbet.run > **Note**: Many Ruby constructs that look like local variables are actually > method calls without parens! Specifically, watch out for `attr_reader` and > zero-argument method definitions. + +Note that given Sorbet's architecture, it's not possible to implement some sort +of annotation that would mark a method as "pure" or "constant," declaring that a +method always returns the same value across consecutive calls to the method +(this also precludes special support for methods defined via `const` or +`attr_reader`). For more information on why, see +[this blog post](https://blog.jez.io/syntactic-control-flow/) which explains +that control flow in Sorbet is a syntactic property, not a semantic property +(which could be influenced by semantic annotations like this). + +## Tips + +These are some helpful tips for helping Sorbet track the types of expressions +throughout a program. + +### Prefer `xs.compact` to `xs.reject(&:nil?)` + +Sorbet does not use the boolean predicate accepted by methods like +`Array#select`, `Array#filter`, or `Array#reject` to infer a more narrow type +about the element of the list. + +To remove the `nil` values from an Array and have Sorbet understand that they +are gone, use `Array#compact` instead: + +```ruby +sig { params(xs: T::Array[T.nilable(Integer)]).void } +def example(xs) + ys = xs.reject(&:nil?) + T.reveal_type(ys) # <- still nilable ❌ + + ys = xs.compact # <- non-nil ✅ +end +``` + +View +on sorbet.run + +### Prefer `xs.grep(A)` to `xs.filter { |x| x.is_a?(A) }` + +Sorbet does not use the boolean predicate accepted by methods like +`Array#select`, `Array#filter`, or `Array#reject` to infer a more narrow type +about the element of the list. + +For the specific case of filtering a list using `is_a?`, prefer using +`Enumerable#grep`: + +```ruby +sig { params(xs: T::Array[T.any(Integer, String)]).void } +def example(xs) + ys = xs.select { |x| x.is_a?(Integer) } + T.reveal_type(ys) # <- not just Integer ❌ + + ys = xs.grep(Integer) + T.reveal_type(ys) # <- only Integers ✅ +end +``` + +View +on sorbet.run + +Two gotchas: + +- This requires the RBI definitions inside Sorbet 0.5.11388 or higher. +- This only works for classes, not modules. + +If either of these gotchas apply, see the `filter_map` tip below. + +### Prefer `xs.filter_map { ... }` to `xs.filter { ... }` + +Sorbet does not use the boolean predicate accepted by methods like +`Array#select`, `Array#filter`, or `Array#reject` to infer a more narrow type +about the element of the list. + +For complicated flow-sensitive predicates (i.e., more than just checking for +`nil?` or `is_a?`), use `Array#filter_map`. For example: + +```ruby +sig { params(xs: T::Array[T.any(Integer, String)]).void } +def example(xs) + ys = xs.select { |x| x.is_a?(Integer) && x.even? } + T.reveal_type(ys) # <- not just Integer ❌ + + ys = xs.filter_map { |x| x if x.is_a?(Integer) && x.even? } + T.reveal_type(ys) # <- only Integers ✅ +end +``` + +View +on sorbet.run + +`Array#filter_map` removes any element for which the filter returns a falsy +value. If the value is truthy, the value at that index is mapped to the new, +truthy value. The `x if x.is_a?(Integer)` evaluates to `x` (an `Integer`, thus +always truthy) if it's an Integer, or implicitly returns `nil` (which is falsy) +if it's a String. diff --git a/website/docs/from-typescript.md b/website/docs/from-typescript.md index 3ab98d8c79..54a02463d5 100644 --- a/website/docs/from-typescript.md +++ b/website/docs/from-typescript.md @@ -103,7 +103,7 @@ the features Sorbet provides. T.untyped - See T.untyped and Gradual + See T.untyped and Gradual Type Checking for more information. @@ -336,11 +336,12 @@ end never - T.noreturn + T.noreturn (in type annotation), + T.absurd (as type assertion) See T.noreturn and Exhaustiveness Checking for more. + href="/docs/exhaustiveness">Exhaustiveness (T.absurd) for more. diff --git a/website/docs/generics.md b/website/docs/generics.md index 8c58b5d5d7..4f797742ae 100644 --- a/website/docs/generics.md +++ b/website/docs/generics.md @@ -208,6 +208,87 @@ if box.is_a?(Box) end ``` +### Reifying generics at runtime + +> This section discusses features like `fixed` and `type_template` which are +> introduced further below. + +Sorbet erases generic types at runtime, but with abstract methods and +[`T::Class`](class-of.md#tclass-vs-tclass_of) it's sometimes possible to reify +those types. For example, commonly we might want the generic type so we can use +it to instantiate a value of that type: + +```ruby +class Factory + extend T::Generic + + InstanceType = type_template + + sig {returns(InstanceType)} + def self.make_bad + InstanceType.new + # ^ this is not valid, because `InstanceType` + # is a generic type (erased at runtime) + end +end +``` + +The problem here is that `InstanceType` does not "store" the runtime type that +might be bound at runtime--it's only there for the purposes of static checking. + +To fix this, we can just add an abstract method to our interface which forces +subclasses to reify the generic: + +```ruby +class Factory + extend T::Generic + abstract! + + InstanceType = type_template + + # (1) Require the user to provide a type + sig { abstract.returns(T::Class[InstanceType]) } + def self.instance_type; end + + sig {returns(InstanceType)} + def self.make + # (2) Call `self.instance_type.new` instead of `InstanceType.new` + self.instance_type.new + end +end + +class A; end + +class AFactory < Factory + # (3) Fix the type in the child + InstanceType = type_template { {fixed: A} } + + # (4) Provide the type explicitly. Sorbet checks that `A` is a valid value of + # type T::Class[A] + sig { override.returns(T::Class[InstanceType]) } + def self.instance_type = A +end +``` + +Some notes: + +1. We declare an abstract method that uses `T::Class[InstanceType]` for its + return type. Subclasses are forced to fill this in (or remain abstract). +2. The parent `Factory` class can call that method in `make` to access the + class at runtime. Unlike `InstanceType.new`, this actually produces the + correct class at runtime. +3. In the subclass, we're forced to redeclare the type template. In this sense, + the type_template acts like a sort of "abstract type". This is done with the + `fixed` annotation. +4. The subclass implements `instance_type` with `A`. Sorbet checks that the + implementation `instance_type` and the value provided to `InstanceType` + remain in sync because of the `T::Class[InstanceType]` return annotation. + +This approach works as long as we only want to reify singleton class types. More +complex types, like `T.any` types or types representing instance classes (not +singleton class types) will either not work at all, or require a little more +ingenuity. + ## `type_member` & `type_template` The `type_member` and `type_template` annotations declare class-level generic diff --git a/website/docs/go-to-def.md b/website/docs/go-to-def.md new file mode 100644 index 0000000000..68ecc30721 --- /dev/null +++ b/website/docs/go-to-def.md @@ -0,0 +1,131 @@ +--- +id: go-to-def +title: Go to Definition, Type Definition, and Implementations +sidebar_label: Go to Definition +--- + +Sorbet supports various "Go to Definition" features via LSP. + + + +As with most LSP features, support for Go to Definition works best in +`# typed: true` files. For more, see +[Feature support by strictness level](lsp-typed-level.md). + +## Troubleshooting + +### No definition results + +Double check [Feature support by strictness level](lsp-typed-level.md). + +If the file is at least `# typed: true` and there are still no results, double +check whether the value is [`T.untyped`](untyped.md). Check this either by +hovering over the expression, or by wrapping the expression in +[`T.revel_type`](troubleshooting.md#treveal_type). + +### Wrong definition location + +Please try to reproduce the issue in the [Sorbet Playground](https://sorbet.run) +and report an issue (click the "Create issue with example" button from the +"Examples" dropdown menu). + +### Jumping to the definition took me to a webpage, instead of a Ruby file + +The [RBI files](rbi.md) that Sorbet uses to define everything from the Ruby +standard library only exist as binary data in Sorbet's executable, not as actual +files on disk. + +To support jumping into these files, Sorbet has support for +[Working with Synthetic or Missing Files](sorbet-uris.md). This support is built +into the [Sorbet VS Code extension](vscode.md), but may require extra +integration work in other language clients. + +When using this feature, Sorbet returns special `sorbet:` URIs when attempting +to jump into synthetic or missing files, which allows them to be opened as +read-only files, directly in the editor. + +When not using this feature, Sorbet responds to "Go to Definition" requests on +these definitions by returning an `https://` URI to the Sorbet repo on GitHub, +where those RBI files live. Most language clients will interpret this `https://` +URI by opening the default web browser. Some language clients may attempt to +fetch the raw HTML of the page, instead of opening the URI in a browser. + +See [Working with Synthetic or Missing Files](sorbet-uris.md) for more +information. + +## Definition vs Type Definition + +The "Go to Definition" feature goes to wherever the thing under the cursor was +defined. For example, if the thing under the cursor is a local variable, it goes +where that local variable was first assigned. If it's a method call, it goes to +the definition of that method. + +By contrast, the "Go to Type Definition" feature first determines the type of +the expression under the cursor. Regardless of whether that's a method call or a +local variable or something else, it determines the type of the expression. Then +Sorbet figures out where that _type_ was defined. For simple +[Class Types](class-types.md) like `MyClass` or `Integer`, Sorbet jumps to the +definition of the class. For complex types like [Union Types](union-types.md), +Sorbet returns the locations of every component in the union. The language +client usually presents these options and asks the user to pick which definition +to go to. + +## Go to Implementations / Find All Implementations + +With the cursor on an abstract method's name (either a call to the method, or +its definition), Sorbet can list all the implementations of that abstract +method. + +This feature also works to find subclasses of an abstract class. + + + +## Go to Overridden Method + +The `override` keyword in a method signature is a Go to Definition target: + +```ruby +class Parent + extend T::Sig, T::Helpers + abstract! + + sig { abstract.void } # ◀── jumps to here + def foo; end +end + +class Child < Parent + sig { override.void } + # ▲ + # └─── Go to Definition here + def foo; end +end +``` + +Using "Go to Definition" on the `override` modifier in a signature jumps to the +method that it overrides, usually a corresponding [`abstract`](abstract.md) or +[`overridable`](override-checking.md) method. + +## Go to Definition on `super` + +The `super` keyword is a Go to Definition target. Clicking it jumps to the +corresponding super method, i.e. the first method in the ancestor chain of the +current class with the same name as the current method. + +```ruby +class Parent + def initialize # ◀── jumps to here + end +end + +class Child < Parent + def initialize + super + # ▲ + # └─── Go to Definition here + end +end +``` diff --git a/website/docs/highlight-untyped.md b/website/docs/highlight-untyped.md index 551a62a22c..567bc6ae16 100644 --- a/website/docs/highlight-untyped.md +++ b/website/docs/highlight-untyped.md @@ -27,39 +27,95 @@ render diagnostics with an [Information] severity. [information]: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#diagnosticSeverity -## Toggling untyped highlights +## Version requirements -**Note**: at the moment, toggling untyped highlights incurs a full restart of -Sorbet. This limitation should be removed in the future. +- Sorbet + - The `highlightUntyped` feature first appeared in Sorbet 0.5.10754 + (2023-04-06), but was a simple `true`/`false` toggle. + - The `highlightUntyped` feature changed to an enum + (`"everywhere"`/`"nowhere"`/`"everywhere-but-tests"`) in Sorbet 0.5.11175 + (2024-01-03). +- VS Code extension + - The "Sorbet: Toggle highlighting untyped code" setting first appeared in + version 0.3.19 of the VS Code extension (2023-04-06). + - Support for `"everywhere-but-tests"` first appeared in 0.3.32 of the VS Code + extension (2024-01-11) + +Newer versions of the VS Code extension are backwards compatible with older +versions of Sorbet. + +## Enabling untyped code highlights ### In VS Code -1. Install version 0.3.19 (or later) of the - [Sorbet VS Code extension](vscode.md). +There are multiple ways to enable untyped code highlights. + +- Open a Ruby file, and run the `Sorbet: Configure highlighting untyped code` + command from the command palette (accessed via ⇧⌘P on macOS, or ⌃⇧P on Windows + and Linux). + + You will be able to choose where to highlight untyped code: + + - Nowhere (the default) + + - Everywhere + + - Everywhere but tests + + Sorbet treats any file in a folder named `test` or ending with the + `.test.rb` suffix as a test file. This is not yet configurable. + + The new setting will be logged in the "Sorbet" output window. -2. Open a Ruby file, and run the `Sorbet: Toggle highlighting untyped code` - command from the command pallet (accessed via ⇧⌘P on macOS, or ⌃⇧P on Windows - and Linux). +- Open a Ruby file, and run the `Sorbet: Toggle highlighting untyped code` + command from the command palette. -This setting should persist through restarts of VS Code. + The toggle command remembers the "previous" highlight untyped setting, and + will go back to it. If there was no "previous" setting chosen via the + Configure command, then `Nowhere` will toggle to `Everywhere` and + `Everywhere`/`Everywhere but tests` will toggle to `Nowhere`. + + After toggling, the new setting will be logged in the "Sorbet" output window. + +- Set the `"sorbet.highlightUntyped": ...` setting in the VS Code preferences. + This changes the _default_ setting--values chosen with the Configure and + Toggle commands above will not change this value. + + For example, to have a workspace default to highlighting untyped code + everywhere, add this to the project's `.vscode/settings.json` file: + + ```json + "sorbet.highlightUntyped": "everywhere" + ``` ### In other LSP clients -This feature relies on the `initializationOptions` parameter to the `initialize` -request that starts -[every language server protocol session](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#initialize). +This feature relies on two LSP features: + +- The `initializationOptions` parameter in the `initialize` request that starts + [every language server protocol session](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#initialize). -To enable this feature, ensure that the `initializationOptions` includes +- The `settings` parameter in the `workspace/didChangeConfiguration` + notification. + +For both the `initializationOptions` and `settings` parameters, Sorbet allows +passing a JSON object containing ```json -"highlightUntyped": true + "highlightUntyped": ..., ``` -as a key-value pair when launching the Sorbet language server in your preferred -client. +Where `...` is one of + +- `"nowhere"` +- `"everywhere"` +- `"everywhere-but-tests"` +- `true` / `false` (for backwards compatibility only; use `"everywhere"` / + `"nowhere"` when possible) -For example, in Neovim, this setting can be provided via the [`init_options` -argument] to the `vim.lsp.start_client()` function. +For example, in Neovim, this `highlightUntyped` setting can be provided via the +[`init_options` argument] to the `vim.lsp.start_client()` function, which then +passes it to the underlying `initialize` request. [`init_options` argument]: https://neovim.io/doc/user/lsp.html#:~:text=initializationOptions diff --git a/website/docs/lsp-typed-level.md b/website/docs/lsp-typed-level.md new file mode 100644 index 0000000000..6b706359a9 --- /dev/null +++ b/website/docs/lsp-typed-level.md @@ -0,0 +1,149 @@ +--- +id: lsp-typed-level +title: Feature support by strictness level +sidebar_label: LSP & Typed Level +--- + +Some of Sorbet's LSP features will not work in [`# typed: false` files] (also, +no LSP features will work in `# typed: ignore` files). + +[`# typed: false` files]: static.md + +## Type inference: `# typed: false` vs `# typed: true` + +All LSP features which rely on type inference will not work in `# typed: false` +files. + +Recall from [Enabling Static Checks](static.md) the primary function of a +`# typed:` sigil is to control which errors are reported and which are silenced. +Sorbet still processes `# typed: false` files, but it silences some (not all) +errors detected in those files. + +The biggest class of errors silenced at `# typed: false` are those relating to +**type inference**: the process of inferring types for local variables and +checking that arguments provided at a method call site match the method's +declared parameter types. + +Since all type inference-related errors are silenced at `# typed: false`, Sorbet +**does not run type inference** over `# typed: false` files at all. It's not +that Sorbet makes an attempt to infer types for local variables and then +refrains from reporting those errors: it does not run inference on these files +at all. + +## Support by LSP feature + +The following table summarizes what to expect to work by Ruby language feature. +The "Works in `# typed: false`" column describes whether to expect LSP features +like Hover to work. For example, the table makes it clear that hovering over a +constant literal in a `# typed: false` file will show information about that +constant, but that hovering over a local variable will not. + +| Ruby feature | Works in `# typed: false` | +| ----------------------------------------------- | ------------------------- | +| `class`/`module` definition | ✅ | +| left side of constant assignment | ✅ | +| constant reference | ✅ | +| method definition | ✅ | +| left side of instance/class variable definition | ✅ | +| instance/class variable usage | ✅**†** | +| method call | ❌ | +| local variable assignment | ❌ | +| local variable usage | ❌ | + +**†**: Only if the instance or class variable at the cursor is defined. Sorbet +only requires these variables to be defined in `# typed: strict` files or above, +so it's possible that there will be no results because Sorbet has silenced an +error complaining that it could not find the definition matching an instance or +class variable usage. + +The table above applies to the following LSP features, which operate on the +thing under the cursor: + + + +- Hover (`textDocument/hover`) +- [Go to Definition](go-to-def.md) (`textDocument/definition`) +- [Go to Type Definition](go-to-def.md#definition-vs-type-definition) + (`textDocument/typeDefinition`) +- [Go to Implementation](go-to-def.md#go-to-implementations--find-all-implementations) + (`textDocument/implementation`) +- [Autocompletion](autocompletion.md) (`textDocument/complete`) +- Signature Help (`textDocument/signatureHelp`) +- Document Highlight (`textDocument/documentHighlight`) + +It also applies to these features which operate on both the thing under the +cursor **and** references to things throughout the codebase: + +- Find All References (`textDocument/references`) +- Rename Symbol (`textDocument/rename`) + +For this second set of requests, the table indicates whether the results will +include usages in `# typed: false` files (in addition to whatever was under the +cursor to initiate the request). For example, Find All References on a constant +literal will find all usages of the constant, even references in +`# typed: false` files. But Find All References on a method definition will only +find calls to that method in `# typed: true` or higher files. + +## Why choose to disable certain editor features? + +An alternative to disabling things like Hover and Go to Definition in +`# typed: false` files would be to turn those features on, but make it clear +that the results are "best effort," because silenced type errors have gotten in +the way of accurately knowing what's under the cursor. + +Choosing to disable these LSP features is intentional, for two main reasons: + +- We do not want to train people to expect incorrect results from Sorbet. It can + be hard to know when to expect an accurate result vs when to expect a "best + effort" result. + + Consistently inaccurate results with Sorbet erodes trust in Sorbet, especially + for new Sorbet users where much of the codebase is not yet `# typed: true`. + +- We want to encourage people to upgrade their files from `# typed: false` to + `# typed: true`. Disabling LSP features in `# typed: false` files provides a + reminder and a nudge that upgrading from `# typed: false` to `# typed: true` + is a good idea. + + Upgrading the file to `# typed: true` allows both catching type errors + introduced by future edits and turning on editor features, which aligns short- + and long-term incentives. + +## `# typed: false` nudges + +To make it clear when LSP features have been disabled, Sorbet usually shows some +sort of nudge. + +![Screenshot of typed: false nudge for hover](/img/lsp/hover-typed-false.png) +_The message shown when hovering over a local variable in a `# typed: false` +file._ + +![Screenshot of typed: false nudge for autocompletion](/img/lsp/typed-false-nudge.png) +_An empty completion item when typing the name of a local variable in a +`# typed: false` file._ + +### Disable autocompletion nudges in `# typed: false` files + +These nudges (for autocompletion specifically) can be disabled. + +In VS Code, either: + +- Use the `` Sorbet: Toggle the auto-complete nudge in `typed: false` files `` + command from the command pallet. + +- Set the `sorbet.typedFalseCompletionNudges` setting to `false` in the VS Code + preferences. This changes the _default_ setting--changing the value with the + above "Toggle" command will override the value in the preferences. + +In other LSP clients, either: + +- Set the `enableTypedFalseCompletionNudges` property to `false` in the + `initialize` request. + + See [`initialize` request](lsp.md#initialize-request) + +- Send a `workspace/didChangeConfiguration` notification to the server which + includes the `enableTypedFalseCompletionNudges` property. + + See + [`workspace/didChangeConfiguration` request](lsp.md#workspacedidchangeconfiguration-notification) diff --git a/website/docs/lsp.md b/website/docs/lsp.md new file mode 100644 index 0000000000..e2eac84a80 --- /dev/null +++ b/website/docs/lsp.md @@ -0,0 +1,430 @@ +--- +id: lsp +title: Sorbet Language Server +sidebar_label: Language Server (LSP) +--- + +Sorbet implements the [Language Server Protocol], which allows using Sorbet +within virtually any text editor. + +[Language Server Protocol]: + https://microsoft.github.io/language-server-protocol/ + +The recommended way to interface with Sorbet's language server is via the +[Sorbet VS Code extension](vscode.md), but it's possible to use Sorbet with +other language clients. + +## Prerequisites + +### Watchman + +For the best experience, Sorbet requires +[Watchman](https://facebook.github.io/watchman/), which listens for changes to +the files on disk in addition to edits that happen to files open in the editor. +For example, without Watchman installed, Sorbet will not detect when files have +changed on disk due to things like changing the currently checked out branch. + +There are installation instructions for installing watchman on various platforms +in the [Watchman docs](https://facebook.github.io/watchman/docs/install.html). + +To get Sorbet working with watchman, there are three options: + +1. Simply install the `watchman` binary somewhere visible via the `PATH` + environment variable. Sorbet will automatically discover it. + +1. Install the `watchman` binary anywhere, and specify the path to it using the + `--watchman-path` command line flag to Sorbet (pass it whenever you pass the + `--lsp` flag according to the next section). + +1. Opt out of using watchman, with the `--disable-watchman` flag. Sorbet will + only read files from disk at startup, and afterwards will only see contents + of files that have been opened or changed in the editor. + +"Why watchman?" See [A note on watchman](#a-note-on-watchman) below. + +### A single input directory + +While the `srb tc` command accepts any number of files and folders (and will +type check all provided files), Sorbet in LSP mode requires a single input +directory. + +This is an artificial limitation that we hope to lift in the future. + +The best way to workaround this is to use one or more `--ignore` flags to ignore +folders that should be excluded. For example, given a project like this, where +you only want to typecheck `lib/` and `test/` but not `bin/`: + +``` +. +├── bin/ +├── lib/ +└── test/ +``` + +Use this in your `sorbet/config` file: + +``` +--dir=. +--ignore=/bin +``` + +instead of this, which will **not** work in LSP mode: + +``` +# ❌ will not work ❌ +--dir=lib +--dir=test +``` + +Using `--ignore` flags like this is imperfect and will not always work, but it's +the best current workaround. For more, see +[Including and excluding files](cli.md#including-and-excluding-files). + +## Using Sorbet as a language server + +With the [Prerequisites](#prerequisites) out of the way, simply add the `--lsp` +flag to however you invoke Sorbet right now. Some examples: + +```bash +srb tc --lsp + +# ... if you use Sorbet via bundler: +bundle exec srb tc --lsp + +# ... if you've built Sorbet from source: +bazel-bin/main/sorbet --lsp + +# ... if you need to pass other LSP-related flags: +srb tc --lsp --watchman-path=/path/to/watchman +``` + +The final step is to teach your language client to run this command whenever +inside a Sorbet project. Check to see if +[instructions for your language client](#instructions-for-specific-language-clients) +are below, or consider contributing! + +## Other useful LSP-related flags + +A short list of useful LSP-related command line flags: + +- `--dir=...` + + Sorbet in LSP mode requires a single input directory. The language server + protocol has support for multiple workspace root directories + (`workspaceFolders`) but Sorbet does not support this yet. + +- `--ignore=...` + + Ignores certain paths. The `...` is a file pattern in a Sorbet-specific + format. See + [Including and excluding files](cli.md#including-and-excluding-files) or + `srb tc --help` for more information. + +- `--disable-watchman` / `--watchman-path=...` + + Configure whether or how the `watchman` binary is invoked. + +- `--enable-all-beta-lsp-features` + + Enable all language server features the Sorbet team considers ready for open + beta. The set of features changes over time. Please give your feedback! + +- `--enable-all-experimental-lsp-features` + + Enable all language server features, even those that might be unreliable or + cause Sorbet to crash frequently. Please give us your feedback! + +- _assorted `--enable-experimental-lsp-*` flags_, (see `srb tc --help`) + + In addition to enabling all beta or all experimental LSP features, most + work-in-progress LSP features have their own command line flag to enable that + feature and no others. + + Note that these flags are removed when the feature is finalized, which can + cause Sorbet to fail to start (unrecognized command line flag). + +- `--lsp-error-cap` + + Certain language clients deal poorly with large quantities of diagnostics + (errors, warnings, information hints, etc.). Sorbet caps the number of + diagnostics it sends to clients at 1,000 diagnostics, but this can be changed + (set it to `0` to remove the cap). + +For all Sorbet flags, be sure to check `srb tc --help`. + +## Instructions for specific language clients + +Each language client will have its own mechanism for starting the language +server. + +### Neovim + +The [nvim-lspconfig] project includes support for Sorbet. Add this to your +`init.lua` file: + +[nvim-lspconfig]: https://github.com/neovim/nvim-lspconfig + +```lua +local lspconfig = require('lspconfig') +lspconfig.sorbet.setup {} +``` + +The default command is `srb tc --lsp`. To tweak the command used to start the +server, set the `cmd` property: + +```lua +local lspconfig = require('lspconfig') +lspconfig.sorbet.setup { + cmd = { "bundle", "exec", "srb", "tc", "--lsp" }, +} +``` + +To specify custom [`initializationOptions`](#initialize-request), set the +`init_options` property: + +```lua +local lspconfig = require('lspconfig') +lspconfig.sorbet.setup { + cmd = { "bundle", "exec", "srb", "tc", "--lsp" }, + init_options = { + highlightUntyped = true, + }, +} +``` + +### Other clients + +If you use Sorbet with a particular language client and want to help out, please +edit this page to add instructions! + +## Sorbet-specific LSP extensions + +Sorbet includes a handful of custom extensions to the language server protocol. + +The reason we recommend the [Sorbet VS Code extension](vscode.md) is that these +custom extensions are supported out-of-the-box when using that client. + +Taking advantage of Sorbet's custom extensions in other clients is possible, but +requires some manual configuration work. + +### `initialize` request + +Sorbet recognizes the following options in the `initializationOptions` of the +[`initialize`] LSP request: + +```typescript +export interface SorbetInitializationOptions { + highlightUntyped?: boolean | string; + enableTypedFalseCompletionNudges?: boolean; + supportsOperationNotifications?: boolean; + supportsSorbetURIs?: boolean; +} +``` + +- `highlightUntyped` + + Whether to [highlight usages of untyped](highlight-untyped.md) even outside of + `# typed: strong` files. Default: `"everywhere"` + +- `enableTypedFalseCompletionNudges` + + Whether to show a notice explaining when Sorbet refuses to provide completion + results because a file is `# typed: false`. Default: `true` + + See [`# typed: false` nudges](lsp-typed-level.md#-typed-false-nudges) + +- `supportsOperationNotifications` + + See [Showing the Language Server Status](server-status.md#api) + +- `supportsSorbetURIs` + + See [Working with Synthetic or Missing Files](sorbet-uris.md) + +### `workspace/didChangeConfiguration` notification + +Sorbet recognizes the same options in the `settings` field of the +[`DidChangeConfigurationParams`] as it recognizes in the `initializationOptions` +of the [`initialize` request](#initialize-request) above. + +See the definition of [`SorbetInitializationOptions`](#initialize-request) +above. + +[`DidChangeConfigurationParams`]: + https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#didChangeConfigurationParams + +### `sorbet/showOperation` notification + +The Sorbet VS Code extension gives an indication of whether Sorbet is currently +“Idle,” meaning it has finished all requested work and is waiting, or whether +there is a current long-running operation which may affect things like whether +all the errors have been reported and whether Sorbet is able to respond to +certain editor commands. + +This feature is powered by the custom `sorbet/showOperation` LSP +server-to-client notification: + +_Notification_: + +- method: `sorbet/showOperation` +- params: `SorbetShowOperationParams` defined as follows + +```typescript +interface SorbetShowOperationParams { + /** + * A stable identifier for this operation. At time of writing, this includes: + * + * - "Indexing" + * - "SlowPathBlocking" + * - "SlowPathNonBlocking" + * - "FastPath" + * - "References" + * - "SymbolSearch" + * - "Rename" + * - "MoveMethod" + * + * Use this field if it's important to programmatically detect a certain state + * in the language client, as these identifiers are stable and unlikely to change. + */ + operationName: string; + + /** + * An unstable, human-readable description of a given operation. We reserve + * the right to change these descriptions in future versions of Sorbet, so to + * programmatically detect them, use `operationName`. + * + * Use these descriptions to show the user what operation is currently ongoing. + * This field contains the operation descriptions documented above, like + * "Typechecking..." instead of the technical "SlowPathBlocking" identifier. + */ + description: string; + + /** + * Operations can overlap. For example, a "References" operation can overlap + * with a "SlowPathNonBlocking" operation. This field indicates whether an + * operation is starting or ending, so the language client can track + * overlapping operations. + * + * Typically language clients will display only the most recently received + * operation notification as the current active operation. For example, if a + * SlowPathNonBlocking operation is ongoing and a new References operation + * begins, the client will show "Finding all references..." until that + * operation ends, at which point the client will once again display + * "Typechecking in background..." (until the SlowPathNonBlocking operation + * also ends). + * + * When there is no active operation, language clients typically display + * something indicating that the process is idle, like "Idle" or ✅ or + * something else. + * + * There will only be one active operation for a given `operationName` at any + * given time. + */ + status: SorbetOperationStatus; +} + +export type SorbetOperationStatus = 'start' | 'end'; +``` + +### `sorbet/showSymbol` request + +The Sorbet VS Code extension has a context menu item called "Copy Symbol to +Clipboard," which copies the fully-qualified name of the thing currently under +the cursor. For example: + +```ruby +class A + class B + class C + # ^ Copy Symbol to Clipboard + end + end +end +``` + +Given this snippet, VS Code puts `A::B::C` on the system clipboard. + +This feature is powered by the custom `sorbet/showSymbol` LSP request: + +_Request_: + +- method: `sorbet/showSymbol` +- params: [`TextDocumentPositionParams`] + +_Response_: + +- result: [`SymbolInformation`] | `null` + +### `sorbet/readFile` request + +See [Working with Synthetic or Missing Files](sorbet-uris.md) for more +information. + +_Request_: + +- method: `sorbet/readFile` +- params: [`TextDocumentIdentifier`] + +_Response_: + +- result: [`TextDocumentItem`] + +## Appendix + +### A note on watchman + +Recall that file watching is important in the first place because certain file +edits may come from files on disk changing despite not being open in the client +itself (for example, doing a `git pull`, or editing files with some script). + +The language server protocol includes support for client-side file watching as +of [version 3.17]. Sorbet does not use this functionality, but might in the +future. Instead, Sorbet does file watching with watchman. + +Sorbet's language _server_ is designed so that it can be run on a separate host +from where the language _client_ runs. For example, the language client (VS +Code) can be running on a laptop, while the server process can be started via an +SSH connection to a remote host, communicating back and forth to the laptop via +the SSH connection's stdin/stdout pipe: + +```bash +ssh some.remote.host -- 'cd project; bundle exec srb tc --lsp' +``` + +In setups like these, certain files in the codebase may not actually exist +according to the client. For example, certain generated files may only be +generated on the remote host, not the laptop. The language client would not +notice changes to these generated files, but a watchman binary running on the +same remote host as the Sorbet language server would. + +Sorbet may add support for LSP-powered file watching in the future (to make +Sorbet easier to use when not using Sorbet over SSH like this). But as +specified, the current protocol is not powerful enough to completely replace +Sorbet's need for watchman. + +Also, watchman uses an algorithm to find the "project path" when setting up file +watches. This algorithm is +[described here](https://facebook.github.io/watchman/docs/cmd/watch-project#whats-a-project-path) +in detail, but basically reduces to "there must be either a `.git`, `.hg`, +`.svn`, or `.watchmanconfig` file in the watched folder or some parent folder." +This list of project indicators is not configurable by Sorbet--it can only be +configured globally for an entire machine. See +[`root_files`](https://facebook.github.io/watchman/docs/config#root_files) and +[Resolution / Scoping](https://facebook.github.io/watchman/docs/config#resolution--scoping) +for more. + +Thus, to use Sorbet with watchman in a project that does not use Git, create an +empty `.watchmanconfig` file in the root of the project. + +[version 3.17]: + https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#version_3_17_0 +[`initialize`]: + https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#initialize +[`TextDocumentPositionParams`]: + https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocumentPositionParams +[`SymbolInformation`]: + https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#symbolInformation +[`TextDocumentIdentifier`]: + https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocumentIdentifier +[`TextDocumentItem`]: + https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocumentItem diff --git a/website/docs/metrics.md b/website/docs/metrics.md index f5434921f9..6df70f0dd3 100644 --- a/website/docs/metrics.md +++ b/website/docs/metrics.md @@ -10,9 +10,13 @@ codebase: 1. File-level typedness: the number of files at each [strictness level](static.md), like `# typed: false` or `# typed: true`. -2. Callsite-level typedness: the number of method call sites where Sorbet knew - whether the method existed or not. (Only tracked in `# typed: true` files or - higher.) +2. Usages of untyped: the number of times that something only typechecked + because a piece of code relied on `T.untyped`. (Only tracked in + `# typed: true` files or higher.) + +> **Note**: in the past we recommended a different metric: callsite-level +> typedness. Sorbet still computes this metric, but it's not as useful as the +> new "usages of untyped" metric. Together, these two metrics provide a counter-balancing force for meaningfully driving adoption of Sorbet in a codebase. In this doc, we'll cover: @@ -23,8 +27,8 @@ driving adoption of Sorbet in a codebase. In this doc, we'll cover: ## Collecting metrics from Sorbet -Sorbet can collect metrics about a project while it's typechecking, and output -them at the end of the run. For example: +Sorbet can collect metrics about a project while typechecking and output them at +the end of the run. For example: ```bash # The most basic invocation: @@ -97,7 +101,7 @@ metrics are reported: ## Reporting metrics directly to statsd -In addition to outputing metrics to a file, Sorbet can also report metrics by +In addition to outputting metrics to a file, Sorbet can also report metrics by connecting directly to a statsd service. This technique is more advanced, which means the instructions will heavily vary by organization and use case. At a high level: @@ -156,8 +160,29 @@ within a project, including RBIs. For example, if a project had three files at `# typed: true`, then sorbet would report `3` for `types.input.files.sigil.true`. +### Usages of untyped + +```text +types.input.untyped.usages +``` + +This metric counts the number of usages of untyped in the whole codebase. A +"usage of untyped" is basically anything where the code only typechecks because +a particular expression was `T.untyped`. To be more specific, "usage of untyped" +errors are the errors that Sorbet reports in files marked +[`# typed: strong`](strong.md). For more information, see +[What counts as a usage of untyped?](strong.md#what-counts-as-a-usage-of-untyped). + +Note that this metric counts a single number: the total usages of untyped in the +entire codebase. For more granular untyped usage data, see the sections below. + ### Call-site-level typedness +> **Note**: This metric is not as good as the usages of untyped metric. +> +> Sorbet still computes and reports it, but it won't track untyped as precisely +> as the `types.input.untyped.usages` metric above. + ```text types.input.sends.total types.input.sends.typed @@ -168,8 +193,8 @@ codebase. There are two metrics here: - `types.input.sends.total` is the total number of sends that are in `# typed: true` files or higher. Sorbet doesn't even attempt to type check - method calls in `# typed: false` files, so this metric represents all the - method call sites Sorbet looked at. + individual method calls in `# typed: false` files, so this metric represents + all the method call sites Sorbet looked at. - `types.input.sends.typed` is the number of sends which Sorbet looked at **and** was sure that method existed. For example, in `x.foo`, if `x` is @@ -181,6 +206,101 @@ codebase. There are two metrics here: `sig`. It's just the number of call sites where the receiver (`x` in `x.foo`) is not [untyped](untyped.md). +## Fine-grained untyped tracking + +All the metrics reported with the `--metrics-file` or `--statsd` command are +coarse grained--they aggregate information across the entire codebase into a +single number. + +Sorbet can compute more granular measurements about typing adoption in a +codebase, but it does not report these granular measurements via the +`--metrics-file`/`--statsd` mechanism. + +Instead, Sorbet uses its `--print` option for more granular stats about typing +adoption, which instructs Sorbet to dump various internal data structures. + +### Per-file usages of untyped + +To report usages of untyped at a per-file level, pass these two flags to the +`srb tc` command: + +```bash +srb tc --track-untyped --print=file-table-json:/tmp/file-table.json + +# or, to output to stdout instead of a file: +srb tc --track-untyped --print=file-table-json +``` + +This instructs Sorbet to dump information about the number of untyped usages in +each file. The format looks something like this: + +```javascript +{ + "files": [ + { + "path": "foo/bar.rb", + // ... + "untyped_usages": 23 + }, + // ... + ] +} +``` + +The full format for this output can be found in +[proto/File.proto](https://github.com/sorbet/sorbet/blob/master/proto/File.proto). +Note that due to idiosyncrasies in Sorbet's choice of serialization format (JSON +via Protobuf), keys whose values equal their "zero" value are omitted. This +means that a file which has no usages of untyped will simply lack an +`"untyped_usages"` key entirely. + +Like with the `types.input.untyped.usages` metric, `# typed: false` and +`# typed: ignore` files are not considered for the sake of untyped usages. So +entries for these files will also lack an `"untyped_usages"` key. + +### Blaming usages of untyped back to methods + +Sorbet can attribute usages of untyped to the original source of untyped. The +docs for doing this live in the Sorbet source repo, because it involves a +slightly more involved process than other metrics: + +→ +[Blaming usages of untyped to definitions](https://github.com/sorbet/sorbet/blob/master/docs/untyped-blame.md) + +To show how it works, consider an example like this: + +```ruby +def method_without_a_sig_1 = 0 +def method_without_a_sig_2 = 0 + +x = method_without_a_sig_1 +x.even? + +y = method_without_a_sig_2 +y.even? +y.even? +``` + +In this snippet, there are three usages of untyped: one from a call to `.even?` +on `x` and two from a call to `.even?` on `y`. Sorbet can blame usages of +`T.untyped` back to its source, so that we could learn that this codebase has +one usage of untyped that blames to `method_without_a_sig_1`, and two that blame +to `method_without_a_sig_2`. + +This is useful, because it essentially creates a punchlist of the methods that +have the best return on investment for adding a signature. + +Unfortunately, tracking this information forces Sorbet change the memory layout +of certain internal data structures, which substantially increases the amount of +memory it uses when type checking. As such, blaming usages of untyped back to a +method requires building Sorbet from source. See +[Blaming usages of untyped to definitions](https://github.com/sorbet/sorbet/blob/master/docs/untyped-blame.md) +for more. + +
+ +
+ ## Metrics philosophy Why track these metrics? These metrics have been the primary metrics tracked by @@ -193,59 +313,57 @@ strictness levels report more errors, so having more files in higher strictness levels means that when Sorbet says "No errors! Great job." it carries more weight. -Tracking call-site-level typedness usually makes less sense to people. For most -people, their gut instinct is to track "how many method definitons have -[signatures](sigs.md)." (That is, instead of tracking at the call site, people -are tempted to track at the definition site.) +Tracking usages of untyped usually makes less sense to people. For most people, +their gut instinct is to track "how many method definitions have +[signatures](sigs.md)." (That is, instead of tracking at the usage site, track +at the definition site.) -Tracking call-site-level typedness is better for a number of reasons: +Tracking usages of untyped is better for a number of reasons: -- Code breaks when it runs, not when it's defined. Call sites represent the - individual places where code runs and thus could break. Call-site-level - typedness informs how saturated the types check code that actually runs. +- Code breaks when it runs, not when it's defined. Usages of untyped represent + the individual places where code runs and thus could break. -- [Untypedness is viral.](gradual.md) If one local variable is untyped, all - calls on that method will return `T.untyped`. That result might be stored in a - variable, and the process repeats. Having one source of untypedness in a - method body can easily erase type information throughout the rest of the - method body. +- [Untypedness is viral.](gradual.md) If one local variable is initialized by a + method that returns `T.untyped`, all calls on that method will return + `T.untyped`. That result might be stored in a variable, and the process + repeats. Having one source of untyped early in a method body can easily erase + type information throughout the entire the method body. -Thus, call-site-level typedness matches up more closely with people's intuitive +Tracking usages of untyped matches up more closely with people's intuitive notion of "type coverage." -Call-site-level typedness is also a useful **counter-balance** to file-level +Usages of untyped is also a useful **counter-balancing** metric to file-level typedness. It's "easy" to make a `# typed: false` into a `# typed: true` file: just use [`T.unsafe(...)`](troubleshooting.md#escape-hatches) to make individual parts of that file untyped—in effect, it's the same as silencing errors with `# typed: false`. -By tracking call-site-level typedness, using `T.unsafe` to work around problems -with `# typed: true` makes file-level typedness go up at the cost of -call-site-level typedness. This makes it harder to "game the system" by -increasing type coverage in a not-very-useful way. +Using `T.unsafe` to work around problems with `# typed: true` makes file-level +typedness go up at the cost of making more usages of untyped. This makes it +harder to "game the system" by increasing type coverage in a not-very-useful +way. ## Suggestions for driving adoption -These two metrics (file-level and call-site-level typedness) are important at +These two metrics (file-level typedness and usages of untyped) are important at different phases of adopting Sorbet in a codebase. 1. During the ramp-up, file-level typedness is more important. While ramping - up, it's important to enable the scope of Sorbet. For example, get as few - files to be `# typed: ignore` as possible, then start moving towards making - as many files `# typed: true` as possible. At this phase, **don't worry** - too much about adding method signatures. + up, it's important to widen the scope of Sorbet in a codebase. For example, + get as few files to be `# typed: ignore` as possible, then start moving + towards making as many files `# typed: true` as possible. At this phase, + **don't worry** too much about adding method signatures. This step usually involves resolving syntax errors or basic type errors in `ignored` or `false` files (like whether a constant is defined or not). This process helps to identify issues that make a codebase hard to type check. 2. Once the majority of files are typed `# typed: true` or higher, it becomes - important to drive up call-site-level typedness. This is a great time to - figure out which are the most impactful files and methods to add type - coverage to, and tackle those parts of a codebase first. These files might - be files that are edited frequently, or where correctness and reliability - are paramount. You can also [ask Sorbet what it thinks] are the most - impactful methods. + important to drive down usages of untyped. This is a great time to figure + out which are the most impactful files and methods to add type coverage to, + and tackle those parts of a codebase first. These files might be files that + are edited frequently, or where correctness and reliability are paramount. + You can [ask Sorbet what it thinks] are the most impactful methods. At this point, it's great to start adding signatures, as individual type annotations will help propagate type information further throughout a @@ -269,7 +387,7 @@ different phases of adopting Sorbet in a codebase. has only a couple dozen `# typed: strong` files. [ask sorbet what it thinks]: - https://github.com/sorbet/sorbet/blob/master/docs/suggest-sig.md + https://github.com/sorbet/sorbet/blob/master/docs/untyped-blame.md [`rubocop-sorbet`]: https://github.com/Shopify/rubocop-sorbet ## What next? diff --git a/website/docs/outline.md b/website/docs/outline.md new file mode 100644 index 0000000000..017dda5f45 --- /dev/null +++ b/website/docs/outline.md @@ -0,0 +1,49 @@ +--- +id: outline +title: Document Outline +sidebar_label: Outline & Document Symbols +--- + +Sorbet supports the `textDocument/documentSymbol` LSP feature, which powers many +editor features. In particular, this powers VS Code's [Outline], [Breadcrumbs], +and [Go to Symbol] features. + +For example, in this screenshot we see VS Code's Outline view in the left +column, and the Breadcrumbs view across the top of the editor: + +![](/img/lsp/outline-breadcrumbs.png) + +Another example: this is what we see after pressing ⇧⌘O to bring up the Go to +Symbol interface, indicated by the `@` as the start of the line: + +![](/img/lsp/go-to-symbol.png) + +## VS Code Symbol Icons + +The icons used to show document symbols are the same as the icons used in +completion results. Read [the VS Code docs][types of completions] to learn what +the icons mean, or see this chart: + +![](/img/lsp/vscode-completion-list.png) + +Other language clients may use different icons to represent the symbol kinds. + +## Outlines in Test Files + +As a side effect of how Sorbet models `describe`- and `it`-style tests, the +document outline can be used to navigate tests defined in a file: + +![](/img/lsp/outline-breadcrumbs.png) + +Notice how the `describe` blocks appear as if they were classes and the `it` +blocks appear as if they are methods. This can also be useful with Go to Symbol +to jump to a test defined in the current file. + +[Outline]: + https://code.visualstudio.com/docs/getstarted/userinterface#_outline-view +[Breadcrumbs]: + https://code.visualstudio.com/docs/editor/editingevolved#_breadcrumbs +[Go to Symbol]: + https://code.visualstudio.com/docs/editor/editingevolved#_go-to-symbol +[types of completions]: + https://code.visualstudio.com/docs/editor/intellisense#_types-of-completions diff --git a/website/docs/overloads.md b/website/docs/overloads.md new file mode 100644 index 0000000000..3ff770d547 --- /dev/null +++ b/website/docs/overloads.md @@ -0,0 +1,226 @@ +--- +id: overloads +title: Methods with Overloaded Signatures +sidebar_label: Overloads +--- + +Sorbet has minimal support for defining methods with overloaded signatures. + +```ruby +sig { params(x: Integer).returns(Integer) } +sig { params(x: String).returns(String) } +def example(x); end +``` + +## Consider not using overloads + +Overloads have multiple downsides: + +- They encourage unwieldy signatures, which are confusing to understand. +- They aren't as precise in [gradually typed](gradual.md) languages like Sorbet + as they are in typed languages which lack [`T.untyped`](untyped.md). +- Their implementation in Sorbet is somewhat second class (due in part to the + previous two points). + +Instead of using overloaded methods, consider simply defining multiple methods. + +For example, instead of defining a method like the following, which accepts +either a string or an array of strings and returns either a single `MyModel` or +an array of `MyModel`s: + +```ruby +sig { params(what: String).returns(MyModel) } +sig { params(what: T::Array[String]).returns(T::Array[MyModel]) } +def find(what); end +``` + +Consider instead simply defining two methods, each with a descriptive name: + +```ruby +sig { params(id: String).returns(MyModel) } +def find_one(id); end + +sig { params(ids: T::Array[String]).returns(T::Array[MyModel]) } +def find_many(ids); end +``` + +The benefits of this approach: + +- Each method has a descriptive name, making the meaning more well-understood at + the call site. +- The argument names can be unique in each definition as well. In this example: + `id` vs `ids`. +- Each method can be documented independently. Documentation above a method + definition is surfaced when hovering over a method call and when selecting + completion items. +- Since the method name controls which method is selected, untyped arguments do + not interfere with the inferred return type of a method call. + +### Multiple methods, but sharing a common implementation + +The biggest downside of this approach has to do with sharing code. + +In most cases, it's possible to implement one method in terms of the other. For +example, with our `find_one`/`find_many` example above: + +```ruby +sig { params(id: String).returns(MyModel) } +def find_one(id) + result = find_many([id]) + raise "find_many did not return a single result" unless result.size == 1 + result.fetch(0) +end +``` + +In cases when this is not possible, another option is to do something like this: + +```ruby +sig do + params(what: T.any(String, T::Array[String])) + .returns(T.any(MyModel, T::Array[MyModel])) +end +private def _find_impl + # ... +end + +sig { params(id: String).returns(MyModel) } +def find_one(id) + T.unsafe(_find_impl(id)) +end + +sig { params(ids: T::Array[String]).returns(T::Array[MyModel]) } +def find_many(ids) + T.unsafe(_find_impl(ids)) +end +``` + +In this example, we define the common logic in a private `_find_impl` method +with a signature that accepts the superset of all arguments, and returns a +superset of all return types. We then call that method inside public methods +with more specific types, using `T.unsafe`. This keeps usage of `T.untyped` +internal to the class's private implementation while exposing a typed public +API. + +There are a couple other ways to accomplish a similar effect: + +- Mark the return type of `_find_impl` as `T.untyped`, to avoid needing a + `T.unsafe` at each call site. Or omit the signature on `_find_impl` entirely. + +- Define the signatures for `find_one` and `find_many` in an [RBI file](rbi.md) + alongside the source file, and use something like + + ```ruby + T.unsafe(self).alias_method(:find_one, :_find_impl) + T.unsafe(self).alias_method(:find_many, :_find_impl) + ``` + + to create method aliases to the `_find_impl` method, but using `T.unsafe` to + hide those aliases from Sorbet, so that the RBI definitions are all Sorbet + sees. + +Any of the options presented here will offer a more first-class experience than +attempting to define a method with overloaded signatures. + +## Restrictions on overloaded methods + +Support for overloaded methods is minimal because there are restrictions on when +they are allowed to appear and how they are allowed to be used. + +Overloaded signatures: + +1. may only appear in RBI files, not in Ruby source files. + +1. prevent the implementation of that method from being type checked. + + If Sorbet sees the method in a source file (in addition to the overloaded + definition in an RBI file), it **will not type check** the method's body. In + fact, Sorbet will report an error in `# typed: true` files or higher when + this happens. + + It's expected that overloaded methods are only used to type external gems' + methods, which can't be rewritten to avoid overloads using the techniques + mentioned in the + [Consider not using overloads](#consider-not-using-overloads) section. + +1. are scanned top-to-bottom when attempting to select a suitable overload. + + If no suitable overload is found, the first overload is selected, which may + be wrong. If multiple suitable overloads are found, the first suitable + candidate is used. + +1. use a very simplistic, sometimes-wrong heuristic for selecting an overload. + + Some examples of the limitations of this heuristic: + + - Overloads which are [generic methods](generics.md), only get approximate + constraint resolution, which means that Sorbet may select an overload + optimistically that causes errors when it could have picked another + overload without errors. + + - Exactly one overload candidate is selected in the end. More specifically, + Sorbet will never attempt to select two overloads and merge their results + together. (For example, if there is an overload which accepts `String` + arguments, and another which accepts `Integer` arguments, when passed an + argument of type `T.any(Integer, String)` Sorbet will find no suitable + overload, and default to the first, which only accepts `Integer`.) + + The usual workaround for cases like these is to manually declare a final + overload which accepts a superset of all types that the method should be + able to accept. + +1. are thwarted by untyped arguments. + + In the presence of untyped arguments, chances are high that the first + overload is selected, which might not be desired. + +1. do not support using the presence, absence, or types of keyword parameters + for deciding whether a given overload is selected. + +### Why these restrictions? + +Consider how overloading works in typed, compiled languages like C++ or Java: +each overload is a separate method. They actually have separate implementations, +are type checked separately, compile (with link-time name mangling) to separate +symbols in the compiled object, and the compiler knows how to resolve each call +site to a specific overload ahead of time, either statically or dynamically via +virtual dispatch. + +Meanwhile, Ruby itself doesn't have overloading—there's only ever one method +registered with a given name in the VM, regardless of what parameters it +accepts. That complicates things. It becomes unclear how Sorbet should typecheck +the body of the method (against all sigs? against one sig? against the +component-wise union of their arguments?). There's no clear answer, and anything +we choose will be bound to confuse or surprise someone. + +Also because Sorbet doesn't control whether the method can be dispatched to, +even if it were going to make a static claim about whether the code type checks, +it doesn't get to control which (fake) overload will get dispatched to at the +call site (again: there's only one version of the method in the VM). + +Finally this choice is somewhat philosophical: codebases that make heavy use of +overloading (even in typed languages where overloading is supported) tend to be +harder for readers to understand at a glance. The above workaround of defining +multiple methods with unique names solves this readability problem, because now +each overload has a descriptive name. + +## Defining methods with overloaded signatures + +Unlike other methods, where every parameter in the method definition must also +have a type in the signature, overloaded signatures are allowed to omit +parameters. For example: + +```ruby +sig { returns(Enumerator[Integer]) } +sig { params(blk: T.proc.params(x: Integer).void).void } +def example(&blk); end + +x = example +T.reveal_type(x) # => Enumerator[Integer] + +y = example { |x| p(x) } +T.reveal_type(y) # => void +``` + +Notice how the first overloaded signature omits giving a type for the `blk` +parameter. Sorbet uses the presence or absence of an argument (including a block +argument) to select a suitable overload. diff --git a/website/docs/overview.md b/website/docs/overview.md index 044143d555..cff9e4a188 100644 --- a/website/docs/overview.md +++ b/website/docs/overview.md @@ -65,5 +65,4 @@ end - [Enabling Runtime Checks](runtime.md) - How to how to get **more confidence** out of Sorbet by enabling runtime - checks. + Learn how to get **more confidence** out of Sorbet by enabling runtime checks. diff --git a/website/docs/procs.md b/website/docs/procs.md index 31135bd513..2d08d9761b 100644 --- a/website/docs/procs.md +++ b/website/docs/procs.md @@ -12,9 +12,8 @@ This is the type of a `Proc` (such as a block passed to a method as a `&blk` parameter) that accepts arguments of types `Arg0Type`, `Arg1Type`, etc., and returns `ReturnType`. -At present, all parameters are assumed to be required positional parameters. We -may add support for optional or keyword parameters in the future, if there is -demand. +At present, all parameters are assumed to be required positional +parameters—`T.proc` types cannot declare optional nor keyword parameters. Types of procs are not checked at all at runtime (whereas methods are), and serve only as hints to `srb` statically (and for documentation). @@ -104,6 +103,142 @@ Note that the `yield` itself in the method body doesn't need to change at all. Since every Ruby method can only accept one block, both Ruby and Sorbet are able to connect the `yield` call to the `blk` parameter automatically. +## Methods that do not take blocks + +It's hard for Sorbet to know whether a method takes a block or not. Technically, +Ruby allows passing a block at runtime to **all methods**, regardless of whether +that method declares an explicit `&blk` parameter or uses the `yield` keyword. + +This means that Sorbet can only catch "Method does not take a block" errors when +the method definition: + +- has a signature +- does not mention a `&blk` parameter +- is defined in a `# typed: strict` file + +Absent all three conditions, Sorbet allows passing a block to a method that +might not actually accept a block. + +### Why `# typed: strict` files? + +To ease the adoption in pre-existing Ruby codebases, Sorbet allows methods to +use `yield` but not declare a `&blk` parameter in `# typed: true` files. We may +reconsider this decision in the future, but for now, Sorbet can only be sure +that a method does not take a block if the method is defined in a +`# typed: strict` file. + +Note that this applies to RBI files too: if a method is defined with a +signature, that does not mention a `&blk` parameter, in a `# typed: strict` RBI +file, Sorbet will error for attempts to pass a block to the method. + +For RBI authors, it's important to ensure that `# typed: strict` RBIs correctly +declare whether a method takes a block or not, or else use `# typed: true` for +that RBI file to opt the methods defined in that RBI out of "Method does not +take a block" errors. + +## Prefer blocks to procs or lambdas + +Sorbet's type inference gives substantial preference to Ruby blocks +(`f { ... }`) over procs and lambdas (`f(proc { ... })`/ `f(-> { ... })`). Code +making heavy use of procs and lambdas will make it hard to avoid depending on +`T.untyped` accidentally. + +Here are some examples: + +```ruby +sig { params(blk: T.proc.params(x: Integer).void).void } +def takes_block(&blk); end + +sig { params(f: T.proc.params(x: Integer).void).void } +def takes_lambda(f); end + +takes_block do |x| + T.reveal_type(x) # => Integer ✅ +end + +takes_lambda(-> (x) do + T.reveal_type(x) # => T.untyped ‼️ +end) + +f = -> (x) { x } +T.reveal_type(f) # => T.proc.params(arg0: T.untyped).returns(T.untyped) ‼️ +takes_lambda(f) +``` + +Sorbet does not do type inference for procs and lambdas. For blocks, it doesn't +have to do type inference: Sorbet simply reads the type of the block argument +from the associated method. + +By contrast, Sorbet computes a type for all non-block arguments (including procs +and lambdas) **before** type checking a call to a method. That means that even +when written like this: + +```ruby +takes_lambda(-> (x) { x }) +``` + +Sorbet computes a type for the first positional argument `f` as if the user had +written this: + +```ruby +f = -> (x) { x } +takes_lambda(f) +``` + +It attempts to compute a type for `f` without "looking ahead" at the type of +`takes_lambda`. There's more on this implementation decision in +[Why does Sorbet sometimes need type annotations for local variables?](https://sorbet.org/docs/why-type-annotations#-for-local-variables), +but it comes down to a combination of performance and simplicity. + +### What can I do for better proc and lambda types? + +- Use blocks instead, if possible. + +- Make a typed wrapper that takes a block and returns a proc or lambda: + + ```ruby + MyProcType = T.type_alias { T.proc.params(x: Integer).returns(Integer) } + + sig { params(blk: MyProcType).returns(MyProcType) } + def make_typed_proc(&blk) + blk + end + + f = make_typed_proc { |x| x } + ``` + + This is good because the body of the proc will benefit from well-typed + arguments, and the proc type only has to be written once—there is little + runtime overhead for this pattern. + +- Use a `T.let` annotation. + + ```ruby + f = T.let( + -> (x) { x }, + T.proc.params(x: Integer).returns(Integer) + ) + ``` + + Using `T.let` with `->` instructs Sorbet to assume that the `T.proc` + annotation holds when typechecking the body of the lambda. Specifically, + Sorbet assumes that the lambda arguments have the types specified in the + `T.let` annotation and checks the lambda body to ensure it returns the + specified return type. + + This only works with Ruby's `->` lambda syntax, with `Kernel.lambda`, and with + `Kernel.proc` (it doesn't work when calling the `lambda` and `proc` methods on + Kernel implicitly, without a receiver). + + ```ruby + f = T.let(-> () { 0 }, T.proc.returns(Integer)) ✅ + f = T.let(Kernel.lambda { 0 }, T.proc.returns(Integer)) ✅ + f = T.let(Kernel.proc { 0 }, T.proc.returns(Integer)) ✅ + + f = T.let(lambda { 0 }, T.proc.returns(Integer)) ❌ + f = T.let(proc { 0 }, T.proc.returns(Integer)) ❌ + ``` + ## Annotating the self type with `T.proc.bind` Many Ruby constructs accept a block argument in one context, but then execute it diff --git a/website/docs/rbi.md b/website/docs/rbi.md index 9e4879802d..4caf83cc4b 100644 --- a/website/docs/rbi.md +++ b/website/docs/rbi.md @@ -196,6 +196,45 @@ RBI files in an `rbi/` directory. When they do this, Tapioca will automatically merge those definitions in the autogenerated RBIs. In the future, we anticipate this to be the preferred way to include RBI files into a project. +## Versioning for standard library RBIs + +There is no versioning for gems whose RBIs live in Sorbet itself. Instead, +Sorbet tries to provide a best effort one-size-fits-all set of RBIs. + +Consider some examples: + +- A new version of Ruby **adds** a method to a class defined in the standard + library. + + We will accept PRs to add an RBI definition for the new method. This means + that Sorbet **will not** report "method does not exist errors" for codebases + that are still on the old version of Ruby. + +- A new version of Ruby **deletes** a method that was deprecated. + + We **will not** accept PRs to remove the corresponding method definition from + Sorbet, as that would prevent users on old Sorbet versions from calling it. + +While this is a general rule, we do make exceptions on a case-by-case basis. +Please open an issue if you're unsure. + +## Does the sigil matter in an RBI file? + +It does. The primary way it matters is that Sorbet only checks "Method does not +take a block" errors for methods defined in `# typed: strict` files. If an RBI +file is `# typed: true`, Sorbet will never report "Method does not take a block" +errors for methods defined inside it, even if a given method logically takes no +block. + +See [Methods that take no blocks](procs.md#methods-that-take-no-blocks) for +more. + +**Note**: At the moment, +[this issue](https://github.com/sorbet/sorbet/issues/7941) in Sorbet prevents +using `# typed: strict` to require that all definitions in an RBI file have +explicit type annotations. This is not intentional and will be fixed in a future +version of Sorbet. + ## DSL RBIs Sorbet by itself does not understand DSLs involving meta-programming, such as @@ -229,7 +268,7 @@ You can read about all the DSL RBI compilers supplied by Tapioca in the > Add Tapioca to your Gemfile then run `bundle install` to install it: > > ```ruby -> gem "tapioca", require: false, :group => :development +> gem "tapioca", require: false, :group => [:development, :test] > ``` > > Once Tapioca is installed, simply run `tapioca init` to initialize your diff --git a/website/docs/runtime.md b/website/docs/runtime.md index 3782ab2bcd..23bcc2f91c 100644 --- a/website/docs/runtime.md +++ b/website/docs/runtime.md @@ -258,10 +258,6 @@ sig {params(xs: T::Array[String]).void.checked(:tests)} # (3) Never runs the runtime checks. Careful! sig {params(xs: T::Array[String]).void.checked(:never)} - -# (4) Runtime checks only run when the file is using the Sorbet Compiler. -# In the interpreter, this behaves identically to checked(:never). -sig {params(xs: T::Array[String]).void.checked(:compiled)} ``` If `.checked(...)` is omitted on a sig, the default is `.checked(:always)`. The @@ -306,7 +302,7 @@ or not until the block is evaluated. So even if a sig is marked is called, Sorbet will discover the `.checked(:never)` and put back the original method. -Sometimes even this tiny amount of runtime metaprogramming is unnacceptable at +Sometimes even this tiny amount of runtime metaprogramming is unacceptable at runtime. To completely eliminate all runtime side effects when defining a signature, replace `sig` with `T::Sig::WithoutRuntime.sig` when annotating methods: @@ -329,6 +325,28 @@ class Foo end ``` +**Note**: For consistency in large codebases, it's best to avoid +`T::Sig::WithoutRuntime` unless it can't be avoided, because the performance win +versus simply using `.checked(:never)` is marginal. Some examples: + +- Circumstances make it impossible to put `extend T::Sig` in the class, but + `sorbet-runtime` is still loaded. +- The method is `method_missing` or `respond_to_missing?`, as `sorbet-runtime` + does not support wrapping these methods. +- Some code not under your control is trying to reflect on the result of + `.parameters` or `.arity` of the sig'ed method. (Note that this precludes code + under _your_ control: Sorbet provides [`T::Utils.signature_for_method`], which + exposes the arity of the underlying method.) +- After careful measurements, even the first call to a method is performance + sensitive. This is rare, but can happen when trying to optimize speed of code + loading specifically. + +If none of these reasons apply, it's best to avoid `T::Sig::WithoutRuntime`, +because it will stand out versus most other usages of `sig` in a codebase. + +[`T::Utils.signature_for_method`]: + https://github.com/sorbet/sorbet/blob/0dbaf2c465314556b5acecec6ec63937f2b9e836/gems/sorbet-runtime/lib/types/utils.rb#L63-L68 + ## T.let, T.cast, T.must, T.bind Type assertions like `T.let`, `T.cast`, `T.must`, and `T.bind` are normally diff --git a/website/docs/server-status.md b/website/docs/server-status.md new file mode 100644 index 0000000000..66d4542dae --- /dev/null +++ b/website/docs/server-status.md @@ -0,0 +1,326 @@ +--- +id: server-status +title: Showing the Language Server Status +sidebar_label: Server Status +--- + +Sorbet implements an extension to the [Language Server Protocol] which allows +showing the current server status. + +[Language Server Protocol]: + https://microsoft.GitHub.io/language-server-protocol/ + + + +
+ +Watch the status tray in the screencast above as various kinds of edits are +made. + +Sorbet gives an indication of whether Sorbet is currently "Idle," meaning it has +finished all requested work and is waiting, or whether there is a current +long-running operation which may affect things like whether all the errors have +been reported and whether Sorbet is able to respond to certain editor commands. + +In [Sorbet's VS Code extension](vscode.md), server statuses are shown +out-of-the-box. In other language clients, some work will need to be done to +show server status (see [API](#api) below). + +## Summary of server statuses + +These are the server statuses Sorbet will report, and what they mean. + +| Status | Sorbet is... | Responsive to IDE features? | Error list is complete? | +| ----------------------------- | ----------------------------------------------------------- | --------------------------- | ----------------------- | +| Idle | waiting for input | ✅ | ✅ | +|   |   |   |   | +| Indexing files... | producing ASTs for each file | ❌ | ❌ | +| Typechecking... | resolving project-wide information | ❌ | ❌ | +| Typechecking in background... | running inference on each file | ✅ | ❌ | +| Typechecking in foreground... | running inference on each file, in a blocking way | ❌ | ❌ | +|   |   |   |   | +| Finding all references... | working to respond to a "Find All References" request | ❌ | 🤔 maybe? | +| Workspace symbol search... | working to respond to a "Workspace Symbol Search" request | ❌ | 🤔 maybe? | +| Renaming... | working to respond to a request to rename a definition | ❌ | 🤔 maybe? | +| Moving... | working to respond to a request to move a method definition | ❌ | 🤔 maybe? | + +The "Responsive to IDE features?" column indicates whether to expect an instant +or delayed response to IDE features like hover, autocompletion, go to +definition, etc. Most phases of Sorbet cause requests for IDE features to queue +until the end of the current operation. The exceptions are the **Idle** and +**Typechecking in background...** phases, which don't cause requests to use IDE +features to queue. + +The "Error list is complete?" column indicates whether the list of errors is +up-to-date, or whether Sorbet is still working on producing all the errors in a +project. The **Idle** status is essentially the only phase where this is true. +The operations that indicate Sorbet is working to respond to a user request, +like finding all references, may or may not have the full set of errors, +depending on whether the request arrived when Sorbet was already in an **Idle** +state, because those operations pause any ongoing **Typechecking in +background...** operation in service of responding to the current request +faster. + +For more details, see the sections below. + +### Idle + +Sorbet has finished all requested work and is waiting either for changes to +files in the project or for the user to make an IDE request. + +All errors have been reported throughout the entire codebase. + +(Technically speaking, this status is reported by the Sorbet VS Code extension, +not the Sorbet language server process. The Sorbet language server simply +reports no active operation to represent when it is idle. See the [API](#api) +below.) + +### Indexing files... + +Sorbet is currently reading files from disk and parsing them into abstract +syntax trees. + +This operation only happens when Sorbet initially starts up in LSP mode for the +first time. If this operation is taking a long time, it can be sped up by +passing the [`--cache-dir`](cli.md#--cache-dir-caching-parse-results) flag. + +### Typechecking... + +Sorbet is currently analyzing the whole codebase to determine which methods are +defined where. This includes things like resolving constant references, +finalizing the inheritance hierarchy, and recording method signatures. It does +not include things like running type inference to report type mismatch errors. + +### Typechecking in background... + +Sorbet has finished resolving all global information (like method definitions +and the inheritance hierarchy) and is now running type inference on the methods +in each file, in parallel. + +This phase is special: only in this phase (and in the **Idle** phase) is Sorbet +able to respond to IDE requests concurrently with other work. Requests like +hover, autocompletion, and go to definition will not be blocked while this +operation is ongoing. + +In this phase Sorbet will still be working to compute the list of errors in this +phase, but it will prioritize reporting errors in recently-edited files over +files that have never been edited or were edited less recently. + +### Typechecking in foreground... + +This is a rare status which happens when Sorbet has finished the +**Typechecking...** phase (where it is resolving global information) and has +started running type inference (like in the **Typechecking in background...** +phase). But due to a rare sequence of events, Sorbet decided to typecheck a +large number of files (over 100) on the main thread, instead of in the +background on many threads. This causes IDE requests to queue in the same way as +they do in the **Typechecking...** phase. + +If this phase happens frequently, please open an issue report against Sorbet and +attempt to explain what sequence of edits causes this phase to happen. + +### Request-specific statuses + +This includes: + +- **Finding all references...** +- **Workspace symbol search...** +- **Renaming...** +- **Moving...** + +These statuses only happen in response to the corresponding user request. +Sorbet's architecture has tradeoffs, making certain operations fast and others +slow. Operations like hover and go to definition can be fast because the +information needed to answer the query is either precomputed or cheap to +recompute. Operations like finding all references are slow because Sorbet has to +compute information on the fly to answer the query, which might involve +essentially re-analyzing the codebase. + +When one of these slow operations is happening, Sorbet updates its status +appropriately to indicate that requests like hover or go to definition (which +would normally be fast) will be queued in the same way that they are in the +**Typechecking...** phase. + +## VS Code-specific statuses + +In the Sorbet VS Code extension, there are a handful more statuses which are +managed and reported by the language client itself, not the Sorbet language +server. When integrating against the [API](#api) for this feature in other +editors and language clients, these statuses will or may need to be provided by +the client itself. + +### Idle + +As mentioned above, the Idle status is technically managed by the VS Code +extension, not the server process. Sorbet is idle when there are no active +operations. + +### Disabled + +The VS Code extension is currently disabled. See +[Disabling the Sorbet extension](vscode.md#disabling-the-sorbet-extension) for +why this is, and how to enable it. + +### Initializing + +The VS Code extension has started the Sorbet LSP server process, but it has not +yet reported a status yet. After starting the Sorbet process for the first time, +the first status it reports is **Indexing...**. + +### Restarting + +The Sorbet process either crashed and needed to be restarted, or the user +requested that it be restarted directly. If Sorbet restarts and it wasn't +explicitly requested to, there is likely a bug in Sorbet which caused it to +crash. Please report a bug to the Sorbet developers. Ideally, please include the +output logged to the file mentioned by `--debug-log-file` if Sorbet was running +with this option. + +### Error + +There was a VS Code-specific error. This likely represents a bug in the Sorbet +VS Code extension (not the Sorbet language server). Please check the VS Code +Sorbet logs and report an issue to the Sorbet developers. + +## Why do some of my edits make Sorbet go back to "Typechecking..."? + +Sorbet ingests the vast majority of edits quickly, returning to its **Idle** +state either immediately or after a few hundred milliseconds. It's able to +handle these edits by _incrementally_ updating its knowledge of what's defined +in the codebase. + +Unfortunately, Sorbet's approach to incrementality is simplistic—in response to +certain edits, Sorbet is forced to give up and retypecheck the codebase from +scratch. This is when the **Typechecking...** status appears. + +Today, this happens for these kinds of edits: + +- Edits which change over 50 files at once. +- Edits which add a new file. +- Edits which make any change in a `__package.rb` file (only if using the + `--stripe-packages` flag). +- Edits which produce an **unrecoverable** syntax error in the file. (Sorbet is + able to recover from most but not all syntax errors.) +- Edits which change the class or inheritance hierarchy. +- Edits which change a definition used in over 50 files. + +Notably, this list does not include these common edits: + +- Edits which change code inside a method body. +- Edits which only add whitespace. +- Edits which rename non-class definitions, like methods, instance variables, + and constant assignments. +- Edits which change method signatures or constant type annotations. + +Sorbet also implements one last trick: if Sorbet is currently in the +**Typechecking...** phase in response to edit `A`, and a new edit `B` comes in +immediately after, and the combination edit `A+B` would have been able to skip +the **Typechecking...** phase, Sorbet will cancel the active **Typechecking...** +operation and reprocess the `A+B` edit as if it were a single edit. This handles +many common cases like making a change and then undoing it, or introducing an +unrecoverable syntax error and then fixing it. + +It is a long-term goal of the Sorbet team to eventually make every edit handled +entirely incrementally, and never have to retypecheck a codebase from scratch +after starting up. + +If you are curious to measure how often and for what reason Sorbet decided to +take the `Typechecking...` phase in a project, enable +[live metric reporting](metrics.md#reporting-metrics-directly-to-statsd) for the +project, and pay attention to these groups of metrics: + +- `.lsp.counters.lsp.updates.{fastpath,slowpath,slowpath_canceled}` +- `.lsp.counters.lsp.slow_path_reason.*` + +For a more in-depth introduction to Sorbet's approach to incrementality, see +this blog post: + +[Making Sorbet More Incremental →](https://blog.jez.io/making-sorbet-more-incremental/) + +(The blog post above includes screenshots of dashboards which monitor those +metrics, if you're curious.) + +## API + +In [Sorbet's VS Code extension](vscode.md), server statuses are shown +out-of-the-box. + +In other language clients, showing the server status requires some extra work. +To configure other language clients consume these statuses, follow these steps: + +### 1. Pass `supportsOperationNotifications` at initialization + +Sorbet only sends server statuses if the client declares it's able to understand +them. To opt into server statuses, pass the `supportsOperationNotifications` +option during the [`initialize` request]. + +[`initialize` request]: lsp.md#initialize-request + +```js +"initializationOptions": { + // ... + "supportsOperationNotifications": true, + // ... +} +``` + +Different language clients will have +[different ways to specify `initializationOptions`](lsp.md#instructions-for-specific-language-clients) +when starting a language server. Consult the documentation of your editor or +language client for how to pass this option on startup in the `initialize` +request. + +As a reference, it may be helpful to consult the implementation in the Sorbet VS +Code extension: + +- [Passing `initializationOptions`](https://github.com/sorbet/sorbet/blob/15df026a4ffe45871809885a26142bddac5cbdde/vscode_extension/src/languageClient.ts#L50-L52) + +### 2. Register a handler for `sorbet/showOperation` notifications that come from the server + +With the above option set, Sorbet will periodically send notifications from the +server to the client indicating the starts and ends of operations. Note that +these are [notifications] in the same sense as `textDocument/publishDiagnostics` +is a notification: they come from the server unprompted, and are not expected to +be given a response like a request would be. + +[notifications]: + https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#notificationMessage + +_Notification_: + +- method: `sorbet/showOperation` +- params: `SorbetShowOperationParams` as defined in the [Sorbet-specific LSP + extensions][sorbet/showOperation] + +[sorbet/showOperation]: lsp.md#sorbetshowoperation-request + +Different language clients will have different ways to register code to run when +a custom notification arrives. Consult the documentation of your editor or +language client for how to register some sort of callback or handler for custom +notifications. + +As a reference, it may be helpful to consult the implementation in the Sorbet VS +Code extension: + +- [Registering a handler](https://github.com/sorbet/sorbet/blob/15df026a4ffe45871809885a26142bddac5cbdde/vscode_extension/src/sorbetStatusProvider.ts#L222-L224) +- [Implementation of the handler](https://github.com/sorbet/sorbet/blob/15df026a4ffe45871809885a26142bddac5cbdde/vscode_extension/src/sorbetStatusProvider.ts#L86-L91) + +## Instructions for specific language clients + +Different language clients will have different ways to register code to run when +a custom notification arrives. Please feel free to contribute instructions for +how to configure your preferred language client, or instructions to use some +off-the-shelf plugin which includes support for these notifications in other +editors. + +### Neovim + +The LSP client built into Neovim has support for registering handlers on custom +notifications. See [`:help lsp-handler`] for documentation. At the time of +writing, the [nvim-lspconfig] project does not include support for +`sorbet/showOperation` notifications out of the box. + +[`:help lsp-handler`]: https://neovim.io/doc/user/lsp.html#lsp-handler +[nvim-lspconfig]: https://github.com/neovim/nvim-lspconfig diff --git a/website/docs/sigs.md b/website/docs/sigs.md index 1899545b8e..74e13a2618 100644 --- a/website/docs/sigs.md +++ b/website/docs/sigs.md @@ -223,7 +223,16 @@ Using `void` instead of `returns(...)` does a number of things: - In the runtime, `sorbet-runtime` will _throw away_ the result of our method, and return a dummy value instead. (All `void` methods return the same dummy - value.) This prevents untyped code from silently depending on what we return. + value.) + + If you do not want this behavior, either use `returns(T.anything)` instead + ([docs for `T.anything`](anything.md)), or + [disable runtime checking](runtime.md) for that method (or all methods). + +Replacing the return value with a meaningless value prevents untyped code from +silently depending on what a typed method returns, so that the implementation is +free to change without worry of breaking existing code which silently depended +on the result of the method being meaningful. Concretely, here's a full example of how to use `void` to type methods with useless returns: @@ -341,3 +350,45 @@ calls: `T.let` and `T.cast` to do type refinements and assertions are central to Sorbet being a gradual type system). Since types must already be valid Ruby, it makes sense to have `sig`s be valid Ruby too. + +## Can I skip writing `extend T::Sig` everywhere? + +To skip writing `extend T::Sig` inside every class that wants to use `sig`, use +this monkey patch: + +```ruby +class Module + include T::Sig +end +``` + +Since every singleton class descends from `Module`, this will make the `sig` +method available in every class body. + +This involves a monkey patch, and is **not** required to use Sorbet. But the +most earnest users of Sorbet all eventually add this monkey patch, because +`extend T::Sig` ends up getting written into almost every class. + +We recommend putting this monkeypatch in the same file that + +- requires `sorbet-runtime`, and +- sets up any [`T::Configuration`](tconfiguration.md) `sorbet-runtime` + configurations + +So that it's impossible to get one of these three things without the others. + +The upside: + +- Drops the "activation energy" required to add the first `sig` to a class. + Simply start writing `sig` above the current method. + +- Fewer lines dedicated to type annotations. + +The downside: + +- Adds a monkey patch to a Ruby standard library class, which might conflict + with other methods defined by the current project or its gems. + +- If users have _only_ required `sorbet-runtime` and forgotten to require the + file defining this monkey patch, the code can fail at runtime in unexpected + ways. diff --git a/website/docs/sorbet-uris.md b/website/docs/sorbet-uris.md new file mode 100644 index 0000000000..9d84facf61 --- /dev/null +++ b/website/docs/sorbet-uris.md @@ -0,0 +1,125 @@ +--- +id: sorbet-uris +title: Working with Synthetic or Missing Files +sidebar_label: sorbet: URIs +--- + +> **Note**: The [Sorbet VS Code extension](vscode.md) supports this +> out-of-the-box. This doc serves as a reference for users of +> [alternative LSP clients](lsp.md) to recreate the VS Code extension behavior +> in their preferred language client. + +When typechecking a project, certain files in the project are synthetic. For +example: every [RBI file](rbi.md) which defines the Ruby standard library is not +a file on disk, but actually a blob of memory baked into the Sorbet binary +executable. Also, some files may be visible to the language server, but +[missing from the language client](lsp.md#a-note-on-watchman). + +Sorbet supports a handful of extensions to the +[language server protocol](lsp.md) to enable Go to Definition to work with these +files: + + + +
+ +The video above shows that Go to Definition on the `File.read` method (defined +in an [RBI file](rbi.md) contained inside Sorbet itself) opens like a normal, +non-modifiable file inside VS Code. + +The extensions powering this are: + +- The `supportsSorbetURIs` property of the [`initialize` request]. +- A custom `sorbet/readFile` LSP request. +- In the [Sorbet VS Code extension](vscode.md): a + [`TextDocumentContentProvider`] to present a Virtual Document. +- The `--lsp-directories-missing-from-client` command line flag, specifying + which extra files the language server knows about, but are not known to the + client, which need to be served as virtual `sorbet:` URI files. (Uncommon, but + available) + +[`TextDocumentContentProvider`]: + https://code.visualstudio.com/api/extension-guides/virtual-documents + +As seen above, [Sorbet's VS Code extension](vscode.md) supports this out of the +box. + +To configure other language clients to support Go to Definition for these files, +follow these steps: + +## 1. Pass `supportsSorbetURIs` at initialization + +By default, Sorbet produces `https://` URIs for synthetic files in its payload, +and normal `file://` URIs for all other files (even those which are missing from +the client). + +The first step is to request that Sorbet send `sorbet:` URIs for these files +instead. + +In the [`initialize` request] that starts the Sorbet language server, be sure to +pass the `supportsSorbetURIs` property: + +```js +"initializationOptions": { + // ... + "supportsSorbetURIs": true, + // ... +} +``` + +Different language clients will have +[different ways to specify `initializationOptions`](lsp.md#instructions-for-specific-language-clients) +when starting a language server. Consult the documentation of your editor or +language client for how to pass this option on startup in the `initialize` +request. + +Setting `supportsSorbetURIs` to `true` informs Sorbet that it can use `sorbet:` +URIs. Whenever a Go to Definition request would attempt to jump into a synthetic +or missing file, instead of sending an `https://` URI or a `file://` URI, it +will send a `sorbet:` URI. + +[`initialize` request]: lsp.md#initialize-request + +## 2. Use the `sorbet/readFile` request to read the virtual file + +By default, the language client will not know how to open files with a `sorbet:` +URI, so the next step is to teach it. + +Sorbet implements a custom LSP request method called `sorbet/readFile` which +language clients can use to get the text content of a synthetic or missing file: + +_Request_: + +- method: `sorbet/readFile` +- params: [`TextDocumentIdentifier`] + +_Response_: + +- result: [`TextDocumentItem`] + +Different language clients have their own way to customize the way files with a +certain URI are opened. For example: + +- VS Code provides an API called [`TextDocumentContentProvider`]. +- Vim and Neovim provide [BufReadCmd] and [FileReadCmd] to customize how files + with certain protocols are read. For more information, see [`:help Cmd-event`] + +The best reference for how to use the `sorbet/readFile` command is the official +Sorbet VS Code extension's TextDocumentContentProvider implementation: + +→ +[vscode_extension/src/sorbetContentProvider.ts](https://github.com/sorbet/sorbet/blob/master/vscode_extension/src/sorbetContentProvider.ts) + +If you have implemented something which uses the `sorbet/readFile` request, +please contribute an edit to this doc which shares a link to your code, so +others can reference it! + +[BufReadCmd]: https://vimhelp.org/autocmd.txt.html#BufReadCmd +[FileReadCmd]: https://vimhelp.org/autocmd.txt.html#FileReadCmd +[`:help Cmd-event`]: https://vimhelp.org/autocmd.txt.html#Cmd-event +[`TextDocumentIdentifier`]: + https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocumentIdentifier +[`TextDocumentItem`]: + https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocumentItem diff --git a/website/docs/tenum.md b/website/docs/tenum.md index 255d25c844..251a978a98 100644 --- a/website/docs/tenum.md +++ b/website/docs/tenum.md @@ -68,46 +68,115 @@ missing. For more information on how exhaustiveness checking works, see ## Enum values in types -We might want a type for "only red suits" or "only black suits." `T::Enum` -allows using individual enum values like `Suit::Hearts` or `Suit::Diamonds` as -types (in addition to the name of the enum itself, like `Suit`): +Sorbet allows using individual enum values in types, especially in union types +and type aliases. For example: ```ruby -# (1) Hearts and Diamonds are enum values and also types -sig {params(red_suit: T.any(Suit::Hearts, Suit::Diamonds)).void} -def handle_red_suit(red_suit) - case red_suit - when Suit::Hearts then "..." - when Suit::Diamonds then "..." - # (2) exhaustive, even with only two cases handled - else T.absurd(red_suit) +RedSuit = T.type_alias { T.any(Suit::Hearts, Suit::Diamonds) } +``` + +This defines a [type alias](type-aliases.md) `RedSuit` composed of only hearts +and diamonds. (Contrast this with the type `Suit`, composed of all four enum +values.) + +## Defining a subset of an enum + +We can combine the techniques from the two previous sections to define subsets +of enum values within an enum, as well as checked cast functions that convert +into those subsets safely. + +For example: + +```ruby +class Suit < T::Enum + enums do + Spades = new + Hearts = new + Clubs = new + Diamonds = new + end + + sig { returns(T.nilable(RedSuit)) } + def to_red_suit + case self + when Spades, Clubs then nil + else + self + end end end -handle_red_suit(Suit::Hearts) # ok -handle_red_suit(Suit::Spades) # type error +RedSuit = T.type_alias { T.any(Suit::Hearts, Suit::Diamonds) } ``` -We can also define [type aliases](type-aliases.md) of various groups of enum -values: +There are two components to this example: -```ruby -# Two type aliases for two different enum subsets -RedSuit = T.type_alias {T.any(Suit::Hearts, Suit::Diamonds)} -BlackSuit = T.type_alias {T.any(Suit::Spades, Suit::Clubs)} +1. We've defined the `RedSuit` enum subset with a type alias. Due to + limitations in `T::Enum`, this type alias must be declared outside of the + enum itself (we expect to lift this restriction in the future). + +1. We've added a `to_red_suit` instance method on `Suit` which converts that + enum value to either `nil` if called on a black suit, or itself otherwise. -sig {params(black_suit: BlackSuit).void} -def handle_black_suit(black_suit) +This `to_red_suit` conversion function could be called like this: + +```ruby +sig { params(red_suit: Suit).void } +def takes_red_suit(red_suit) # ... end -``` -Using enum values in types is useful for modeling enums where there is a -frequently-used subset of enum values. +sig { params(suit: Suit).void } +def example(suit) + red_suit = suit.to_red_suit + if red_suit + takes_red_suit(red_suit) + else + puts("#{suit} was not a red suit") + end +end +``` -→ +→ View full example on sorbet.run +Sorbet knows that `to_red_suit` returns `nil` if the suit was not red, so by +assigning to a local variable and using `if`, Sorbet's +[flow-sensitive type checking](flow-sensitive.md) kicks in and allows using +`red_suit` with the non-nil type `RedSuit` inside the `if` condition. + +Note that the structure of the `to_red_suit` method implementation is +intentional: it specifies the examples of what is **not** a red suit, instead of +specifying all the cases of what is a red suit in order to be **safe in the +presence of refactors**. + +To outline the refactor-safety of this method, here's an extended explanation. +For our `Suit` example it gets a little contrived, because there are always only +four suits and that's very unlikely to change. But we can pretend anyways: + +- If a new enum value is added, maybe called `Stars`, Sorbet will catch that the + `else` branch has type `T.any(Hearts, Diamonds, Stars)`, which is not a + subtype of `RedSuit`. + + To treat `Stars` as a red suit, we'd want to update the definition of + `RedSuit` to mention it. To treat it as not a red suit, we'd want to add it to + the `when ... nil` statement. + +- If an enum value is removed from `RedSuit`, then Sorbet will report that as a + type error similar to the above. After removing a suit from the type alias, + the fix would be to explicitly list that removed suit in the `when ... nil` + statement. + +- If an enum value is removed from `Suit` entirely, Sorbet reports this as an + "Unable to resolve constant" error. The fix will be to either remove the + reference from the `RedSuit` type alias, or from the `when ... nil` statement + (depending on what was removed from `Suit`). + +(It would be possible to achieve the same effect with +[exhaustiveness on enums](#exhaustiveness), but in this particular case that +ends up being overkill—we can get the same safety guarantees with less +redundancy.) + ## Converting enums to other types Enumerations do not implicitly convert to any other type. Instead, all @@ -292,14 +361,14 @@ into `T::Enum`, and without mutating state. ## Defining one enum as a subset of another enum > This section has been superseded by the -> [Enum values in types](#enum-values-in-types) section above. This section is -> older, and describes workarounds relevant before that section above existed. -> We include this section here mostly for inspiration (the ideas in this section -> are not discouraged, just verbose). - -In addition to using [enum values in types](#enum-values-in-types) and type -aliases of enum values, there are two other ways to define one enum as a subset -of another: +> [Defining a subset of an enum](#defining-a-subset-of-an-enum) section above. +> This section is older, and describes workarounds relevant before that section +> above existed. We include this section here mostly for inspiration (the ideas +> in this section are not discouraged, just verbose). + +In addition to [defining a subset of an enum](#defining-a-subset-of-an-enum) +with type aliases and conversion methods, there are two other ways to define one +enum as a subset of another: 1. By using a [sealed module](sealed.md) 2. By explicitly converting between multiple enums diff --git a/website/docs/tstruct.md b/website/docs/tstruct.md index a765547e7b..aca076c501 100644 --- a/website/docs/tstruct.md +++ b/website/docs/tstruct.md @@ -377,6 +377,9 @@ aware of: p(foo_serialized) # => {"foo_or_bar"=>} ``` +- Same with [generic-typed](generics.md) properties containing `T::Struct` + instances: these are also not serialized. + ### `from_hash` gotchas As mentioned in the [previous section][legacy], the `deserialize` behavior was diff --git a/website/docs/type-annotations.md b/website/docs/type-annotations.md index a0bf92a692..8e0f23b881 100644 --- a/website/docs/type-annotations.md +++ b/website/docs/type-annotations.md @@ -51,13 +51,50 @@ end ## Annotating constants -Sorbet does not, by default, infer the types of constants, but they can be -specified using `T.let`: +Sorbet does _very_ minimal inference for types of constants. These are the cases +where Sorbet infers constant types: + +- Constants initialized with simple literals (like `"foo"` or `123`) will have + their types inferred. Importantly, this does not include `Array` or `Hash` + literals. + +- Constants initialized with a call to `SomeClass.new` will have their type + inferred to `SomeClass`. Importantly, this assumption happens **regardless** + of whether the `new` method actually returns an instance of `SomeClass`, which + might not be the case if the `new` method has been overridden. + + In these cases, Sorbet reports an error stating that it requires an explicit + type annotation to correct the faulty assumption. + +In all other cases, Sorbet does not infer the types of constants, and will +assume a type of `T.untyped`. In [`# typed: strict`](static.md) files, Sorbet +reports an error requiring that a type be specified, so that Sorbet does not +assume `T.untyped`. + +To specify the type of a constant, use `T.let`: ```ruby NAMES = T.let(["Nelson", "Dmitry", "Paul"], T::Array[String]) ``` +In codebases that require calling `.freeze` on constants, the call to `.freeze` +**must** go inside the `T.let`, or Sorbet will not see the call to `T.let`. + +```ruby +# ✅ Good +NAMES = T.let(["Nelson", "Dmitry", "Paul"].freeze, T::Array[String]) +# ^^^^^^^ ✅ + +# ❌ BAD +NAMES = T.let(["Nelson", "Dmitry", "Paul"], T::Array[String]).freeze +# ❌ ^^^^^^^ +``` + +We recommend using the [`rubocop-sorbet`] gem, which modifies the behavior of +RuboCop's `Style/MutableConstant` rule, making it aware of `T.let`. + +[`rubocop-sorbet`]: https://github.com/Shopify/rubocop-sorbet + ## Declaring class and instance variables To declare the static type of an instance variable, we can use `T.let` in a @@ -128,13 +165,30 @@ smart enough to understand that either: 2. `@user` has not yet been initialized, but the initial value, computed using `ENV.fetch('USER')`, has type `String` (and is thus non-nil). +Note that using `||=` like this only works when `nil` is the same as +"uninitialized." If it's possible for the instance variable to be initialized +and also possibly `nil` (meaning that there's no need to attempt to +re-initialize it on subsequent calls), use the `defined?` keyword built into +Ruby: + +```ruby +module B + sig {returns(T.nilable(String))} + def current_git_dir + return @git_dir if defined?(@git_dir) + @git_dir = T.let(ENV['GIT_DIR'], T.nilable(String)) + end +end +``` + ## Limitations on instance variable inference A current shortcoming of Sorbet is that in many cases it cannot reuse static type knowledge in order to automatically determine the type of an instance or class variable. In the following example, Sorbet will naturally understand that `@x` is of type `Integer`, but it cannot determine the static type of `@y` -without a `T.let` and therefore treats it as `T.untyped`: +without a `T.let` and therefore treats it as `T.untyped` when used in other +methods: ```ruby class Foo @@ -142,7 +196,10 @@ class Foo def initialize(x, y) @x = x @y = y + 0 + end + sig {void} + def example T.reveal_type(@x) # Integer T.reveal_type(@y) # T.untyped end diff --git a/website/docs/union-types.md b/website/docs/union-types.md index ff95caab70..e3205a5626 100644 --- a/website/docs/union-types.md +++ b/website/docs/union-types.md @@ -125,6 +125,7 @@ example, the following is _not_ valid: class A extend T::Sig + # ERROR, intentionally unsupported sig { params(input_param: T.any('foo', 'bar')).void } def a(input_param) puts input_param diff --git a/website/docs/unsupported.md b/website/docs/unsupported.md new file mode 100644 index 0000000000..cd6e3c5dc2 --- /dev/null +++ b/website/docs/unsupported.md @@ -0,0 +1,343 @@ +--- +id: unsupported +title: Unsupported Ruby Features +--- + +Some features of Ruby Sorbet intentionally does not support. This doc aims to +document the most commonly Ruby features which are not supported in Sorbet +**and** for which Sorbet will not produce an error. + +> **Note**: This page is not exhaustive. +> +> If something is missing from this page, it says nothing about whether Sorbet +> supports it, supports it but has a bug in the implementation, or does not ever +> intend to support it. +> +> When in doubt, please +> [open an issue](https://github.com/sorbet/sorbet/issues/new/choose). + +## Constant resolution through constant scopes via inheritance + +```ruby +class Parent + X = 1 +end + +class Child < Parent + p(X) # ✅ okay in both +end + +p(Child::X) # in Ruby => ✅ 1 + # in Sorbet => ❌ error +``` + +Sorbet does not support constant resolution through inheritance when given an +explicit scope. + +### Alternative + +Use `Parent::X` instead of `Child::X` + +### Why? + +- Performance + + Constant resolution is one of the most performance sensitive parts of Sorbet. + +- Understandability + + In this case, the code is easier to understand by simply replacing `Child::X` + with `Parent::X`. This can always be done because Sorbet does not support + dynamic constant references, so the scope is always known statically. + +## `prepend` + +```ruby +module WillBePrepended + def foo + puts 'WillBePrepended#foo' + end +end + +class Example + prepend WillBePrepended + def foo + puts 'Example#foo' + end +end + +Example.new.foo # => WillBePrepended#foo +``` + +Sorbet does not model inheritance relationships introduced by `prepend`. + +### Alternative + +- Usually, static support for `prepend` is not required, even in codebases that + make heavy use of `prepend`. +- In the rare cases where `prepend` is required, we recommend using + [escape hatches](troubleshooting.md#escape-hatches) to work around the + problems. + +### Why? + +- Support for `prepend` would force Sorbet to use more memory throughout an + entire codebase, even if the codebase makes no use of `prepend`. Usage of + `prepend` is far more rare than the cost it would inflict in terms of memory. + +- Supporting `prepend` would add implementation complexity to Sorbet's + internals. For example: consider how to do + [Override Checking](override-checking.md) and + [generic bounds checking](generics.md#bounds-on-type_members-and-type_templates-fixed-upper-lower) + in the presence of prepended modules. + +- Historically, Sorbet was developed at Stripe, which lints against usage of + `prepend`. + +- Historically and maybe still today: `sorbet-runtime` had (has?) poor support + for runtime-checked type annotations on methods defined with prepended + modules. + +## Refinements and `refine do` + +```ruby +class C + def foo + puts "C#foo" + end +end + +module M + refine C do + def foo + puts "C#foo in M" + end + end +end + +using M + +c = C.new + +c.foo # prints "C#foo in M" +``` + +Sorbet does not support refinements. + +### Alternatives + +- Use an RBI file to define the methods. Sorbet will assume that the methods are + defined everywhere, not just where the `using` directive lives. This means + that Sorbet will not reject code that would not have caused problems at + runtime, at the expense of not catching situations that might have. + +- Use an [Escape Hatch](troubleshooting.md#escape-hatches). + +- Use a monkey patch. + +### Why? + +- While refinements are better than monkey patching, they still amount to monkey + patching. Sorbet's role as a type checker is not only to catch errors, but to + steer people towards simpler designs. + +- Historically, Sorbet was developed at Stripe, which does not use refinements. + +- Support for refinements would add implementation complexity to Sorbet. + +- Supporting refinements would require doing program-wide work to discover and + use refinements even if a codebase does not use them at all, which comes with + a performance cost. + +## Creating method aliases to methods in parent classes + +```ruby +class Parent + def defined_in_parent; end +end + +class Child < Parent + alias_method :defined_in_child, :defined_in_parent + # Sorbet thinks this method doesn't exist ^ +end +``` + +Sorbet does not support aliasing to a method defined in a parent class from a +child class. + +### Alternative + +1. Override the parent method in the child, and have the implementation just + call `super`: + + ```ruby + class Parent + def defined_in_parent; end + end + + class Child < Parent + def defined_in_parent + super + end + + alias_method :defined_in_child, :defined_in_parent + end + ``` + +1. Use [RBI files](rbi.md) to define the methods that would be defined this + way: + + ```ruby + # -- foo.rb -- + class Parent + def defined_in_parent; end + end + + class Child < Parent + # Hide the alias_method call from Sorbet to silence the error + T.unsafe(self).alias_method :defined_in_child, :defined_in_parent + end + + # -- foo.rbi -- + class Child < Parent + # Define the method that will be defined with `alias_method` at runtime + def defined_in_child; end + end + ``` + +1. Use an [Escape Hatch](troubleshooting.md#escape-hatches) to silence errors + at call sites. + +### Why? + +Due to original design decisions made in Sorbet's architecture, all methods are +defined before inheritance information is resolved. + +There is nothing fundamental or performance sensitive which requires making this +choice (i.e., resolving ancestor information does not require knowing the set of +defined methods). But backing out this design decision at this point would +require more work than we currently believe the payoff is. + +Because this is not a fundamental nor ideological limitation, it's possible this +feature may gain support in the future. + +## Multi-line calls to to keyword-named methods with trailing `.` + +```ruby +# 1. single-line method call +x.end() # ok + +# 2. multi-line method call, leading `.` +x + .end() # ok + +# 3. multi-line method call, trailing `.` +x. + end() # not ok +``` + +Ruby allows methods to be defined with names that are nominally reserved for +keywords—like the method called `end()` above, even though there is a keyword +called `end`. + +For methods which share a name with a Ruby keyword, Sorbet does not allow a +newline to appear between the `.` token and the method name. + +### Alternative + +Use a leading `.` for chained multi-line method calls, instead of a trailing +`.`. + +Note: newer versions of `irb` and `pry` support the leading `.` syntax about as +well as the trailing `.` syntax. In old versions of `irb` and `pry`, the REPLs +did a poor job of detecting multi-line pastes, and would eagerly evaluate each +line instead of waiting for the full paste and evaluating the entire snippet. +Newer versions of `irb` and `pry` detect the terminal emulator's +[bracketed paste](https://github.com/ruby/irb/commit/45aeb52575) functionality +and pause evaluation until the paste finishes. + +### Why? + +One of the most common syntax errors in a Ruby program looks like this: + +```ruby +def example(x) + x. +end +``` + +At a glance, it looks like the syntax error is that the user has forgotten or is +in the process of typing the method name after the `x.`. But to the Ruby parser, +the method name **was** provided—it's a method named `end`. Instead, the syntax +error the Ruby parser sees is that the user forgot to terminate their method +definition with an `end` keyword after the last line. + +In order to make error messages and autocompletion suggestions better, Sorbet +reverts to treating the characters `end` as a keyword, not a method name, after +it sees a sequence of `.` followed by `\n`. This is a small change to the Ruby +grammar with a small cost to implement, for a large improvement in developer +ergonomics when working in an IDE, with a straightforward workaround. + +Note that this applies to **all** keywords, not just `end`. For a complete list +of Ruby keywords, see +[the Ruby docs](https://docs.ruby-lang.org/en/master/keywords_rdoc.html). + +For more, see [#1993](https://github.com/sorbet/sorbet/pull/1993). + +## Tracking code loading order + +```ruby +# -- a.rb -- +class A; end + +# -- b.rb -- +# ... does not require/autoload `a.rb` ... + +puts(A) # => in Ruby: ❌ NameError + # => in Sorbet: ✅ +``` + +Sorbet does not track `require`, `require_relative`, or `autoload` declarations. + +This means that Sorbet does not report errors for these errors or warnings from +the Ruby VM: + +- Accessing constants that are defined somewhere in the project but haven't been + loaded yet when the code runs. +- Reassigning a constant (`X = 1; X = 2`), which are warnings in the Ruby VM, so + long as the declared type of the constant is the same in both definitions. +- Redefining a method (`def f; end; def f; end`), so long as the arity of the + method is the same in both definitions +- Accessing an instance variable before it's been initialized. +- _etc._ + +### Alternative + +Use runtime code loading mechanisms (e.g. tests or other runtime checks) to make +sure that code can be loaded, possibly opting into more verbose warning checking +in the Ruby VM. + +### Why? + +- Different projects use different code loading paths for gems. Rather than have + Sorbet reimplement the algorithm Ruby/rbenv/rvm/etc. use to load gems out of + system directories, Sorbet requests that all gems are declared with RBI files + included in the args specified at the command line. + +- Certain require statements will be computed dynamically, either behind + `if`/`else` expressions, inside method calls, or even with non-static string + arguments. Sorbet cannot analyze these—the problem would reduce to having + Sorbet statically evaluate Ruby code. + +- Many projects, especially Rails projects, use a path-based autoloader, like + [zeitwerk](https://github.com/fxn/zeitwerk). Projects using code loaders like + this typically do not make their `autoload` statements visible to Sorbet at + all: the `autoload` statements are dynamically generated by the project at + runtime. + +- Sorbet does not track whether or in what order code loads. For example, a + project might define two versions of a file: one which is loaded on old + versions of Ruby, one which is loaded on newer versions of Ruby. The files + might define the same classes and methods, but with different implementations. + The project itself knows that at runtime only one of these will be loaded, but + there would be no way to indicate that to Sorbet. diff --git a/website/docs/vscode.md b/website/docs/vscode.md index dd6fe87d61..52f63ae23c 100644 --- a/website/docs/vscode.md +++ b/website/docs/vscode.md @@ -74,6 +74,12 @@ in the editor. You can use the `sorbet.lspConfigs` setting described above to have the VS Code extension always pass these command line flags when starting Sorbet. +Also, watchman requires that watched projects either be version controlled (e.g. +have a `.git` folder in the project root) or have a `.watchmanconfig` file at +the root of the project. Without one of these, watchman will not be able to +start successfully. See [LSP: A note on watchman](lsp.md#a-note-on-watchman) for +more. + ## Features Live error squiggles for Sorbet typechecking errors @@ -142,34 +148,6 @@ your preferred LSP client using the [`sorbet/showSymbol` LSP request].) [`sorbet/showsymbol` lsp request]: https://github.com/sorbet/sorbet/blob/ec02be89e3d1895ea51bc72464538073d27b812c/vscode_extension/src/LanguageClient.ts#L154-L179 -Highlight `T.untyped` code. This feature is in beta. - -This feature reports diagnostics to the editor for occurrences of `T.untyped` -code. Note that it is not yet perfect and may miss occurrences of such values. - -It can be enabled by adding the following to your VS Code `settings.json` and -either reopening VS Code or restarting Sorbet. - -```json -"sorbet.highlightUntyped": true -``` - -or by using the `Sorbet: Toggle Highlight untyped values` command from the -command palette (note this causes a full restart of Sorbet). - -To enable this feature in other language clients, configure your language client -to send - -```json -"initializationOptions": { - "highlightUntyped": true -} -``` - -when sending the LSP initialize request to the Sorbet language server. - - - ## Switching between configurations The Sorbet extension supports switching between multiple configurations to make @@ -341,52 +319,17 @@ that variable as having ever been defined. Otherwise, try to reproduce the issue on https://sorbet.run/ and file a bug on the [issue tracker](https://github.com/sorbet/sorbet/issues). -### Go to Definition/Go to Type Definition/Find all References - -#### Go to Definition/Go to Type Definition/Find all References is not working / Find all References is missing some expected results. - -First, make sure that Sorbet is running. You should see "Sorbet: Idle" in VS -Code's status bar. - -It's possible that the feature is working as intended. Go to Definition and Find -all References are not available in all circumstances. A ‘Yes’ entry in the -following table indicates two distinct things: - -- A _language construct_ where these features work. For example, you can right - click a class reference and go to its definition or find its references in - typed and untyped files. -- An item that will be included in the results of these features. For example, - the result of Find all References on a method includes method calls in typed - files, but not method calls in untyped files. - -| | `# typed: true` or above | `# typed: false` | -| --------------------------------------------- | ------------------------ | ----------------- | -| Class/module/constant definition or reference | Yes | Yes | -| ivar (@foo) | Yes, if defined\* | Yes, if defined\* | -| cvar (@@foo) | Yes, if defined\* | Yes, if defined\* | -| Method definition | Yes | Results only\*\* | -| Method call | Yes | No | -| Local variable | Yes | No | - -\* Indicates that the variable -[_must_ be defined with `T.let`](/docs/type-annotations#declaring-class-and-instance-variables). -Otherwise, Sorbet doesn’t see that variable as having ever been defined. - -\*\* You cannot use either feature from this language construct, but this item -will be included in Go to Definition/Find all References results from typed -files. - -We have these restrictions in place to avoid weird/nonsensical behavior caused -by untyped files, which may have partial and potentially incorrect type -information. We heartily encourage you to type files to gain access to these -features. - -Note that some items marked "Yes" in the table may not work with these features -if Sorbet does not have the necessary type information. In particular, method -calls on an object `foo` where `foo` is untyped will not be included in Find all -References and will not work with Go to Definition because Sorbet is unable to -resolve which method is being called at that location. Using `# typed: strict` -should suss out most of these untyped locations on a per-file basis. +### Find all References + +#### Find all References is not working / Find all References is missing some expected results. + +Make sure that Sorbet is running. You should see "Sorbet: Idle" in VS Code's +status bar. Otherwise, see +[Feature support by strictness level](lsp-typed-level.md). + +If the information in that document doesn't apply (e.g., the current file is +`# typed: true` or higher), check whether the expression is `T.untyped`. See +[Troubleshooting](troubleshooting.md) for more information. #### Find all References is slow. @@ -400,14 +343,7 @@ one file. Find all References also waits for "Typechecking in background..." to complete so that it does not contend with typechecking for CPU time. -#### Go to Definition/Go to Type Definition brought me to what I believe is the wrong location. - -Ensure that you see "Sorbet: Idle" and not "Sorbet: Disabled" at the bottom of -VS Code. If Sorbet is enabled and it is returning a weird/unexpected definition -site, please try to reproduce the issue on https://sorbet.run/ and file a bug on -the [issue tracker](https://github.com/sorbet/sorbet/issues). - -#### Go to Definition/Go to Type Definition/Find all References brought me to a file that I cannot edit. +#### Find all References brought me to a file that I cannot edit. These features may return results in type definitions for core Ruby libraries, which are baked directly into the Sorbet executable and are not present on the @@ -418,67 +354,6 @@ them, we've configured the Sorbet extension to display them in this read-only view. Note that certain extension features, like hover and Go to Definition, will not function in some of these special files. -### Completion - -#### I don't see any completion results. - -- Are you in a `typed: false` file? No completion results are expected. -- Is the place where you're trying to see results unreachable? For example, - after a return statement, or in an else condition that can't happen? Sorbet - can't provide completion results here. -- Can you see completion results for other things? Sorbet only supports - completing local variables, methods, keywords, suggested sigs, classes, - modules, and constants right now. Notably, it doesn't support completing the - names of instance variables. - -#### I don't see any completion results right after I type A:: or x. - -You'll have to type at least one character after the dot (like x.f) or after the -colon (like A::B) before completion results show up. - -We tried to get this working before the initial ship, but it ended up being a -more complicated change than we expected. We have a couple ideas how to support -this, so expect this to be supported in the future. - -#### The completion results look wrong. - -Completion results can come from many different extensions, not just Sorbet. You -can try to figure out what extension returned the results by looking at the icon -that VS Code shows in the completion list: - -![](/img/lsp/vscode-completion-list.png) - -Results from Sorbet will only ever have 1 of 6 icons (currently): `method`, -`variable`, `field`, `class`, `interface`, `module`, `enum`, `keyword`, and -`snippet`. - -**Notably**, the abc icon (`word`) means the results came either from VS Code’s -`editor.wordBasedSuggestions` setting or some other generic autocomplete -extension. - -Also, `snippet` results can come from other extensions. Snippet results that -come from Sorbet will always say `(sorbet)` somewhere in the snippet -description. Sorbet does not have control over any snippet results that don't -say `(sorbet)` in them; if they look wrong, the only suggestion is to turn them -off. - -#### Can I have Sorbet only suggest method names, not the entire snippet, with types? - -Sorbet inserts a suggested snippet into the document when accepting a completion -result. - -- Snippet results will have highlighted sections inside them. -- These represent "holes" (tabstops) that you'll need to fill in—the aim is that - every tabstop is for a required argument (i.e., optional / default arguments - won't be present). -- As the default text for each of these holes, Sorbet uses the type of the - corresponding argument. -- Press `TAB` to cycle through the holes (tabstops), or press `ESC` to deselect - all the tabstops. - -It is not possible to opt-out of these completion snippets. If you find that -this is annoying, please let us know. - ## Reporting metrics > Sorbet does not require metrics gathering for full functionality. If you are diff --git a/website/pages/en/community.js b/website/pages/en/community.js index 6f179bcb48..87e68d3364 100644 --- a/website/pages/en/community.js +++ b/website/pages/en/community.js @@ -112,6 +112,11 @@ class Index extends React.Component { link: '/docs/talks/ruby-conf-2019', venue: 'RubyConf 2019', }, + { + title: 'Compiling Ruby to Native Code with Sorbet and LLVM', + link: '/docs/talks/ruby-conf-2021', + venue: 'RubyConf 2021', + }, ]} /> @@ -129,12 +134,6 @@ class Index extends React.Component { description: 'A central repository for sharing type definitions for Ruby gems', }, - { - title: 'sorbet-rails', - link: 'https://github.com/chanzuckerberg/sorbet-rails', - description: - 'A set of tools to make Sorbet work with Rails seamlessly', - }, { title: 'Sord', link: 'https://github.com/AaronC81/sord', @@ -191,6 +190,12 @@ class Index extends React.Component { link: 'https://github.com/Shopify/spoom', description: 'Useful tools for Sorbet enthusiasts', }, + { + title: 'rubymine-sorbet-lsp', + link: 'https://github.com/simoleone/rubymine-sorbet-lsp', + description: + 'RubyMine plugin for error highlights and fixes powered by Sorbet language server', + }, ]} />

diff --git a/website/sidebars.json b/website/sidebars.json index d07fbc2091..083fd6c96b 100644 --- a/website/sidebars.json +++ b/website/sidebars.json @@ -20,7 +20,8 @@ "troubleshooting", "why-type-annotations", "faq", - "error-reference" + "error-reference", + "unsupported" ], "Type System": [ "sigs", @@ -52,12 +53,21 @@ "strong" ], "Editor Features": [ + "lsp", + "server-status", + "lsp-typed-level", + "go-to-def", + "autocompletion", + "code-actions", + "outline", "sig-suggestion", - "highlight-untyped" + "highlight-untyped", + "sorbet-uris" ], "Experimental Features": [ "tuples", "shapes", + "overloads", "requires-ancestor" ] }, diff --git a/website/siteConfig.js b/website/siteConfig.js index 36e70337ff..bfcd7d84bb 100644 --- a/website/siteConfig.js +++ b/website/siteConfig.js @@ -36,6 +36,11 @@ const users = [ image: 'img/flexport-logo.jpg', infoLink: 'https://flexport.com/', }, + { + caption: 'GitHub', + image: 'img/github-logo.svg', + infoLink: 'https://github.com/', + }, { caption: 'Grailed', image: 'img/grailed-logo.png', @@ -71,11 +76,6 @@ const users = [ image: 'img/marketplacer-logo.png', infoLink: 'https://marketplacer.com/', }, - { - caption: 'PhishSafety', - image: 'img/phishsafety-logo.png', - infoLink: 'https://phishsafety.com', - }, { caption: 'TaskRabbit', image: 'img/taskrabbit-logo.svg', diff --git a/website/static/css/overrides.css b/website/static/css/overrides.css index 72ed3b7147..d1acc16179 100644 --- a/website/static/css/overrides.css +++ b/website/static/css/overrides.css @@ -108,7 +108,7 @@ sup { /* * The default showcase section heading uses our one of the colors from our * siteConfig.js. This color only looks good in the headers for us, not in text - * colors, so we're reseting it to initial (black) here. + * colors, so we're resetting it to initial (black) here. */ .productShowcaseSection h2 { color: initial; diff --git a/website/static/docs/index.html b/website/static/docs/index.html index f92072a7d8..7fd1eb7131 100644 --- a/website/static/docs/index.html +++ b/website/static/docs/index.html @@ -4,5 +4,5 @@ Redirecting… -Click here if you are not redirected. +Click here if you are not redirected. diff --git a/website/static/docs/typed-super.html b/website/static/docs/typed-super.html new file mode 100644 index 0000000000..35c20ebe50 --- /dev/null +++ b/website/static/docs/typed-super.html @@ -0,0 +1,8 @@ + + + +Redirecting… + + +Click here if you are not redirected. + diff --git a/website/static/img/github-logo.svg b/website/static/img/github-logo.svg new file mode 100644 index 0000000000..37fa923df3 --- /dev/null +++ b/website/static/img/github-logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/static/img/lsp/autocompletion.mp4 b/website/static/img/lsp/autocompletion.mp4 new file mode 100644 index 0000000000..2e152ee1f4 Binary files /dev/null and b/website/static/img/lsp/autocompletion.mp4 differ diff --git a/website/static/img/lsp/convert-to-singleton-class-method.mp4 b/website/static/img/lsp/convert-to-singleton-class-method.mp4 new file mode 100644 index 0000000000..43d6cf7f9a Binary files /dev/null and b/website/static/img/lsp/convert-to-singleton-class-method.mp4 differ diff --git a/website/static/img/lsp/find-all-implementations.mp4 b/website/static/img/lsp/find-all-implementations.mp4 new file mode 100644 index 0000000000..bed8163e81 Binary files /dev/null and b/website/static/img/lsp/find-all-implementations.mp4 differ diff --git a/website/static/img/lsp/fix-available.png b/website/static/img/lsp/fix-available.png new file mode 100644 index 0000000000..0038fc52ff Binary files /dev/null and b/website/static/img/lsp/fix-available.png differ diff --git a/website/static/img/lsp/go-to-symbol.png b/website/static/img/lsp/go-to-symbol.png new file mode 100644 index 0000000000..e25cee0f9b Binary files /dev/null and b/website/static/img/lsp/go-to-symbol.png differ diff --git a/website/static/img/lsp/hover-typed-false.png b/website/static/img/lsp/hover-typed-false.png new file mode 100644 index 0000000000..90d9181019 Binary files /dev/null and b/website/static/img/lsp/hover-typed-false.png differ diff --git a/website/static/img/lsp/move-method-to-new-module.mp4 b/website/static/img/lsp/move-method-to-new-module.mp4 new file mode 100644 index 0000000000..eee6bec7f5 Binary files /dev/null and b/website/static/img/lsp/move-method-to-new-module.mp4 differ diff --git a/website/static/img/lsp/outline-breadcrumbs-test.png b/website/static/img/lsp/outline-breadcrumbs-test.png new file mode 100644 index 0000000000..0446e68f58 Binary files /dev/null and b/website/static/img/lsp/outline-breadcrumbs-test.png differ diff --git a/website/static/img/lsp/outline-breadcrumbs.png b/website/static/img/lsp/outline-breadcrumbs.png new file mode 100644 index 0000000000..33791fe24b Binary files /dev/null and b/website/static/img/lsp/outline-breadcrumbs.png differ diff --git a/website/static/img/lsp/struct-enum-snippet.mp4 b/website/static/img/lsp/struct-enum-snippet.mp4 new file mode 100644 index 0000000000..7630186b1a Binary files /dev/null and b/website/static/img/lsp/struct-enum-snippet.mp4 differ diff --git a/website/static/img/lsp/struct-snippet.png b/website/static/img/lsp/struct-snippet.png new file mode 100644 index 0000000000..79fffedd0f Binary files /dev/null and b/website/static/img/lsp/struct-snippet.png differ diff --git a/website/static/img/lsp/typed-false-nudge.png b/website/static/img/lsp/typed-false-nudge.png new file mode 100644 index 0000000000..1a3f8d5150 Binary files /dev/null and b/website/static/img/lsp/typed-false-nudge.png differ diff --git a/website/static/img/lsp/vscode-completion-list.png b/website/static/img/lsp/vscode-completion-list.png index 90de466ee4..e34d5e874e 100644 Binary files a/website/static/img/lsp/vscode-completion-list.png and b/website/static/img/lsp/vscode-completion-list.png differ diff --git a/website/static/img/lsp/vscode-server-status.mp4 b/website/static/img/lsp/vscode-server-status.mp4 new file mode 100644 index 0000000000..40aa56ad29 Binary files /dev/null and b/website/static/img/lsp/vscode-server-status.mp4 differ diff --git a/website/static/img/lsp/vscode-text-document-content-provider.mp4 b/website/static/img/lsp/vscode-text-document-content-provider.mp4 new file mode 100644 index 0000000000..5e9d1f9d2d Binary files /dev/null and b/website/static/img/lsp/vscode-text-document-content-provider.mp4 differ diff --git a/website/static/img/lsp/yard-snippet.mp4 b/website/static/img/lsp/yard-snippet.mp4 new file mode 100644 index 0000000000..36773ccc9f Binary files /dev/null and b/website/static/img/lsp/yard-snippet.mp4 differ diff --git a/website/static/img/phishsafety-logo.png b/website/static/img/phishsafety-logo.png deleted file mode 100644 index 6f13036df3..0000000000 Binary files a/website/static/img/phishsafety-logo.png and /dev/null differ diff --git a/website/yarn.lock b/website/yarn.lock index bca3d5edb4..5412edef0e 100644 --- a/website/yarn.lock +++ b/website/yarn.lock @@ -16,6 +16,14 @@ dependencies: "@babel/highlight" "^7.12.13" +"@babel/code-frame@^7.22.13": + version "7.22.13" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e" + integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w== + dependencies: + "@babel/highlight" "^7.22.13" + chalk "^2.4.2" + "@babel/compat-data@^7.13.11", "@babel/compat-data@^7.13.15", "@babel/compat-data@^7.13.8", "@babel/compat-data@^7.14.0": version "7.14.0" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.14.0.tgz#a901128bce2ad02565df95e6ecbf195cf9465919" @@ -51,6 +59,16 @@ jsesc "^2.5.1" source-map "^0.5.0" +"@babel/generator@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.0.tgz#df5c386e2218be505b34837acbcb874d7a983420" + integrity sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g== + dependencies: + "@babel/types" "^7.23.0" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + jsesc "^2.5.1" + "@babel/helper-annotate-as-pure@^7.10.4", "@babel/helper-annotate-as-pure@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.12.13.tgz#0f58e86dfc4bb3b1fcd7db806570e177d439b6ab" @@ -110,6 +128,11 @@ resolve "^1.14.2" semver "^6.1.2" +"@babel/helper-environment-visitor@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" + integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== + "@babel/helper-explode-assignable-expression@^7.12.13": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.13.0.tgz#17b5c59ff473d9f956f40ef570cf3a76ca12657f" @@ -126,6 +149,14 @@ "@babel/template" "^7.12.13" "@babel/types" "^7.12.13" +"@babel/helper-function-name@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" + integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== + dependencies: + "@babel/template" "^7.22.15" + "@babel/types" "^7.23.0" + "@babel/helper-get-function-arity@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz#bc63451d403a3b3082b97e1d8b3fe5bd4091e583" @@ -141,6 +172,13 @@ "@babel/traverse" "^7.13.15" "@babel/types" "^7.13.16" +"@babel/helper-hoist-variables@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" + integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== + dependencies: + "@babel/types" "^7.22.5" + "@babel/helper-member-expression-to-functions@^7.13.12": version "7.13.12" resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.12.tgz#dfe368f26d426a07299d8d6513821768216e6d72" @@ -221,11 +259,28 @@ dependencies: "@babel/types" "^7.12.13" +"@babel/helper-split-export-declaration@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" + integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-string-parser@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" + integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== + "@babel/helper-validator-identifier@^7.12.11", "@babel/helper-validator-identifier@^7.14.0": version "7.14.0" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz#d26cad8a47c65286b15df1547319a5d0bcf27288" integrity sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A== +"@babel/helper-validator-identifier@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" + integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== + "@babel/helper-validator-option@^7.12.17": version "7.12.17" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.12.17.tgz#d1fbf012e1a79b7eebbfdc6d270baaf8d9eb9831" @@ -259,11 +314,25 @@ chalk "^2.0.0" js-tokens "^4.0.0" +"@babel/highlight@^7.22.13": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.20.tgz#4ca92b71d80554b01427815e06f2df965b9c1f54" + integrity sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg== + dependencies: + "@babel/helper-validator-identifier" "^7.22.20" + chalk "^2.4.2" + js-tokens "^4.0.0" + "@babel/parser@^7.12.13", "@babel/parser@^7.14.0": version "7.14.1" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.1.tgz#1bd644b5db3f5797c4479d89ec1817fe02b84c47" integrity sha512-muUGEKu8E/ftMTPlNp+mc6zL3E9zKWmF5sDHZ5MSsoTP9Wyz64AhEf9kD08xYJ7w6Hdcu8H550ircnPyWSIF0Q== +"@babel/parser@^7.22.15", "@babel/parser@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.0.tgz#da950e622420bf96ca0d0f2909cdddac3acd8719" + integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw== + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.13.12": version "7.13.12" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.13.12.tgz#a3484d84d0b549f3fc916b99ee4783f26fabad2a" @@ -923,17 +992,28 @@ "@babel/parser" "^7.12.13" "@babel/types" "^7.12.13" -"@babel/traverse@^7.12.5", "@babel/traverse@^7.13.0", "@babel/traverse@^7.13.15", "@babel/traverse@^7.14.0": - version "7.14.0" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.14.0.tgz#cea0dc8ae7e2b1dec65f512f39f3483e8cc95aef" - integrity sha512-dZ/a371EE5XNhTHomvtuLTUyx6UEoJmYX+DT5zBCQN3McHemsuIaKKYqsc/fs26BEkHs/lBZy0J571LP5z9kQA== +"@babel/template@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" + integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w== dependencies: - "@babel/code-frame" "^7.12.13" - "@babel/generator" "^7.14.0" - "@babel/helper-function-name" "^7.12.13" - "@babel/helper-split-export-declaration" "^7.12.13" - "@babel/parser" "^7.14.0" - "@babel/types" "^7.14.0" + "@babel/code-frame" "^7.22.13" + "@babel/parser" "^7.22.15" + "@babel/types" "^7.22.15" + +"@babel/traverse@^7.12.5", "@babel/traverse@^7.13.0", "@babel/traverse@^7.13.15", "@babel/traverse@^7.14.0": + version "7.23.2" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.2.tgz#329c7a06735e144a506bdb2cad0268b7f46f4ad8" + integrity sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw== + dependencies: + "@babel/code-frame" "^7.22.13" + "@babel/generator" "^7.23.0" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/parser" "^7.23.0" + "@babel/types" "^7.23.0" debug "^4.1.0" globals "^11.1.0" @@ -945,6 +1025,47 @@ "@babel/helper-validator-identifier" "^7.14.0" to-fast-properties "^2.0.0" +"@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.0.tgz#8c1f020c9df0e737e4e247c0619f58c68458aaeb" + integrity sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg== + dependencies: + "@babel/helper-string-parser" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.20" + to-fast-properties "^2.0.0" + +"@jridgewell/gen-mapping@^0.3.2": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" + integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== + dependencies: + "@jridgewell/set-array" "^1.0.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" + integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== + +"@jridgewell/set-array@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" + integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== + +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + +"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": + version "0.3.19" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz#f8a3249862f91be48d3127c3cfe992f79b4b8811" + integrity sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + "@mrmlnc/readdir-enhanced@^2.2.1": version "2.2.1" resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" @@ -1001,13 +1122,13 @@ resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.4.tgz#15925414e0ad2cd765bfef58842f7e26a7accb24" integrity sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug== -accepts@~1.3.7: - version "1.3.7" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" - integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== +accepts@~1.3.8: + version "1.3.8" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" + integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== dependencies: - mime-types "~2.1.24" - negotiator "0.6.2" + mime-types "~2.1.34" + negotiator "0.6.3" address@1.1.2, address@^1.0.1: version "1.1.2" @@ -1381,21 +1502,23 @@ bl@^1.0.0: readable-stream "^2.3.5" safe-buffer "^5.1.1" -body-parser@1.19.0: - version "1.19.0" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" - integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== +body-parser@1.20.2: + version "1.20.2" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd" + integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA== dependencies: - bytes "3.1.0" - content-type "~1.0.4" + bytes "3.1.2" + content-type "~1.0.5" debug "2.6.9" - depd "~1.1.2" - http-errors "1.7.2" + depd "2.0.0" + destroy "1.2.0" + http-errors "2.0.0" iconv-lite "0.4.24" - on-finished "~2.3.0" - qs "6.7.0" - raw-body "2.4.0" - type-is "~1.6.17" + on-finished "2.4.1" + qs "6.11.0" + raw-body "2.5.2" + type-is "~1.6.18" + unpipe "1.0.0" body@^5.1.0: version "5.1.0" @@ -1505,10 +1628,10 @@ bytes@1: resolved "https://registry.yarnpkg.com/bytes/-/bytes-1.0.0.tgz#3569ede8ba34315fab99c3e92cb04c7220de1fa8" integrity sha1-NWnt6Lo0MV+rmcPpLLBMciDeH6g= -bytes@3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" - integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== +bytes@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== cache-base@^1.0.1: version "1.0.1" @@ -1851,7 +1974,14 @@ console-stream@^0.1.1: resolved "https://registry.yarnpkg.com/console-stream/-/console-stream-0.1.1.tgz#a095fe07b20465955f2fafd28b5d72bccd949d44" integrity sha1-oJX+B7IEZZVfL6/Si11yvM2UnUQ= -content-disposition@0.5.3, content-disposition@^0.5.2: +content-disposition@0.5.4: + version "0.5.4" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" + integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== + dependencies: + safe-buffer "5.2.1" + +content-disposition@^0.5.2: version "0.5.3" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== @@ -1863,6 +1993,11 @@ content-type@~1.0.4: resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== +content-type@~1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" + integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== + continuable-cache@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/continuable-cache/-/continuable-cache-0.3.1.tgz#bd727a7faed77e71ff3985ac93351a912733ad0f" @@ -1880,10 +2015,10 @@ cookie-signature@1.0.6: resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= -cookie@0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" - integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== +cookie@0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051" + integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw== copy-descriptor@^0.1.0: version "0.1.1" @@ -2260,15 +2395,15 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= -depd@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" - integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= +depd@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== -destroy@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" - integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= +destroy@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" + integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== detect-port-alt@1.1.6: version "1.1.6" @@ -2742,37 +2877,38 @@ expand-range@^1.8.1: fill-range "^2.1.0" express@^4.17.1: - version "4.17.1" - resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" - integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== + version "4.19.2" + resolved "https://registry.yarnpkg.com/express/-/express-4.19.2.tgz#e25437827a3aa7f2a827bc8171bbbb664a356465" + integrity sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q== dependencies: - accepts "~1.3.7" + accepts "~1.3.8" array-flatten "1.1.1" - body-parser "1.19.0" - content-disposition "0.5.3" + body-parser "1.20.2" + content-disposition "0.5.4" content-type "~1.0.4" - cookie "0.4.0" + cookie "0.6.0" cookie-signature "1.0.6" debug "2.6.9" - depd "~1.1.2" + depd "2.0.0" encodeurl "~1.0.2" escape-html "~1.0.3" etag "~1.8.1" - finalhandler "~1.1.2" + finalhandler "1.2.0" fresh "0.5.2" + http-errors "2.0.0" merge-descriptors "1.0.1" methods "~1.1.2" - on-finished "~2.3.0" + on-finished "2.4.1" parseurl "~1.3.3" path-to-regexp "0.1.7" - proxy-addr "~2.0.5" - qs "6.7.0" + proxy-addr "~2.0.7" + qs "6.11.0" range-parser "~1.2.1" - safe-buffer "5.1.2" - send "0.17.1" - serve-static "1.14.1" - setprototypeof "1.1.1" - statuses "~1.5.0" + safe-buffer "5.2.1" + send "0.18.0" + serve-static "1.15.0" + setprototypeof "1.2.0" + statuses "2.0.1" type-is "~1.6.18" utils-merge "1.0.1" vary "~1.1.2" @@ -2988,17 +3124,17 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" -finalhandler@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" - integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== +finalhandler@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" + integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== dependencies: debug "2.6.9" encodeurl "~1.0.2" escape-html "~1.0.3" - on-finished "~2.3.0" + on-finished "2.4.1" parseurl "~1.3.3" - statuses "~1.5.0" + statuses "2.0.1" unpipe "~1.0.0" find-cache-dir@^2.0.0: @@ -3072,10 +3208,10 @@ form-data@~2.3.2: combined-stream "^1.0.6" mime-types "^2.1.12" -forwarded@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" - integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== fragment-cache@^0.2.1: version "0.2.1" @@ -3544,27 +3680,16 @@ http-cache-semantics@3.8.1: resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz#39b0e16add9b605bf0a9ef3d9daaf4843b4cacd2" integrity sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w== -http-errors@1.7.2: - version "1.7.2" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" - integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== - dependencies: - depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.1" - statuses ">= 1.5.0 < 2" - toidentifier "1.0.0" - -http-errors@~1.7.2: - version "1.7.3" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" - integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== +http-errors@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" + integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== dependencies: - depd "~1.1.2" + depd "2.0.0" inherits "2.0.4" - setprototypeof "1.1.1" - statuses ">= 1.5.0 < 2" - toidentifier "1.0.0" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" http-parser-js@>=0.5.1: version "0.5.3" @@ -3692,11 +3817,6 @@ inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -inherits@2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= - ini@^1.3.4, ini@^1.3.5: version "1.3.8" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" @@ -4570,6 +4690,11 @@ mime-db@1.47.0, mime-db@^1.28.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.47.0.tgz#8cb313e59965d3c05cfbf898915a267af46a335c" integrity sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw== +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + mime-types@^2.1.12, mime-types@~2.1.19, mime-types@~2.1.24: version "2.1.30" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.30.tgz#6e7be8b4c479825f85ed6326695db73f9305d62d" @@ -4577,6 +4702,13 @@ mime-types@^2.1.12, mime-types@~2.1.19, mime-types@~2.1.24: dependencies: mime-db "1.47.0" +mime-types@~2.1.34: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + mime@1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" @@ -4624,17 +4756,12 @@ ms@2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= -ms@2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" - integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== - ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@^2.1.1: +ms@2.1.3, ms@^2.1.1: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== @@ -4666,10 +4793,10 @@ nearley@^2.7.10: railroad-diagrams "^1.0.0" randexp "0.4.6" -negotiator@0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" - integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== nice-try@^1.0.4: version "1.0.5" @@ -4849,10 +4976,10 @@ object.values@^1.1.0, object.values@^1.1.1, object.values@^1.1.2: es-abstract "^1.18.0-next.2" has "^1.0.3" -on-finished@~2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" - integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= +on-finished@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" + integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== dependencies: ee-first "1.1.1" @@ -5498,12 +5625,12 @@ proto-list@~1.2.1: resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk= -proxy-addr@~2.0.5: - version "2.0.6" - resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf" - integrity sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw== +proxy-addr@~2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== dependencies: - forwarded "~0.1.2" + forwarded "0.2.0" ipaddr.js "1.9.1" pseudomap@^1.0.2: @@ -5534,10 +5661,12 @@ q@^1.1.2: resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= -qs@6.7.0: - version "6.7.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" - integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== +qs@6.11.0: + version "6.11.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" + integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== + dependencies: + side-channel "^1.0.4" qs@^6.4.0: version "6.10.1" @@ -5599,13 +5728,13 @@ range-parser@~1.2.1: resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== -raw-body@2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" - integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== +raw-body@2.5.2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a" + integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA== dependencies: - bytes "3.1.0" - http-errors "1.7.2" + bytes "3.1.2" + http-errors "2.0.0" iconv-lite "0.4.24" unpipe "1.0.0" @@ -5953,7 +6082,7 @@ safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: +safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -6008,9 +6137,9 @@ semver-truncate@^1.1.2: semver "^5.3.0" "semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.5.0, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== semver@7.0.0: version "7.0.0" @@ -6018,38 +6147,38 @@ semver@7.0.0: integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -send@0.17.1: - version "0.17.1" - resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" - integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== +send@0.18.0: + version "0.18.0" + resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" + integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== dependencies: debug "2.6.9" - depd "~1.1.2" - destroy "~1.0.4" + depd "2.0.0" + destroy "1.2.0" encodeurl "~1.0.2" escape-html "~1.0.3" etag "~1.8.1" fresh "0.5.2" - http-errors "~1.7.2" + http-errors "2.0.0" mime "1.6.0" - ms "2.1.1" - on-finished "~2.3.0" + ms "2.1.3" + on-finished "2.4.1" range-parser "~1.2.1" - statuses "~1.5.0" + statuses "2.0.1" -serve-static@1.14.1: - version "1.14.1" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" - integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== +serve-static@1.15.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" + integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== dependencies: encodeurl "~1.0.2" escape-html "~1.0.3" parseurl "~1.3.3" - send "0.17.1" + send "0.18.0" set-getter@^0.1.0: version "0.1.1" @@ -6068,10 +6197,10 @@ set-value@^2.0.0, set-value@^2.0.1: is-plain-object "^2.0.3" split-string "^3.0.1" -setprototypeof@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" - integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== shallow-clone@^3.0.0: version "3.0.1" @@ -6329,10 +6458,10 @@ static-extend@^0.1.1: define-property "^0.2.5" object-copy "^0.1.0" -"statuses@>= 1.5.0 < 2", statuses@~1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" - integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== strict-uri-encode@^1.0.0: version "1.1.0" @@ -6620,10 +6749,10 @@ to-regex@^3.0.1, to-regex@^3.0.2: regex-not "^1.0.2" safe-regex "^1.1.0" -toidentifier@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" - integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== toml@^2.3.2: version "2.3.6" @@ -6689,7 +6818,7 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0: resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= -type-is@~1.6.17, type-is@~1.6.18: +type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==